Mouse picking is really sensative

I am working with mouse picking and different things. I have written a small app that when the mouse clicks on a cube it starts to spin. If I click on it again, it stops, and so on and so on. The problem is I have to click really fast or time it just right or it counts more than one click. If I hold the mouse button down it continues to click until I release it.



Is there a way that I can tone down the click sensitivity?

"mojomonk" wrote:
Ah, found your problem. The problem actually lies in how you are building your second box:

b2 = new Box("My Box2", new Vector3f(-4, -1, -1), new Vector3f(-3, 1, 1));



You are saying, it's minimum point is (-4,-1,-1) and maximum (-3,1,1) but local translation (local coordinate origin) is (0,0,0). So it's spinning about (0,0,0), but the shape is shifted over slightly.

When I change it to:

b2 = new Box("My Box2", new Vector3f(-1, -1, -1), new Vector3f(1, 1, 1));
        b2.setLocalTranslation(new Vector3f(-5,0,0));



You get the results you are expecting.

I figured it was a DOH! thing. Thanks a bunch.

Ok, almost there. Now the problem is that they are sharing a Quaternion and so they are both rotating exactly the same. Also, after I click one, then click they other, then stop them. No matter which one I click, they both start spinning together.



So I need to figure out somewhere that when one is clicked, it get’s it’s very own Quaternion, I think. Or something like that.

The following should work. (You’ll have to change the package). You have two quaternions for the rotation and you switch depending on which box was selected. Note that right now only one box can spin at a time. You’ll have to add some more code if you want to be able to spin both boxes at the same time. (Since toSpin gets overwritten at the moment). Hope this helps. Neat little demo by the way.


package com.meka.app;

import java.net.URL;
import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingSphere;
import com.jme.image.Texture;
import com.jme.input.AbsoluteMouse;
import com.jme.input.InputSystem;
import com.jme.input.MouseButtonStateType;
import com.jme.input.MouseInput;
import com.jme.intersection.BoundingPickResults;
import com.jme.intersection.PickResults;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Ray;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.scene.Geometry;
import com.jme.scene.shape.Box;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.TextureState;
import com.jme.util.TextureManager;

public class MousePick extends SimpleGame {

    AbsoluteMouse am;
    Box b;
    Box b2;
    PickResults pr;

    private Quaternion rotQuat;
/*NEW CODE*/
    private Quaternion rotQuat2;
    private float angle = 0;
    private Vector3f axis;
    boolean spin = false;
    Geometry toSpin;


