Which way is down!

Hi guys,



I'm sorry, but I think this is a simple question, but I am not getting the expected result.



I would like to put some boxes on a TerrainBlock. I have positioned a block waaay above the terrain and attempted to cast a ray straight down. However, I must be missing something (hopefully obvious) because the ray is only finding one thing… The box itself. I would have hoped on its journey it would have found the terrain as well. No such luck! Here is the code:


        Box b = new Box("Number Box", new Vector3f(50,100,50), 2, 2, 2); // put it up nice and high
        b.setModelBound(new BoundingBox());
        b.updateModelBound();

        Vector3f down = new Vector3f(0f, -1f, 0f);

        Ray boxesFloor = new Ray(b.getCenter(), down);
        PickResults results = new BoundingPickResults(); /* Set accuracy */
        results.setCheckDistance(true);  /* Order results closest first */
        b.findPick(boxesFloor, results);

        System.out.println("Picks found=" + results.getNumber());

        if (results.getNumber() > 0) {
            PickData data = results.getPickData(0);
            System.out.println(data.getTargetMesh().toString());
            System.out.println(data.getDistance());
        }


        rootNode.attachChild(b);


The annoying result I get is:

Picks found=1
Number Box (com.jme.scene.shape.Box)
0.0

I've checked that I added the terrain first (I have). I'm out of ideas  :( I never really got how a Vector3f could represent direction to be honest. I am struggling to find it documented on the Wiki, but that may be ineptitude  :P

Yours hopefully
Richard

Hi,



i think you need to use TrianglePickResult instead of BoundingPickResult to pick the Terrain.



Dennis

i think you need to use TrianglePickResult instead of BoundingPickResult to pick the Terrain.


Thanks for your response.

I did try that shortly after posting! Changing to TrianglePickResult doesn't make a difference in terms of results (ie only one found). The difference was that the distance was now 2.0, so I will clearly need to use the TrianglePickResult, but I still don't understand why it's not finding the terrain!!

Your Ray probably hits the box first then the Terrain,

A ray hits multiple targets on it infinite way into the direction you cast it.

You need to iterate over the results until you find want you want.

Thanks Core-Dump, but it says Picks found = 1. Am I missing something?



PickData data = results.getPickData(0) works ok (and returns me the box), but PickData data = results.getPickData(1); throws an IndexArray exception.



Am I doing it wrong?



Question: new Vector3f(0f, -1f, 0f); // This is down, right!?

ah sorry, i didnt see that "Picks found = 1".

And yes 0, -1, 0 is down.



The Problem might be the way you create your Box.

Usually you need to keep the Box Center at 0,0,0.

With your Constructor the center is at 50,100,50 and localtranslation propably 0,0,0?



Use this instead:


Box b = new Box("Number Box", new Vector3f(0,0,0), 2, 2, 2); // create a Box with center at 0,0,0, size 2,2,2
b.setModelBound(new BoundingBox());
b.updateModelBound();
b.setLocalTranslation(50,100,50);// put it up nice and high

Thanks for taking another look at this core-dump, but I'm afraid that hasn't quite nailed it either  :?



I have managed to create a test class that illustrates the issue. Maybe I'm doing something wrong with the terrain placement?



package jmetry1;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.intersection.PickData;
import com.jme.intersection.PickResults;
import com.jme.intersection.TrianglePickResults;
import com.jme.math.Ray;
import com.jme.math.Vector3f;
import com.jme.scene.shape.Box;
import com.jme.scene.state.TextureState;
import com.jme.util.TextureManager;
import com.jmex.terrain.TerrainBlock;
import java.util.Random;

public class Main extends SimpleGame {

    public static void main(String[] args) {
        Main app = new Main();
        app.setConfigShowMode(ConfigShowMode.AlwaysShow);
        app.start();
    }

    protected void simpleInitGame() {
        addTerrain();
        addBoxes();
    }

    private void addTerrain() {
        float[] map = new float[100];
        int heightValue = 0;
        Random rand = new Random();
       
        for (int i = 0; i < map.length; i++) {
            heightValue += (rand.nextInt(11) - 5);
            map[i] = heightValue;
        }

        Vector3f scale = new Vector3f(100f, 0.5f, 100f);
        TerrainBlock tb = new TerrainBlock("Simple terrain", 10, scale, map, new Vector3f(0, 0, 0));
        tb.setModelBound(new BoundingBox());
        tb.updateModelBound();

        try {
            Texture grassTexture = TextureManager.loadTexture(Main.class.getClassLoader().getResource("jmetest/data/texture/dirt.jpg"), Texture.MinificationFilter.BilinearNearestMipMap, Texture.MagnificationFilter.Bilinear);
            grassTexture.setScale(new Vector3f(100f, 100f, 100f));
            grassTexture.setWrap(Texture.WrapMode.Repeat);
            TextureState grassTextureState = display.getRenderer().createTextureState();
            grassTextureState.setEnabled(true);
            grassTextureState.setTexture(grassTexture);
            tb.setRenderState(grassTextureState);
        } catch (Exception ex) {
            System.err.println("Unable to load terrain image: " + ex);
        }

        rootNode.attachChild(tb);
    }

    private void addBoxes() {
        Box b = new Box("Number Box", new Vector3f(0,0,0), 2, 2, 2); // put it up nice and high
        b.setModelBound(new BoundingBox());
        b.updateModelBound();
        b.setLocalTranslation(50f, 100f, 50f);

        Vector3f down = new Vector3f(0f, -1f, 0f);

        Ray boxesFloor = new Ray(b.getCenter(), down);
        PickResults results = new TrianglePickResults(); /* Set accuracy */
        results.setCheckDistance(true);  /* Order results closest first */
        b.findPick(boxesFloor, results);

        System.out.println("Picks found=" + results.getNumber());

        if (results.getNumber() > 0) {
            PickData data = results.getPickData(0);
            System.out.println(data.getTargetMesh().toString());
            System.out.println(data.getDistance());
        }

        rootNode.attachChild(b);
    }
}



I can sleep easier once I know what the problem is  ;)

