Hello,
I wrote inputHandler for rotation around asigned spatial. I was looking for it here, but I didn't found, so I hope it will be useful for somebody.
First class is InputHandler extension - only what you need is target object, camera node and scene size. You can then rotate with mouse or keyboard.
/*
* RotateCamera.java
*/
import com.jme.input.AbsoluteMouse;
import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.action.InputActionEvent;
import com.jme.input.action.KeyInputAction;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.scene.CameraNode;
import com.jme.scene.Spatial;
/**
* InputHandler for orbit rotation around selected object
*
* @author slaava
* @version 1.0
*/
public class RotateCamera extends InputHandler {
/**
* Custom mouse input handler
*/
private RotateMouseInput mouseInput;
/**
* Selected spatial to rotation around
*/
private Spatial target;
/**
* Node with camera
*/
private CameraNode camNode;
/**
* Rotation
*/
private Quaternion quatRotation;
/**
* Camera translation
*/
private Vector3f camTranslation;
/**
* Distance between target and camera
*/
private float distance;
/**
* Zoom modificator
*/
private float zoomTime = 0.005f;
/**
* Scene width
*/
private int width;
/**
* Scene height
*/
private int height;
/**
* Action for keyboard input to rotate up
*/
private KeyInputAction up;
/**
* Action for keyboard input to rotate down
*/
private KeyInputAction down;
/**
* Action for keyboard input to rotate left
*/
private KeyInputAction left;
/**
* Action for keyboard input to rotate right
*/
private KeyInputAction right;
/**
* Action for keyboard input to zoom in
*/
private KeyInputAction zoomIn;
/**
* Action for keyboard input to zoom out
*/
private KeyInputAction zoomOut;
/**
* Constructor with scene dimension and camera node
*
* @param width scene width
* @param height scene height
* @param camNode camera node
*/
public RotateCamera(int width, int height, CameraNode camNode) {
super();
this.width = width;
this.height = height;
createMouseInput();
createKeyInput();
this.camNode = camNode;
}
/**
* Asign new target. Camera will rotate around it.
*
* @param target target object
*/
public void reLoad(Spatial target) {
this.target = target;
quatRotation = new Quaternion();
Camera camera = camNode.getCamera();
distance = camera.getLocation().distance(target.getWorldTranslation());
camTranslation = new Vector3f(0, 0, -distance);
camNode.setLocalTranslation(camera.getLocation());
camNode.lookAt(target.getWorldTranslation(), camera.getUp());
quatRotation = camNode.getLocalRotation();
}
/**
* Create mouse input
*/
private void createMouseInput() {
AbsoluteMouse absoluteMouse = new AbsoluteMouse("Absolute mouse", width, height);
mouseInput = new RotateMouseInput(absoluteMouse, this);
addAction(mouseInput);
}
/**
* Create key input
*/
private void createKeyInput() {
KeyBindingManager keyboard = KeyBindingManager.getKeyBindingManager();
keyboard.set("up", KeyInput.KEY_W);
keyboard.set("down", KeyInput.KEY_S);
keyboard.set("right", KeyInput.KEY_A);
keyboard.set("left", KeyInput.KEY_D);
keyboard.set("zoomIn", KeyInput.KEY_UP);
keyboard.set("zoomOut", KeyInput.KEY_DOWN);
up = new KeyInputAction() {
public void performAction(InputActionEvent evt) {
rotate((width / 2), (height / 2), (width / 2), (height / 2) + 1);
}
};
down = new KeyInputAction() {
public void performAction(InputActionEvent evt) {
rotate((width / 2), (height / 2), (width / 2), (height / 2) - 1);
}
};
left = new KeyInputAction() {
public void performAction(InputActionEvent evt) {
rotate((width / 2), (height / 2), (width / 2) - 1, (height / 2));
}
};
right = new KeyInputAction() {
public void performAction(InputActionEvent evt) {
rotate((width / 2), (height / 2), (width / 2) + 1, (height / 2));
}
};
zoomIn = new KeyInputAction() {
public void performAction(InputActionEvent evt) {
zoom(zoomTime);
}
};
zoomOut = new KeyInputAction() {
public void performAction(InputActionEvent evt) {
zoom(-zoomTime);
}
};
addAction(down, "down", true);
addAction(up, "up", true);
addAction(left, "left", true);
addAction(right, "right", true);
addAction(zoomIn, "zoomIn", true);
addAction(zoomOut, "zoomOut", true);
}
/**
* Move camera to new position depending the mouse location
*
* @param oldX old X position
* @param oldY old Y position
* @param newX new X position
* @param newY new Y position
*/
protected void rotate(int oldX, int oldY, int newX, int newY) {
Vector3f oLastVector3f = this.computeTrackPosition(oldX, oldY);
Vector3f oNewVector3f = this.computeTrackPosition(newX, newY);
Vector3f oAxisVector = oLastVector3f.cross(oNewVector3f);
Vector2f oVector2f = new Vector2f(newX - oldX, newY - oldY);
float tAngle = oVector2f.length();
Quaternion quatLocal = new Quaternion();
quatLocal.fromAngleAxis(-tAngle * FastMath.DEG_TO_RAD, oAxisVector);
quatRotation.multLocal(quatLocal);
camNode.setLocalTranslation(quatRotation.mult(camTranslation).add(target.getWorldTranslation()));
camNode.setLocalRotation(quatRotation);
}
/**
* TrackBall mapping
*
* @param x X position
* @param y Y position
* @return TrackBall position
*/
private Vector3f computeTrackPosition(int x, int y) {
float newX = ((float) x) - (float) width / 2;
float newY = ((float) y) - (float) height / 2;
float z2 = width * height * 1000 - x * x - y * y;
float z = z2 > 0 ? FastMath.sqrt(z2) : 0;
return new Vector3f(newX, newY, z);
}
/**
* Zoom in/out camera to target
*
* @param value amount zooming
*/
protected void zoom(float value) {
camTranslation.multLocal(1-value);
Vector3f difference = camNode.getLocalTranslation().subtract(target.getWorldTranslation());
difference.multLocal(1-value);
Vector3f newCoords = target.getWorldTranslation().add(difference);
camNode.setLocalTranslation(newCoords);
}
/**
* Update dimension
*
* @param width new width
* @param height new height
*/
public void updateSize(int width, int height) {
this.width = width;
this.height = height;
}
}
Second class is MouseInputHandler extension.
/*
* RotateMouseInput.java
*/
import com.jme.input.AbsoluteMouse;
import com.jme.input.MouseInput;
import com.jme.input.action.InputActionEvent;
import com.jme.input.action.MouseInputAction;
/**
* Custom mouse handler for RotateCamera handler
*
* @author slaava
* @version 1.0
*/
public class RotateMouseInput extends MouseInputAction {
/**
* RotateCamera with target object
*/
private RotateCamera parent;
/**
* Left mouse button
*/
public static int MOUSE_BUTTON_LEFT = 0;
/**
* Button pressed
*/
private boolean pressed = false;
/**
* Old X position
*/
private int oldX;
/**
* Old Y position
*/
private int oldY;
/**
* New X position
*/
private int newX;
/**
* New Y position
*/
private int newY;
/**
* Zoom modificator
*/
private float zoomSpeed = 0.001f;
/**
* Constructor with mouse and RotateCamera
*
* @param mouse mouse input
* @param parent RotateCamera with target
*/
public RotateMouseInput(AbsoluteMouse mouse, RotateCamera parent) {
this.mouse = mouse;
this.parent = parent;
}
public void performAction(InputActionEvent evt) {
// left button
if (MouseInput.get().isButtonDown(MOUSE_BUTTON_LEFT)) {
if (!pressed) {
pressed = true;
oldX = MouseInput.get().getXAbsolute();
oldY = MouseInput.get().getYAbsolute();
}
newX = MouseInput.get().getXAbsolute();
newY = MouseInput.get().getYAbsolute();
// camera rotation
parent.rotate(oldX, oldY, newX, newY);
oldX = newX;
oldY = newY;
} else {
pressed = false;
}
// zoom
int wdelta = MouseInput.get().getWheelDelta();
if (wdelta != 0) {
parent.zoom(wdelta * zoomSpeed);
}
}
/**
* Zoom modificator getter
*
* @return zoom modificator
*/
public float getZoomSpeed() {
return zoomSpeed;
}
/**
* Zoom modificator setter
*
* @param zoomSpeed new zoom modificator
*/
public void setZoomSpeed(float zoomSpeed) {
this.zoomSpeed = zoomSpeed;
}
}
And here is some example project
import com.jme.app.SimpleGame;
import com.jme.math.Vector3f;
import com.jme.scene.CameraNode;
import com.jme.scene.shape.Box;
/**
*
* @author slaava
* @version 1.0
*/
public class Example extends SimpleGame {
private Box b;
private RotateCamera rotateCam;
private CameraNode camNode;
public static void main(String[] args) {
Example example = new Example();
example.setDialogBehaviour(SimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
example.start();
}
public Example() {
super();
}
protected void simpleInitGame() {
// target of rotation
b = new Box("Mybox", new Vector3f(-1, -1, -1), new Vector3f(1, 1, 1));
b.setRandomColors();
b.setLocalTranslation(0, 0, 0);
rootNode.attachChild(b);
// have to create CameraNode
camNode = new CameraNode("camNode", cam);
rootNode.attachChild(camNode);
// new RoteteCamera
rotateCam = new RotateCamera(display.getWidth(), display.getHeight(), camNode);
// set target
rotateCam.reLoad(b);
this.input = rotateCam;
}
@Override
protected void simpleUpdate() {
input.update(tpf);
}
}
Here is video record on youtube http://www.youtube.com/watch?v=6pbqbyIHhuk
And that's all :) I hope it is useful for somebody, i used this class in my project where I switched inputHandlers between FirstPersonHandler and RotationCamera.
If you find some bug or have any idea to extension, your posts are welcome!