[Solved] Alternative Camera Control with mouse

Hey,



I’ve written a little project for school and need to present it. Since this is going to happen on a SmartBoard (basically a large touchscreen) I wanted to change the camera controls to the followning (everything with left mouse button):

Single click: select geometry

double click: if not clicked a geometry, create a new one

single click & hold: move in camera direction

single click & drag: move in drag direction

double click & drag: rotate in drag direction



The Problems:

I’m unable to detect the dragging direction. My approach is:

  1. Store where the mouse button is pressed in oldMousePos.
  2. If the mouse is moved while button is down set boolean move = true.
  3. In simpleUpdate() if move is true subtract oldMousePos from current cursor position and move camera accordingly.

    Somehow the difference is always (0.0, 0.0).



    Sometimes while rotating the camera it suddenly stops rotation and starts moving.



    Disabling the logging doesn’t work always (but that’s not that important).



    The code (shortened):

    [java]import examples.BasicEnviroment;

    import com.jme3.math.;

    import com.jme3.scene.Geometry;

    import com.jme3.input.controls.
    ;

    import com.jme3.input.;

    import com.jme3.collision.CollisionResults;

    import com.jme3.renderer.queue.RenderQueue.ShadowMode;



    public class MouseControl extends BasicEnviroment {

    private ActionListener actionListener;

    private AnalogListener analogListener;

    private boolean mouseDown, doubleClick, mouseMoved, rotate, move;

    private long mouseDownTime; // last time the left mouse button was pressed

    // how long the button must be pressed (without mouse movement to start moving in camera direction

    private int mouseMoveOffset;

    private float camSpeed;

    private Vector2f oldMousePos; // were the button was pressed before dragging the mouse



    public static void main(String[] args) {

    new MouseControl().start();

    }

    // run in BasicEnviroment.simpleInitApp

    public void sceneInit() {

    inputManager.setCursorVisible(true);

    setDisplayStatView(false);

    setDisplayFps(false);

    inputManager.clearMappings();



    rootNode.setShadowMode(ShadowMode.Off);

    camSpeed = 10;

    mouseMoveOffset = 500;



    analogListener = new AnalogListener() {

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

    if(name.equals(“mouseMove”) && mouseDown) {

    mouseMoved = true;

    // if double click & drag -> rotate

    // if single click & drag -> move

    if(doubleClick) rotate = true;

    else move = true;

    }

    }

    };

    inputManager.addMapping(“mouseMove”, new MouseAxisTrigger(MouseInput.AXIS_X, false));

    inputManager.addMapping(“mouseMove”, new MouseAxisTrigger(MouseInput.AXIS_Y, false));

    inputManager.addListener(analogListener, new String[] { “mouseMove” });



    actionListener = new ActionListener() {

    public void onAction(String name, boolean keyPressed, float tpf) {

    if(name.equals(“mouseClick”)) {

    if(keyPressed) {

    mouseDown = true;

    mouseDownTime = System.currentTimeMillis();

    // were the button was pressed before dragging the mouse

    oldMousePos = inputManager.getCursorPosition();

    }

    else { // mouse released

    mouseDown = move = rotate = mouseMoved = false;

    if(!mouseMoved && System.currentTimeMillis() - mouseDownTime < mouseMoveOffset) {

    // [snip] First part of checking if a Geometry was single clicked

    Geometry target = null;

    // [snip] Second part

    if(target != null) foo(); // Geometry was clicked

    else if(doubleClick) bar(); // Doubleclick & no geometry was clicked

    else { // single click & no geometry was clicked

    doubleClick = true;

    new Thread() {

    public void run() {

    try{

    sleep(350);

    }catch(InterruptedException e){}

    finally{ doubleClick = false; }

    }

    }.start();

    }

    }

    }

    } else if(name.equals(“esc”) && !keyPressed) {

    stop(); // btw: Is this the correct way to stop a SimpleApplication?

    }

    }

    };

    inputManager.addMapping(“mouseClick”, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));

    inputManager.addMapping(“esc”, new KeyTrigger(KeyInput.KEY_ESCAPE));

    inputManager.addListener(actionListener, new String[] { “mouseClick”, “esc” });

    }

    private void foo() {

    System.out.println(“foo”);

    }

    private void bar() {

    System.out.println(“bar”);

    }

    public void simpleUpdate(float tpf) {

    // If single click & drag -> move camera in drag direction

    if(move) {

    // dir = current mouse pos - mouse pos when clicked

    // for whatever reason dir is always (0.0, 0.0). That’s my problem.

    Vector2f dir = inputManager.getCursorPosition().subtract(oldMousePos);

    System.out.println(dir);

    // I think it’s easier to have 4 direction movement.

    // So first check if movement along x or y axis and then check in positive or negative direction

    if(Math.abs(dir.x) > Math.abs(dir.y)) {

    if(dir.x < 0) cam.setLocation(cam.getLocation().add(cam.getLeft().mult(tpf
    camSpeed)));

    else cam.setLocation(cam.getLocation().add(cam.getLeft().mult(-tpfcamSpeed)));

    } else {

    if(dir.y <0) cam.setLocation(cam.getLocation().add(cam.getUp().mult(-tpf
    camSpeed)));

    else cam.setLocation(cam.getLocation().add(cam.getUp().mult(tpfcamSpeed)));

    }

    // If long click without movement -> move in camera direction (works).

    } else if(mouseDown && !mouseMoved && System.currentTimeMillis() - mouseDownTime > mouseMoveOffset) {

    cam.setLocation(cam.getLocation().add(cam.getDirection().mult(tpf
    camSpeed)));

    // If double click & drag -> rotate in drag direction.

    // Same problem as with moving the camera. dir is always (0.0, 0.0).

    } else if(rotate) {

    Vector2f dir = inputManager.getCursorPosition().subtract(oldMousePos);

    if(Math.abs(dir.x) > Math.abs(dir.y))

    if(dir.x < 0) {

    Quaternion rot = new Quaternion().fromAngleAxis(-tpf2, Vector3f.UNIT_Y);

    cam.lookAtDirection(rot.mult(cam.getDirection()), rot.mult(cam.getUp()));

    } else {

    Quaternion rot = new Quaternion().fromAngleAxis(tpf
    2, Vector3f.UNIT_Y);

    cam.lookAtDirection(rot.mult(cam.getDirection()), rot.mult(cam.getUp()));

    }

    else

    if(dir.y <0) {

    Quaternion rot = new Quaternion().fromAngleAxis(tpf2, cam.getLeft());

    cam.lookAtDirection(rot.mult(cam.getDirection()), rot.mult(cam.getUp()));

    }

    else {

    Quaternion rot = new Quaternion().fromAngleAxis(-tpf
    2, cam.getLeft());

    cam.lookAtDirection(rot.mult(cam.getDirection()), rot.mult(cam.getUp()));

    }

    }

    }

    }[/java]



    BasicEnviroment.java (without errors, just so you can run the code)

    [java]import com.jme3.app.SimpleApplication;

    import com.jme3.light.DirectionalLight;

    import com.jme3.light.AmbientLight;

    import com.jme3.math.Vector3f;

    import com.jme3.math.ColorRGBA;

    import com.jme3.material.Material;

    import com.jme3.scene.Geometry;

    import com.jme3.shadow.BasicShadowRenderer;

    import com.jme3.renderer.queue.RenderQueue.ShadowMode;

    import com.jme3.util.SkyFactory;

    import com.jme3.scene.debug.Arrow;



    public abstract class BasicEnviroment extends SimpleApplication {

    Geometry xBar, yBar, zBar;

    public void simpleInitApp() {

    // Unnötige INFO Meldungen abschalten

    java.util.logging.Logger.getLogger("").setLevel(java.util.logging.Level.WARNING);



    // Globales Licht

    DirectionalLight sun = new DirectionalLight();

    sun.setColor(ColorRGBA.White);

    sun.setDirection(new Vector3f(-0.55f,-0.6f,-1f).normalizeLocal());

    rootNode.addLight(sun);



    // Kamera Sichtweite

    cam.setFrustumFar(1000);

    // Kameraposition

    cam.setLocation(new Vector3f(4,4,7));

    cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);



    // “Licht offset”; Dieses Licht wird überall addiert, sodass es keine

    // komplett schwarzen Stellen gibt.

    AmbientLight al = new AmbientLight();

    al.setColor(ColorRGBA.White.mult(0.5f));

    rootNode.addLight(al);



    Geometry xBar = makeArrow(Vector3f.ZERO, new Vector3f(1,0,0), ColorRGBA.Red);

    Geometry yBar = makeArrow(Vector3f.ZERO, new Vector3f(0,1,0), ColorRGBA.Green);

    Geometry zBar = makeArrow(Vector3f.ZERO, new Vector3f(0,0,1), ColorRGBA.Blue);



    // Kamera auf Kugel gerichtet

    //camNode.lookAt(sphere.getLocalTranslation(), Vector3f.UNIT_Y);

    cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);



    // Schatten

    BasicShadowRenderer bsr = new BasicShadowRenderer(assetManager, 2058);

    bsr.setDirection(new Vector3f(sun.getDirection()).normalizeLocal());

    viewPort.addProcessor(bsr);



    // Schatten generell abschalten, sodass er extra f�r Objekte, die Schatten

    // werfen oder “empfangen” sollen, “angeschalter” werden muss(bessere Performance)

    rootNode.setShadowMode(ShadowMode.Off);



    // Hintergrund

    rootNode.attachChild(SkyFactory.createSky(

    assetManager, “Textures/Sky/Bright/BrightSky.dds”, false));



    sceneInit();

    }



    public void showCoordinateAxes(boolean _b) {

    if(_b && !rootNode.hasChild(xBar)) {

    rootNode.attachChild(xBar);

    rootNode.attachChild(yBar);

    rootNode.attachChild(zBar);

    } else if(!_b && rootNode.hasChild(xBar)) {

    rootNode.detachChild(xBar);

    rootNode.detachChild(yBar);

    rootNode.detachChild(zBar);

    }

    }

    protected Geometry makeArrow(Vector3f _pos, Vector3f _dir, ColorRGBA _c) {

    Arrow a = new Arrow(_dir);

    a.setLineWidth(3);

    Geometry g = new Geometry(“arrow”, a);

    g.setLocalTranslation(_pos);

    Material m = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);

    m.setColor(“Color”, _c);

    g.setMaterial(m);

    rootNode.attachChild(g);

    return g;

    }



    public abstract void sceneInit();

    }[/java]

Hey,



Using a RawInputListener would grant you direct and quick-and-dirty access to mouse movements. I don’t think this is the recommended way, but it will work. Use evt.getDx() to get change and evt.getX() for absolute coordinates.



[java]

class MousePick implements RawInputListener {



public void beginInput() { }



public void endInput() { }



public void onJoyAxisEvent(JoyAxisEvent evt) { }



public void onJoyButtonEvent(JoyButtonEvent evt) { }



public void onMouseMotionEvent(MouseMotionEvent evt) {

evt.getDX();

evt.getX();

}



public void onMouseButtonEvent(MouseButtonEvent evt) { }



public void onKeyEvent(KeyInputEvent evt) { }



public void onTouchEvent(TouchEvent evt) { }



}



// Remember to add the raw input listener as a listener to the input manager:

SimpleApplication.inputManager.addRawInputListener( new MousePick() );

[/java]



Hope it helps…

Thank you. It works perfectly with the RawInputListener. I guess you saved me from a lot of debugging.