Center location of a Geometry

Is there a function to get the center location of a geometry?



I’m trying to place a new box next to another box after ‘picking’ the first one.



Here’s the general idea: (note, the .getCenter() is just a place holder atm until I can find out how to get the center of the first box)

[java] boxNode.setLocalTranslation(closest.getGeometry().getCenter().add(closest.getContactNormal().mult(2f)));[/java]

if you created the box with new Box(Vector3fZero,1,1,1)

the center of the box (in local space is) Vector3fZero

Hmmm, so from what you said I’m getting the understanding that the center of the box is simply the local translation of the entire box.



So now I have this just to try to make a box in exactly the same spot:

[java]boxNode.setLocalTranslation(closest.getGeometry().getLocalTranslation());[/java]



But now it’s making the new boxes at the origin. Also, if I try to ‘pick’ a box that was created while running the game and I’m close to that box, the game crashes. Pretty stuck and not sure why it’s doing this. :? Any ideas?

Hm, my best guess is you use the Ray class wrong, can you show the relevant code?

Also, if I try to ‘pick’ a box that was created while running the game and I’m close to that box, the game crashes.

I've had the same playing around with picking up and putting down boxes based on the jme3 HelloPicking example.
The boxes end up on the wrong place and sometimes the program crashes (I believe when there is another object behind wen picking/placing).
Code:
// 1. Reset results list. CollisionResults results = new CollisionResults(); // 2. Aim the ray from cam loc to cam direction. Ray ray = new Ray(cam.getLocation(), cam.getDirection()); // 3. Collect intersections between Ray and Shootables in results list. shootables.collideWith(ray, results);
    for (int i = 0; i < results.size(); i++) {
        // For each hit, we know distance, impact point, name of geometry.
        float dist = results.getCollision(i).getDistance();
        Vector3f pt = results.getCollision(i).getContactPoint();
        Geometry geo = results.getCollision(i).getGeometry();
        if(geo != null){
            String hit = geo.getName();
            System.out.println("* Collision #" + i);
            System.out.println("  You shot " + hit + " at " + pt + ", " + dist + " wu away.");
        }
    }</div>
