Picking, Dragging and Dropping

Hi everyone,



I just managed to implement these three funtionalities, but I’m getting a bug everytime. The object that I start to move sinks a bit inside the ground while dragging. Here is my code:



[java]

/*

  • To change this template, choose Tools | Templates
  • and open the template in the editor.

    */

    package de.dfki.carmina.dte.jme;



    import com.jme3.app.SimpleApplication;

    import com.jme3.asset.plugins.ZipLocator;

    import com.jme3.collision.CollisionResults;

    import com.jme3.input.ChaseCamera;

    import com.jme3.input.KeyInput;

    import com.jme3.input.MouseInput;

    import com.jme3.input.controls.ActionListener;

    import com.jme3.input.controls.AnalogListener;

    import com.jme3.input.controls.KeyTrigger;

    import com.jme3.input.controls.MouseAxisTrigger;

    import com.jme3.input.controls.MouseButtonTrigger;

    import com.jme3.light.AmbientLight;

    import com.jme3.math.ColorRGBA;

    import com.jme3.math.FastMath;

    import com.jme3.math.Ray;

    import com.jme3.math.Vector3f;

    import com.jme3.scene.Node;

    import com.jme3.scene.Spatial;



    /**

    *
  • @author Biasutti

    /

    public class Scenario extends SimpleApplication {



    private ChaseCamera camera;

    private boolean walk = true;

    private boolean dragAndDrop = false;

    private Node walker;

    private Node scenario;

    private Node selectables;

    private Node notSelectables;

    private Spatial selectedObject;

    private AnalogListener analogListener = new AnalogListener() {



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

    if (walk) {

    if (name.equals("Right")) {

    walker.move(-value * 20, 0, 0);

    }

    if (name.equals("Left")) {

    walker.move(value * 20, 0, 0);

    }

    if (name.equals("Front")) {

    walker.move(0, 0, value * 20);

    }

    if (name.equals("Back")) {

    walker.move(0, 0, -value * 20);

    }

    }

    if (dragAndDrop && name.equals("Moving") && selectedObject != null) {

    System.out.println("Moving");

    CollisionResults results = new CollisionResults();

    Vector3f origin = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.0f);

    Vector3f direction = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.3f);

    direction.subtractLocal(origin).normalizeLocal();



    Ray ray = new Ray(origin, direction);

    scenario.collideWith(ray, results);

    if (results.size() > 0) {

    Vector3f newLoc = results.getClosestCollision().getContactPoint();

    selectedObject.setLocalTranslation(newLoc);

    }



    }

    }

    };

    private ActionListener actionListener = new ActionListener() {



    @Override

    public void onAction(String name, boolean pressed, float f) {

    if (name.equals("Picking")) {

    if (pressed) {

    System.out.println("Clicked");

    CollisionResults results = null;

    // 1. Reset results list.

    results = new CollisionResults();

    // 2. Aim the ray from cam loc to cam direction.

    Vector3f origin = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.0f);

    Vector3f direction = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.3f);

    direction.subtractLocal(origin).normalizeLocal();



    Ray ray = new Ray(origin, direction);

    // 3. Collect intersections between Ray and Shootables in results list.

    selectables.collideWith(ray, results);



    if (results.size() > 0) {

    selectedObject = selectables.getChild(results.getClosestCollision().getGeometry().getName());

    selectables.detachChild(selectedObject);

    notSelectables.attachChild(selectedObject);

    }

    dragAndDrop = true;

    } else {

    if(selectedObject != null) {

    notSelectables.detachChild(selectedObject);

    selectables.attachChild(selectedObject);

    selectedObject = null;

    }

    dragAndDrop = false;

    System.out.println("Dropped");

    }

    }

    }

    };



    @Override

    public void simpleInitApp() {



    walker = new Node("walker");

    selectables = new Node("selectables");

    scenario = new Node("scenario");

    notSelectables = new Node("notSelectables");



    flyCam.setEnabled(false);

    camera = new ChaseCamera(cam, walker, inputManager);

    camera.setDefaultVerticalRotation(FastMath.HALF_PI);

    camera.setDragToRotate(true);

    camera.setTrailingEnabled(false);

    camera.setMinVerticalRotation(FastMath.HALF_PI);

    camera.setMaxVerticalRotation(FastMath.HALF_PI);

    camera.setDefaultDistance(100);

    camera.setMaxDistance(500);

    camera.setMinDistance(30);

    camera.setZoomInTrigger(new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false), new KeyTrigger(KeyInput.KEY_I));

    camera.setZoomOutTrigger(new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true), new KeyTrigger(KeyInput.KEY_O));

    camera.setZoomSensitivity(50f);



    camera.setToggleRotationTrigger();

    camera.setSmoothMotion(false);

    camera.setRotationSensitivity(0);

    camera.setInvertHorizontalAxis(false);



    rootNode.attachChild(walker);

    scenario.attachChild(selectables);

    rootNode.attachChild(scenario);

    rootNode.attachChild(notSelectables);



    /
    * A white ambient light source. /

    AmbientLight ambient = new AmbientLight();

    ambient.setColor(ColorRGBA.White.mult(4f));

    rootNode.addLight(ambient);



    initKeys();



    dummyDraw();

    }



    /
    * Custom Keybinding: Map named actions to inputs. */

    private void initKeys() {

    // You can map one or several inputs to one named action

    inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_LEFT), new KeyTrigger(KeyInput.KEY_A));

    inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_RIGHT), new KeyTrigger(KeyInput.KEY_D));

    inputManager.addMapping("Front", new KeyTrigger(KeyInput.KEY_UP), new KeyTrigger(KeyInput.KEY_W));

    inputManager.addMapping("Back", new KeyTrigger(KeyInput.KEY_DOWN), new KeyTrigger(KeyInput.KEY_S));

    inputManager.addMapping("Picking", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));

    inputManager.addMapping("Moving", new MouseAxisTrigger(MouseInput.AXIS_X, true));

    inputManager.addMapping("Moving", new MouseAxisTrigger(MouseInput.AXIS_X, false));

    inputManager.addMapping("Moving", new MouseAxisTrigger(MouseInput.AXIS_Y, true));

    inputManager.addMapping("Moving", new MouseAxisTrigger(MouseInput.AXIS_Y, false));





    inputManager.addListener(analogListener, new String[]{"Left", "Right", "Front", "Back", "Moving"});

    inputManager.addListener(actionListener, new String[]{"Picking"});

    }



    @Override

    public void simpleUpdate(float tpf) {

    rootNode.updateGeometricState();

    }



    private void dummyDraw() {



    assetManager.registerLocator("C:\Users\Biasutti\Downloads\town.zip", ZipLocator.class.getName());

    Spatial gameLevel = assetManager.loadModel("main.scene");

    gameLevel.setLocalScale(2);

    gameLevel.center();





    selectables.attachChild(assetManager.loadModel("Models/Oto/Oto.mesh.xml"));

    scenario.attachChild(gameLevel);

    }

    }

    [/java]



    I will be glad if anyone is able to see problem!

    Thanks in advance,

    Biasutti