    public static void main(String[] args) {
        MousePick app = new MousePick();
        app.setDialogBehaviour(SimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
        app.start();
    }

    protected void simpleInitGame(){

        rotQuat = new Quaternion();
/*NEW CODE*/
        rotQuat2 = new Quaternion();
        axis = new Vector3f(0, 1, 0.0f);
        am = new AbsoluteMouse("The Mouse", display.getWidth(), display.getHeight());
        TextureState ts = display.getRenderer().createTextureState();
        URL cursorLoc;
        cursorLoc = MousePick.class.getClassLoader().getResource("jmetest/data/cursor/cursor1.png");
        Texture t = TextureManager.loadTexture(cursorLoc, Texture.MM_LINEAR, Texture.FM_LINEAR, 0f, true);
        ts.setTexture(t);
        am.setRenderState(ts);

        AlphaState as = display.getRenderer().createAlphaState();
        as.setBlendEnabled(true);
        as.setSrcFunction(AlphaState.SB_SRC_ALPHA);
        as.setDstFunction(AlphaState.DB_ONE_MINUS_SRC_ALPHA);
        as.setTestEnabled(true);
        as.setTestFunction(AlphaState.TF_GREATER);
        am.setRenderState(as);

        am.setMouseInput(InputSystem.getMouseInput());
        am.setLocalTranslation(new Vector3f(display.getWidth()/2, display.getHeight()/2, 0));
        input.setMouse(am);

        b = new Box("My Box", new Vector3f(-1, -1, -1), new Vector3f(1,1,1));
        b.setModelBound(new BoundingSphere());
        b.updateModelBound();

        b2 = new Box("My Box2", new Vector3f(-1, -1, -1), new Vector3f(1, 1, 1));
        b2.setLocalTranslation(new Vector3f(-5,0,0));
        b2.setModelBound(new BoundingSphere());
        b2.updateModelBound();



        rootNode.attachChild(b);
        rootNode.attachChild(b2);
        rootNode.attachChild(am);
        lightState.detachAll();

        pr = new BoundingPickResults();
    }

    protected void simpleUpdate(){

        if (timer.getTimePerFrame() < 1) {
            angle = angle + (timer.getTimePerFrame() * 50);
            if (angle > 360) {
                angle = 0;
            }
        }

        if (spin){
/* NEW CODE*/
            if(toSpin.getName() == "My Box"){
                rotQuat.fromAngleAxis(angle*FastMath.DEG_TO_RAD, axis);
                toSpin.setLocalRotation(rotQuat);
            }
            else{
                rotQuat2.fromAngleAxis(angle*FastMath.DEG_TO_RAD, axis);
                toSpin.setLocalRotation(rotQuat2);
            }
               
        }


        MouseInput thisMouse = am.getMouseInput();

        MouseButtonStateType t = thisMouse.getButtonState();
        if (thisMouse.isButtonDown(0)){
            spin = true;
            Vector2f screenPos = new Vector2f();
            screenPos.set(am.getHotSpotPosition().x, am.getHotSpotPosition().y);
            Vector3f worldCoords = display.getWorldCoordinates(screenPos, 0);
            Ray mouseRay = new Ray(cam.getLocation(), worldCoords.subtractLocal(cam.getLocation()));
            pr.clear();
            rootNode.findPick(mouseRay, pr);


            for (int i = 0; i < pr.getNumber(); i++){
                //pr.getPickData(i).getTargetMesh().setRandomColors();
                toSpin = pr.getPickData(i).getTargetMesh();
            }
        }
        if (thisMouse.isButtonDown(1)){
            spin = false;
        }
    }
}

Great! Thanks for the help. I’ll get this going and then once I get them both spinning, I’ll post that code.

Hmmm, good point. Right now, it’s pretty much just a direct mapping to LWJGL API, i.e. the call to the mouse button is just passed on to LWJGL. Hencer there is nothing for setting sensitivity. Currently, you’d have to put a timer sense last pressed mouse button at the moment.

"mojomonk" wrote:
Hmmm, good point. Right now, it's pretty much just a direct mapping to LWJGL API, i.e. the call to the mouse button is just passed on to LWJGL. Hencer there is nothing for setting sensitivity. Currently, you'd have to put a timer sense last pressed mouse button at the moment.

Okie dokie. In the mean time I just changed it so that left button spins, right button stops the spin. Thanks for the info.

We have a .setRepeat to keep key presses from doing that, we might be able to do the same with the mouse button.

"mojomonk" wrote:
We have a .setRepeat to keep key presses from doing that, we might be able to do the same with the mouse button.

Either that or somehow have a simple clicked event that only registers once the mouse button is released. So the isButtonClicked event would require 2 actions, the mouse pressed and mouse released. Kind of like Swing.

Ok, 2nd part of this question since you appear to reading this...;)

I have placed 2 Box's on the rootNode. I am trying to make it so that depending on which box I click on, that one starts to spin. But it isn't working. If I click on the first box I add, it spins fine. If I click on the other box, it rotates around the first box. I thought that when the mouse was clicked, if I captured did

toSpin = pr.getPickData(i).getTargetMesh();

where toSpin is a Geometry object and then in the sinpleUpdate method did:

if (spin){
rotQuat.fromAngleAxis(angle*FastMath.DEG_TO_RAD, axis);
toSpin.setLocalRotation(rotQuat);
}

Then it would work. But appeantly not. Here is the whole program. It's a modified version of the Mouse picking tut in the starter guide.


public class MousePick extends SimpleGame {

    AbsoluteMouse am;
    Box b;
    Box b2;
    PickResults pr;

    private Quaternion rotQuat;
    private float angle = 0;
    private Vector3f axis;
    boolean spin = false;
    Geometry toSpin;


