Rotation camera

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!

Hi! thanks for your code, I found it useful. but, I wonder how I can modify it to change limits to the movement range…

I want to explore a object but only over the ground plane (forbid the camera go under the floor) and also I would like to change the zoom min/max values…

thanks for your hints!

Is this for a school assignment?

research (academic) project. why?  :?

Just wondering. There seems to be several users trying accomplish this, all around the same time. Just made me think it was a class assignment.

Or it#s somthing so common in games, that many need it ^^

At least mine was not amde for school.