Hey Biasutti,



At the risk of showing off my absolute noob’ness of JME3, can i suggest after reading through your code that maybe this line following isnt helping in the way that you desire…



pre type="php"
Vector3f newLoc = results.getClosestCollision().getContactPoint(); 
/pre



I say this because i dont see where youre defining that your game scene is a collidable object, and so maybe your selected object does sink to a ‘default’ level for the collision detection, your ‘floor’ just happens to be a bit higher than that default plane (or level)… ??



If this isnt the case, just pat me on the head and tell me to keep quite LOL



Regards,



Chris

Hi Chris,



Thank for the answer, but the collision is between geometries which are Collidable elements and the real problem happens because the center of the object is positioned in its middle. If I change its center to its foot, the object stays correctly in the scene. Can’t say this is a sotution! If anyone knows already how to deal with it, plz don’t hesitate!



Cheers,

Biasutti

By the way, I tried by enhancing the scenario with physics and it did’t work either.

Uh… From what you explain its just the fact that the model center is at its waist, so when you place it at some location that center will be put to that location. You just need to place the model above the ground then (pseudo-code):

[java]Vector3f newLoc = results.getClosestCollision().getContactPoint();

BoundingBox box=(BoundingBox)selectedObject.getWorldBounds();

newLoc.y += box.getYExtent()/2.0f;

selectedObject.setLocalTranslation(newLoc);[/java]

Thank you very much normen! It worked fine, although without dividing the boundingbox’s y axis by 2.



Cheers,

Biasutti

Oh yeah, right… Its the extent already… D’oh ^^