    public static void main(String[] args) {
        MousePick app = new MousePick();
        app.setDialogBehaviour(SimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
        app.start();
    }

    protected void simpleInitGame(){

        rotQuat = new Quaternion();
        axis = new Vector3f(0, 1, 0.0f);
        am = new AbsoluteMouse("The Mouse", display.getWidth(), display.getHeight());
        TextureState ts = display.getRenderer().createTextureState();
        URL cursorLoc;
        cursorLoc = MousePick.class.getClassLoader().getResource("jmetest/data/cursor/cursor1.png");
        Texture t = TextureManager.loadTexture(cursorLoc, Texture.MM_LINEAR, Texture.FM_LINEAR, 0f, true);
        ts.setTexture(t);
        am.setRenderState(ts);

        AlphaState as = display.getRenderer().createAlphaState();
        as.setBlendEnabled(true);
        as.setSrcFunction(AlphaState.SB_SRC_ALPHA);
        as.setDstFunction(AlphaState.DB_ONE_MINUS_SRC_ALPHA);
        as.setTestEnabled(true);
        as.setTestFunction(AlphaState.TF_GREATER);
        am.setRenderState(as);

        am.setMouseInput(InputSystem.getMouseInput());
        am.setLocalTranslation(new Vector3f(display.getWidth()/2, display.getHeight()/2, 0));
        input.setMouse(am);

        b = new Box("My Box", new Vector3f(-1, -1, -1), new Vector3f(1,1,1));
        b.setModelBound(new BoundingSphere());
        b.updateModelBound();

        b2 = new Box("My Box2", new Vector3f(-4, -1, -1), new Vector3f(-3, 1, 1));
        b2.setModelBound(new BoundingSphere());
        b2.updateModelBound();



        rootNode.attachChild(b);
        rootNode.attachChild(b2);
        rootNode.attachChild(am);
        lightState.detachAll();

        pr = new BoundingPickResults();
    }

    protected void simpleUpdate(){

        if (timer.getTimePerFrame() < 1) {
            angle = angle + (timer.getTimePerFrame() * 50);
            if (angle > 360) {
                angle = 0;
            }
        }

        if (spin){
            rotQuat.fromAngleAxis(angle*FastMath.DEG_TO_RAD, axis);
            toSpin.setLocalRotation(rotQuat);
        }


        MouseInput thisMouse = am.getMouseInput();

        MouseButtonStateType t = thisMouse.getButtonState();
        if (thisMouse.isButtonDown(0)){
            spin = true;
            Vector2f screenPos = new Vector2f();
            screenPos.set(am.getHotSpotPosition().x, am.getHotSpotPosition().y);
            Vector3f worldCoords = display.getWorldCoordinates(screenPos, 0);
            Ray mouseRay = new Ray(cam.getLocation(), worldCoords.subtractLocal(cam.getLocation()));
            pr.clear();
            rootNode.findPick(mouseRay, pr);


            for (int i = 0; i < pr.getNumber(); i++){
                //pr.getPickData(i).getTargetMesh().setRandomColors();
                toSpin = pr.getPickData(i).getTargetMesh();
            }
        }
        if (thisMouse.isButtonDown(1)){
            spin = false;
        }
    }
}

Ah, found your problem. The problem actually lies in how you are building your second box:


b2 = new Box("My Box2", new Vector3f(-4, -1, -1), new Vector3f(-3, 1, 1));



You are saying, it's minimum point is (-4,-1,-1) and maximum (-3,1,1) but local translation (local coordinate origin) is (0,0,0). So it's spinning about (0,0,0), but the shape is shifted over slightly.

When I change it to:

b2 = new Box("My Box2", new Vector3f(-1, -1, -1), new Vector3f(1, 1, 1));
        b2.setLocalTranslation(new Vector3f(-5,0,0));



You get the results you are expecting.

Ok, I was able to spend some time today and get this figured out. Below is the code I used. You’ll want to replace any textures with your own if you want to run the program.



Left click spins the object. Right click stops the spinning. I’d like to know how anyone else would have done this simpler maybe? Although I think it was pretty simple the way I did it. jME rocks!



public class MousePick extends SimpleGame {

    AbsoluteMouse am;
    ObjectToSpin b;
    ObjectToSpin b2;
    PickResults pr;

    boolean spin = false;
    ObjectToSpin toSpin;
    ArrayList spinList;