I had to check that the geometry you get back is in fact not null
Code:
Geometry geo = results.getCollision(i).getGeometry(); if(geo != null){

Why it gets to be null sometimes is beyond me, but I can only conclude that raycasting sometimes doesnt work (at least in the way it's used in the example).
Would be interested too in what's going wrong.

Can you please post a test case showing the problem? I can’t reproduce the issue with the HelloPicking test.

ok I have played around with the (altered - since i did the exercizes) HelloPicking example again and

  1. The nullpointer exception is gone. Geometry is always ok now. I did get the latest nightly build this week though. Could that be it?
  2. I can now stack cubes reliably on top of eachother without them jumping all over the place. The issue was that, in the example’e makeCube() method, a box gets created and then it gets move()ed. This movement is apparenly attached to the Box shape. When you then put the box somewhere else (with geometry.Translate()) the movement is still added an it ends up all over the place.

    In stead the example should create a Box, add it to a Geom and then translate the geom to position it.



    Here is my finished exercize. Left button to shoot and mark a ray, right mouse button to pick up and drop a cube/ogre. Maybe it helps.
Code:
import com.jme3.app.SimpleApplication; import com.jme3.bounding.BoundingBox; import com.jme3.collision.CollisionResult; import com.jme3.collision.CollisionResults; import com.jme3.font.BitmapText; import com.jme3.input.KeyInput; import com.jme3.input.controls.ActionListener; import com.jme3.input.controls.KeyTrigger; import com.jme3.input.controls.MouseButtonTrigger; import com.jme3.light.DirectionalLight; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.math.Ray; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Sphere;

/** Sample 8 - how to let the user pick (select) objects in the scene

  • using the mouse or key presses. Can be used for shooting, opening doors, etc. */
    public class HelloPicking extends SimpleApplication {

    public static void main(String[] args) {
    HelloPicking app = new HelloPicking();
    app.start();
    }
    Node shootables;
    Geometry mark;
    Node inventory;
    boolean hasItem = false;
    String itemName = “”;
    Geometry itemGeo;

    @Override
    public void simpleInitApp() {
    initCrossHairs(); // a “+” in the middle of the screen to help aiming
    initKeys(); // load custom key mappings
    initMark(); // a red sphere to mark the hit

     /** create four colored boxes and a floor to shoot at: */
     shootables = new Node("Shootables");
     rootNode.attachChild(shootables);
     shootables.attachChild(makeCube("a Dragon", -2f, 0f, 1f));
     shootables.attachChild(makeCube("a tin can", 1f, -2f, 0f));
     shootables.attachChild(makeCube("the Sheriff", 0f, 1f, -2f));
     shootables.attachChild(makeCube("the Deputy", 1f, 0f, -4f));
     shootables.attachChild(makeFloor());
    
     // add a golem to the scene
     Spatial golem = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
     golem.scale(0.5f);
     golem.move(-2f, 2, 3f);
     golem.setModelBound(new BoundingBox()); // apparently needed for collision-
     golem.updateModelBound();               // detection but with or without
     // it still isn't right
     shootables.attachChild(golem);
    
     // golem needs lighting to be seen
     DirectionalLight dl = new DirectionalLight();
     dl.setDirection(new Vector3f(-0.1f, -1f, -1).normalizeLocal());
     rootNode.addLight(dl);
    
     inventory = new Node("Inventory");
    

    }

    /** Declaring the “Shoot” action and mapping to its triggers. */
    private void initKeys() {
    inputManager.addMapping(“Shoot”,
    new KeyTrigger(KeyInput.KEY_SPACE), // trigger 1: spacebar
    new MouseButtonTrigger(0)); // trigger 2: left-button click
    inputManager.addListener(actionListener, “Shoot”);
    inputManager.addMapping(“Pick”,
    new MouseButtonTrigger(1)); // trigger 2: right-button click
    inputManager.addListener(actionListener, “Pick”);
    }

    /** Defining the “Shoot” action: Determine what was hit and how to respond. */
    private ActionListener actionListener = new ActionListener() {

     @Override
     public void onAction(String name, boolean keyPressed, float tpf) {
         if(keyPressed) return;  // or it fires twice
         // 1. Reset results list.
         CollisionResults results = new CollisionResults();
         // 2. Aim the ray from cam loc to cam direction.
         Ray ray = new Ray(cam.getLocation(), cam.getDirection());
         // 3. Collect intersections between Ray and Shootables in results list.
         shootables.collideWith(ray, results);
         // 4. Print the results.
         if(results.size()==0){
                 System.out.println("Miss");
                 rootNode.detachChild(mark);
                 return;
         }
         CollisionResult closest = results.getClosestCollision();
         Geometry closestGeo = closest.getGeometry();
         for (int i = 0; i < results.size(); i++) {
             // For each hit, we know distance, impact point, name of geometry.
             float dist = results.getCollision(i).getDistance();
             Vector3f pt = results.getCollision(i).getContactPoint();
             String hit = results.getCollision(i).getGeometry().getName();
             System.out.println(i+": Hit " + hit + " at " + pt + ", " + dist + " wu away.");
         }
         if (name.equals("Shoot") && !keyPressed) {
             if (results.size() > 0) {
                 System.out.println("Shoot "+closest.getGeometry().getName());
                 // we mark the hit with a red dot.
                 mark.setLocalTranslation(closest.getContactPoint());
                 rootNode.attachChild(mark);
                 // color the victim
                 rootNode.getChild(closest.getGeometry().getName()).setMaterial(
                         assetManager.loadMaterial("Materials/Leakthrough.j3m"));
             }
         }
         if (name.equals("Pick") && !keyPressed) {
             if (!hasItem) {
                 if (results.size() > 0) {
                     System.out.println("Pick up "+closest.getGeometry().getName());
                     // The closest collision point is what was truly hit:
                     Geometry geo = closest.getGeometry();
                     shootables.detachChild(geo);
                     itemGeo = geo;  // put in inventory
                     hasItem = true;
                 }
             } else {
                 if (results.size() > 0) {
                     System.out.println("Drop "+closest.getGeometry().getName());
                     itemGeo.setLocalTranslation(closest.getContactPoint().add(0,1,0));
                     shootables.attachChild(itemGeo);
                     hasItem = false;
                 }
             }
         }
     }
    

    };

    /** A cube object for target practice */
    protected Geometry makeCube(String name, float x, float y, float z) {
    Box box = new Box(1, 1, 1);
    Geometry cube = new Geometry(name, box);
    Material mat1 = new Material(assetManager, “Common/MatDefs/Misc/SolidColor.j3md”);
    mat1.setColor(“m_Color”, ColorRGBA.randomColor());
    cube.setMaterial(mat1);
    cube.setLocalTranslation(x, y, z);
    return cube;
    }

    /** A floor to show that the “shot” can go through several objects. */
    protected Geometry makeFloor() {
    Box box = new Box(new Vector3f(0, -4, -5), 15, .2f, 15);
    Geometry floor = new Geometry(“the Floor”, box);
    Material mat1 = new Material(assetManager, “Common/MatDefs/Misc/SolidColor.j3md”);
    mat1.setColor(“m_Color”, ColorRGBA.Gray);
    floor.setMaterial(mat1);
    return floor;
    }

    /** A red ball that marks the last spot that was “hit” by the “shot”. */
    protected void initMark() {
    Sphere sphere = new Sphere(30, 30, 0.15f);
    mark = new Geometry(“BOOM!”, sphere);
    Material mark_mat = new Material(assetManager, “Common/MatDefs/Misc/SolidColor.j3md”);
    mark_mat.setColor(“m_Color”, ColorRGBA.Red);
    mark.setMaterial(mark_mat);
    }

    /** A centred plus sign to help the player aim. */
    protected void initCrossHairs() {
    guiNode.detachAllChildren();
    guiFont = assetManager.loadFont(“Interface/Fonts/Default.fnt”);
    BitmapText ch = new BitmapText(guiFont, false);
    ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
    ch.setText("+"); // crosshairs
    ch.setLocalTranslation( // center
    settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2,
    settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
    guiNode.attachChild(ch);
    }
    }

[java]

/** geometries and collisions shapes for bricks and cannon balls. /

private static final Box brick;

private static final BoxCollisionShape boxCollisionShape;

private static final Sphere cannonball;

private static final SphereCollisionShape cannonballCollisionShape;



/
* brick dimensions /

private static final float brickLength = 1f;

private static final float brickWidth = 1f;

private static final float brickHeight = 1f;



/
* Materials /

Material wall_mat;

Material stone_mat;

Material terrain_mat;



/
* terrain /

private TerrainQuad terrain;



/
* movement setup /

Boolean left = false, right = false, up = false, down = false;

private Vector3f walkDirection = new Vector3f();



/
* Physics for players, landscape and projectiles /

private BulletAppState bulletAppState;

PhysicsCharacterNode player;

PhysicsNode landscape;



static {

brick = new Box(Vector3f.ZERO, brickLength, brickHeight, brickWidth);

brick.scaleTextureCoordinates(new Vector2f(1f, .5f));

boxCollisionShape =

new BoxCollisionShape(new Vector3f(brickLength, brickHeight, brickWidth));

/
* initializing the cannon ball geometry that is reused later /

cannonball = new Sphere(32, 32, 0.4f, true, false);

cannonball.setTextureMode(TextureMode.Projected);

cannonballCollisionShape=new SphereCollisionShape(0.4f);

}



/
* A loop that builds a wall out of individual bricks. */

public void initWall() {

float startpt = brickLength / 4;

float height = 0;

for (int j = 0; j < 10; j++) {

for (int i = 0; i < 4; i++) {

Vector3f vt =

new Vector3f(i * brickLength * 2 + startpt, brickHeight + height, 0);

makeBrick(vt);

}

startpt = -startpt;

height += 2 * brickHeight;

}

}

[/java]



For some reason the rest of the code didn’t show up, so I’m including it in a block quote:

/** This method creates one individual physical brick. */
protected void makeBrick(Vector3f location) {
/** initializing the brick geometry that is reused later */
Geometry box_geo = new Geometry("brick", brick);
box_geo.setMaterial(wall_mat);
//box_geo.setLocalTranslation(location);
PhysicsNode brickNode = new PhysicsNode(
box_geo, // geometry
boxCollisionShape, // collision shape
10f); // mass
/** position the brick and activate shadows */
brickNode.setLocalTranslation(location);
rootNode.attachChild(brickNode);
bulletAppState.getPhysicsSpace().add(brickNode);
}

public void placeBrick() {
// 1. Reset results list.
CollisionResults results = new CollisionResults();
// 2. Aim the ray from cam loc to cam direction.
Ray ray = new Ray(cam.getLocation(), cam.getDirection());
// 3. Collect intersections between Ray and Shootables in results list.
rootNode.collideWith(ray, results);

if (results.size() > 0) {
// The closest collision point is what was truly hit:
CollisionResult closest = results.getClosestCollision();
Vector3f brickPosition = new Vector3f(closest.getGeometry().getLocalTranslation());
makeBrick(brickPosition);
}
}


It seems to me that 'closest.getGeometry().getLocalTranslation());' is always returning the origin for some reason. From my understanding, this code should place a new box in the same spot as the one I 'picked'. I'm not sure how to properly debug stuff so I can't check out the value for sure.

I've also tried translating the geometry as Durandal suggested (see line 60), but that just makes things go bat-shit crazy. (boxes from the wall flying around everywhere like crazy).

Since you’re attaching your brick geometry to a physics node, and then modifying the position of that, the local translation of your brick geometry will be 0 then.

[java]

Geometry box_geo = new Geometry(“brick”, brick);

PhysicsNode brickNode = new PhysicsNode( box_geo, … )

brickNode.setLocalTranslation(location);

[/java]



Consider maybe using getWorldTranslation() to get the world space translation.

Thanks Momoko, that seems to have done the trick. :slight_smile: The box locations are now being correctly identified, however the previous results persist if the position of the boxes are close to the origin, even if I click on the terrain (usually it crashes if I click on the terrain). So whenever the box I am trying to pick is less than say 5f-10f from the origin (in all directions), the new boxes are created at the origin. I should be able to avoid that portion of my map, but it seems like a silly effect to be having.



Is there a minimum distance for getWorldTranslation() to return something that isn’t 0?