there are a few things:

you did: box.findPick(ray), this check just if the box intersects with the ray.

you need to do rootNode.findPick(ray) to find all meshes who intersect.



Then findPick works with world bounds, thats why updateGeometricstate() (or at least updateWoldBound()) needs to be called before the pick.



    private void addBoxes() {
        Box b = new Box("Number Box", new Vector3f(0,0,0), 2, 2, 2); // put it up nice and high
        b.setModelBound(new BoundingBox());
        b.updateModelBound();
        b.setLocalTranslation(50f, 100f, 50f);
        Vector3f down = new Vector3f(0f, -1f, 0f);
        // Attach the box to the scene and update world bounds and coordinates
        rootNode.attachChild(b);
        rootNode.updateGeometricState(0,false);
        
        Ray boxesFloor = new Ray(b.getLocalTranslation(), down);
        PickResults results = new TrianglePickResults(); /* Set accuracy */
        results.setCheckDistance(true);  /* Order results closest first */
        // find all meshes who intersect with the ray
        rootNode.findPick(boxesFloor, results);

        System.out.println("Picks found=" + results.getNumber());

        if (results.getNumber() > 0) {
            PickData data = results.getPickData(1);
            System.out.println(data.getTargetMesh().toString());
            System.out.println(data.getDistance());
        }
    }


You're a star core-dump. I can safely say it would have been a long time before I took a stab at updateGeometricState() !



I guess I could (should?) use terrainBlock.findPick(…), as all I am interested in finding out is where the ray intersects with the terrain.



I would be happy to write this up with an example if you think it would add value to the Wiki?



Thanks again

Richard

Sure i guess terrainBlock.findPick() would be the best in your case.

Contributing to the wiki as always a good idea. i just saw there is already a big entry about picking http://www.jmonkeyengine.com/wiki/doku.php/picking