    public static void main(String[] args) {
        MousePick app = new MousePick();
        app.setDialogBehaviour(SimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
        app.start();
    }

    protected void simpleInitGame(){

        spinList = new ArrayList();
        am = new AbsoluteMouse("The Mouse", display.getWidth(), display.getHeight());
        TextureState ts = display.getRenderer().createTextureState();
        URL cursorLoc;
        cursorLoc = MousePick.class.getClassLoader().getResource("jmetest/data/cursor/cursor1.png");
        Texture t = TextureManager.loadTexture(cursorLoc, Texture.MM_LINEAR, Texture.FM_LINEAR, 0f, true);
        ts.setTexture(t);
        am.setRenderState(ts);

        TextureState ts2 = display.getRenderer().createTextureState();
        URL rock1URL = MousePick.class.getClassLoader().getResource("com/gthought/jme/test/rock1.jpg");
        Texture rock1T = TextureManager.loadTexture(rock1URL, Texture.MM_LINEAR, Texture.FM_LINEAR, 0f, true);
        ts2.setTexture(rock1T);

        TextureState ts3 = display.getRenderer().createTextureState();
        URL rock2URL = MousePick.class.getClassLoader().getResource("com/gthought/jme/test/rock2.jpg");
        Texture rock2T = TextureManager.loadTexture(rock2URL, Texture.MM_LINEAR, Texture.FM_LINEAR, 0f, true);
        ts3.setTexture(rock2T);

        AlphaState as = display.getRenderer().createAlphaState();
        as.setBlendEnabled(true);
        as.setSrcFunction(AlphaState.SB_SRC_ALPHA);
        as.setDstFunction(AlphaState.DB_ONE_MINUS_SRC_ALPHA);
        as.setTestEnabled(true);
        as.setTestFunction(AlphaState.TF_GREATER);
        am.setRenderState(as);

        am.setMouseInput(InputSystem.getMouseInput());
        am.setLocalTranslation(new Vector3f(display.getWidth()/2, display.getHeight()/2, 0));
        input.setMouse(am);

        b = new ObjectToSpin("My Box", new Vector3f(-1, -1, -1), new Vector3f(1,1,1));
        b.setQuat(new Quaternion());
        b.setAxis(new Vector3f(0, 1, 0.5f));
        b.setTimer(timer);
        b.setSpinnable(false);
        b.setModelBound(new BoundingSphere());
        b.updateModelBound();
        b.setRenderState(ts2);
        spinList.add(b);

        b2 = new ObjectToSpin("My Box2", new Vector3f(-1, -1, -1), new Vector3f(1, 1, 1));
        b2.setQuat(new Quaternion());
        b2.setAxis(new Vector3f(1, 0, 0));
        b2.setTimer(timer);
        b2.setSpinnable(false);
        b2.setLocalTranslation(new Vector3f(-5,0,0));
        b2.setModelBound(new BoundingSphere());
        b2.updateModelBound();
        b2.setRenderState(ts3);
        spinList.add(b2);

        rootNode.attachChild(b);
        rootNode.attachChild(b2);
        rootNode.attachChild(am);
        lightState.detachAll();

        pr = new BoundingPickResults();
    }

    protected void simpleUpdate(){

         for (int i = 0; i < spinList.size(); i++){
             ObjectToSpin spinnable = (ObjectToSpin)spinList.get(i);
             if (spinnable.isSpinnable()){
                 spinnable.setTimer(timer);
                 spinnable.spin();
             }
        }

        MouseInput thisMouse = am.getMouseInput();

        if (thisMouse.isButtonDown(0)){

            Vector2f screenPos = new Vector2f();
            screenPos.set(am.getHotSpotPosition().x, am.getHotSpotPosition().y);
            Vector3f worldCoords = display.getWorldCoordinates(screenPos, 0);
            Ray mouseRay = new Ray(cam.getLocation(), worldCoords.subtractLocal(cam.getLocation()));
            pr.clear();
            rootNode.findPick(mouseRay, pr);

            for (int i = 0; i < pr.getNumber(); i++){
                toSpin = (ObjectToSpin)pr.getPickData(i).getTargetMesh();
                toSpin.setSpinnable(true);
            }
        }
        if (thisMouse.isButtonDown(1)){

            Vector2f screenPos = new Vector2f();
            screenPos.set(am.getHotSpotPosition().x, am.getHotSpotPosition().y);
            Vector3f worldCoords = display.getWorldCoordinates(screenPos, 0);
            Ray mouseRay = new Ray(cam.getLocation(), worldCoords.subtractLocal(cam.getLocation()));
            pr.clear();
            rootNode.findPick(mouseRay, pr);

            for (int i = 0; i < pr.getNumber(); i++){
                toSpin = (ObjectToSpin)pr.getPickData(i).getTargetMesh();
                toSpin.setSpinnable(false);
            }
        }
    }

    public class ObjectToSpin extends Box {

        private Quaternion quat;

        private boolean spinnable;
        private Vector3f axis;
        private Timer timer;
        private float angle = 0f;

        public ObjectToSpin(String name, Vector3f start, Vector3f end){
            super(name, start, end);
        }

        public Quaternion getQuat() {
            return quat;
        }


        public void setQuat(Quaternion quat) {
            this.quat = quat;
        }

        public Vector3f getAxis() {
            return axis;
        }

        public void setAxis(Vector3f axis) {
            this.axis = axis;
        }

        public Timer getTimer() {
            return timer;
        }

        public void setTimer(Timer timer) {
            this.timer = timer;
        }

        public boolean isSpinnable() {
            return spinnable;
        }

        public void setSpinnable(boolean spin) {
            this.spinnable = spin;
        }

        public float getAngle() {
            return angle;
        }

        public void setAngle(float angle) {
            this.angle = angle;
        }

        public void spin(){
            if (this.timer.getTimePerFrame() < 1) {
                angle = angle + (this.timer.getTimePerFrame() * 50);
                if (angle > 360) {
                    angle = 0;
                }
            }
            this.getQuat().fromAngleAxis(angle*FastMath.DEG_TO_RAD, axis);
            this.setLocalRotation(this.getQuat());
        }
    }
}

OK, below is a link to the JWS version I put up. It will probably only run with Java 1.5 but it has libs for Linux,Windows, and Mac. This is just if you want to see it without compiling and running it yourself.



http://www.embeddedthought.com/gregg/jme/gthought.jnlp



Let me know if you are running Java 1.5 and are having issues with it.