Picking in Parallel Projection

Hi everyone,



I've just set up a 4x4 set of tiles in a quadtree with the toplevel nodes having a Quad each. Each Quad is surrounded by a OrientedBoundingBox. I've got the picking working in normal perspective, but when I change to parallel projection, the picking doesn't make sence anymore.



The camera is set up like so when it works:


Actually, the problem might be far more basic. I'm getting the idea that the conversion from screen coordinates to world coordinates isn't right. This is the code that I'm using:


            Vector2f screenPos = new Vector2f();
            screenPos.set( mouse.getHotSpotPosition().x, mouse.getHotSpotPosition().y );
           
           
            // Convert to world coordinates
            display = SceneManager.getDisplay();
            Vector3f worldCoordinates = display.getWorldCoordinates( screenPos, 0 );
          
           
            // Create a ray going from the camera to the mouse and beyond
            Ray mouseRay = new Ray( camera.getLocation(), worldCoordinates.subtractLocal( camera.getLocation() ) );
            pickResults.clear();
            pickResults.setCheckDistance( true );
           
            Node rootNode = SceneManager.getRootNode();
            rootNode.findPick( mouseRay, pickResults );
            for(int i = 0; i < pickResults.getNumber(); i++){
                PickData closest = pickResults.getPickData(0);
                closest.getTargetMesh().setRandomColors();
            }



The SceneManager is a static class so I can acces the root node and display in a different class without having to pass them along a parameters all the way.

Does anyone see a place where I'm goin wrong?

It is likely that screen<->world coordinates are computed wrong in parallel mode - nobody has tested it yet (parallel mode is quite new). As well I suspect culling to be flawed in parallel mode… I'm going to have a look at that as soon as I can.

good news: getWorldCoordinates works fine for otho3d, too :slight_smile:

Your problem is that your ray is constructed wrong: In orthogonal projection the ray must not start at the camera location. I changed HelloMousePick to use getWorldCoordinates twice - have a look at it for a code example.

Thanks, I'm getting right on it.

At least now both perspectives are producing the same strange behaviour :wink:



I've got a 33 by 33 tile (Quad) map set up, rotated 1/2 pi around the z-axes. I've set it up in such a manner now that a clicked tile should change color and this works. The problem that now arises is that the picking isn't always correct. Especially after I move my mouse a lot between clicks, the tile that is selected is the wrong one. It seems as though the the mouse position that the system reacts to comes from a mouse with a slower movement rate then the one on-screen.



This is what my action handler looks like:



package nl.tygron.simport.client.gui.input;

import nl.tygron.simport.client.gui.SceneManager;
import nl.tygron.simport.client.gui.Tile;

import com.jme.input.MouseInput;
import com.jme.input.AbsoluteMouse;
import com.jme.input.action.InputActionEvent;
import com.jme.input.action.MouseInputAction;
import com.jme.intersection.PickData;
import com.jme.intersection.PickResults;
import com.jme.math.Ray;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.scene.Node;
import com.jme.system.DisplaySystem;

public class SimPortMouseAction extends MouseInputAction {

    private MouseInput input;

    private AbsoluteMouse mouse;

    private final int LEFT_BUTTON = 0;

    private DisplaySystem display;

    private PickResults pickResults;

    private boolean wasDown = false;

    public SimPortMouseAction( AbsoluteMouse n_mouse, Camera n_camera, PickResults n_pickResults ) {

        mouse = n_mouse;
        pickResults = n_pickResults;
        input = MouseInput.get();
    }

    @Override
    public void performAction( InputActionEvent action ) {

        if (input.isButtonDown( LEFT_BUTTON )) {
            if (!wasDown) {
                Vector2f screenPos = new Vector2f();
                screenPos.set( mouse.getHotSpotPosition().x, mouse.getHotSpotPosition().y );
               
                display = SceneManager.getDisplay();
                // Get the world location of that X,Y value
                Vector3f worldCoords = display.getWorldCoordinates(screenPos, 0);
                Vector3f worldCoords2 = display.getWorldCoordinates(screenPos, 1);

                // Create a ray starting from the camera, and going in the direction
                // of the mouse's location
                Ray mouseRay = new Ray(worldCoords, worldCoords2.subtractLocal(worldCoords));

                pickResults.clear();
                pickResults.setCheckDistance( true );

                Node rootNode = SceneManager.getRootNode();
                rootNode.findPick( mouseRay, pickResults );
                for (int i = 0; i < pickResults.getNumber(); i++) {
                    PickData closest = pickResults.getPickData( 0 );
                    Tile tile = (Tile) closest.getTargetMesh().getParent();
                    tile.handleClick();
                }
                wasDown = true;
            }
        } else {
            wasDown = false;
        }

    }

}

Is that "mouse" in the source above the same AbsoluteMouse that visualizes your mouse cursor? If no consider that. Or if you are using the hardware cursor call mouse.setUsingDeltas( false ).

:?

I have been facing an "Arithmetic Exception:  This matrix cannot be inverted" when trying parallel picking in my game.  I am not sure what the problem is, but I am able to reproduce the error in HelloMousePick using the following steps:



1.  In BasicSimpleGame, change the 50 in the below line to something larger, like 500 (I am not sure HOW much larger to recreate the error.  500 does it for me though.)


protected void cameraParallel() {
        cam.setParallelProjection( true );
        float aspect = (float) display.getWidth() / display.getHeight();
        cam.setFrustum( -100, 1000, -500 * aspect, 500 * aspect, -500, 500 ); <


I have changed the 50s to 500s. 
        cam.update();
    }



2.  Run HelloMousePick, change the thing to parallel projection (press F2) and click a couple times.  You should get an Arithmetic Exception with a stack trace to the tune of the following:


java.lang.ArithmeticException: This matrix cannot be inverted
   at com.jme.math.Matrix4f.invert(Matrix4f.java:1075)
   at com.jme.renderer.AbstractCamera.getWorldCoordinates(AbstractCamera.java:942)
   at com.jme.system.DisplaySystem.getWorldCoordinates(DisplaySystem.java:745)
   at com.jme.system.DisplaySystem.getWorldCoordinates(DisplaySystem.java:728)
   at jmetest.TutorialGuide.HelloMousePick.simpleUpdate(HelloMousePick.java:130)
   at com.jme.app.SimpleGame.update(SimpleGame.java:72)
   at com.jme.app.BaseGame.start(BaseGame.java:66)
   at jmetest.TutorialGuide.HelloMousePick.main(HelloMousePick.java:75)



I traced this to the point where the code compares (FastMath compare) a variable called 'fDet' to an Epsilon (Error tolerance?) value in the Matrix4f's invert function.  Is there something wrong with making the world coordinates so vast?  I was hoping for a 1600x1200 world. Or is this something I should flag as a bug?

Thanks in advance!

fDet is the determinant of the matrix, and a requirement for a matrix to be invertible is that it's determinant != 0 (the epsilon). in this setup it seems the modelviewprojection matrix is ill conditioned/non-invertible, which can be the case with strange projections. don't have the code up front so i can't give you any better explanation this second, but i'll look at it later…