Question about setLocalTranslation

Ok, I have a bit of a noob question… I am on my 4th day of playing around with jME and learning java as well for that matter lol.

You need to use a controller that will walk your character from point a to point b. Here is an example:



import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.scene.Controller;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Box;

public class WalkTest extends SimpleGame {

   private KeyBindingManager keyboard =
      KeyBindingManager.getKeyBindingManager();
   
   private Box walker;
   private Vector3f pointA = new Vector3f(-30, 0, -50);
   private Vector3f pointB = new Vector3f(30, 0, -50);
   private WalkController walk;
   
   private String cmdToggleGoal = "toggleGoal";
   
   @Override
   protected void simpleInitGame() {
      walker = new Box("walker", Vector3f.ZERO, 10, 10, 10);
      walker.setModelBound(new BoundingBox());
      walker.updateModelBound();
      walker.getLocalTranslation().set(pointA);
      rootNode.attachChild(walker);
      
      walk = new WalkController(walker);
      walk.setSpeed(20);
      walker.addController(walk);
      
      keyboard.add(cmdToggleGoal, KeyInput.KEY_SPACE);
   }
   
   private static class WalkController extends Controller {
      private static final long serialVersionUID = 1L;
      
      private Spatial controlled;
      private Vector3f desiredLocation;
      private boolean moving = false;
      private Vector3f temp = new Vector3f();
      
      public WalkController(Spatial controlled) {
         this.controlled = controlled;
      }
      
      public void setDesiredLocation(Vector3f location) {
         desiredLocation = location;
         moving = true;
      }
      
      public Vector3f getDesiredLocation() {
         return desiredLocation;
      }
      
      public void update(float time) {
         if (!moving) return;
         
         Vector3f directional = desiredLocation.subtract(
               controlled.getLocalTranslation(), temp);
         float length = directional.length();
         if (length < FastMath.ZERO_TOLERANCE) {
            moving = false;
            return;
         }
         
         float walkDistance = getSpeed()*time;
         if (walkDistance > length) walkDistance = length;
         directional.multLocal(walkDistance/length);
         
         controlled.getLocalTranslation().addLocal(directional);
      }
   }
   
   @Override
   protected void simpleUpdate() {

   }
   
   protected void updateInput() {
      super.updateInput();
      
      if (keyboard.isValidCommand(cmdToggleGoal, false)) {
         if (walk.getDesiredLocation() == pointA) {
            walk.setDesiredLocation(pointB);
         } else {
            walk.setDesiredLocation(pointA);
         }
      }
   }
   
   public static void main(String[] args) {
      WalkTest game = new WalkTest();
      game.setDialogBehaviour(
            SimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
      game.start();
   }

}

Thanks, havent had a chance to look at this yet as i'm at work, but when I get some time i'll play around with this!  I appreciate your help!  I'll post a followup and let everyone know if I am successful  XD

Alright, I've done a bit of tinkering here, and i'll show you a full overview of what I have going on.  I like how the walk controller is working, but the problem is (im guessing) where I'm telling to set its desired location to.  The reason I went with   



walk.setDesiredLocation(worldCoords2.subtractLocal(worldCoords).normalizeLocal());



is beacuse I figured this would subtract the target coord's from the current coords and move my model in that direction, but it isnt.  It is just kind of randomly moving it for a short distance than stopping.  (Like I said I dont know alot about java specifically but I am really interested in learning… so even if you dont have an exact answer for me, could you point me towards some documentation that will show me about this area?)



My desired effect is to have it so that when I click a spot in the "game" that it moves my model to that spot.


package world.area.foggy;

import javax.swing.ImageIcon;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.AbsoluteMouse;
import com.jme.input.ChaseCamera;
import com.jme.input.InputSystem;
import com.jme.input.MouseInput;
import com.jme.input.ThirdPersonHandler;
import com.jme.input.joystick.Joystick;
import com.jme.input.joystick.JoystickInput;
import com.jme.input.thirdperson.ThirdPersonJoystickPlugin;
import com.jme.light.DirectionalLight;
import com.jme.math.FastMath;
import com.jme.math.Ray;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.Controller;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.CullState;
import com.jme.scene.state.FogState;
import com.jme.scene.state.TextureState;
import com.jme.util.TextureManager;
import com.jme.util.export.binary.BinaryImporter;
import com.jmex.model.converters.FormatConverter;
import com.jmex.model.converters.ObjToJme;
import com.jmex.terrain.TerrainPage;
import com.jmex.terrain.util.FaultFractalHeightMap;
import com.jmex.terrain.util.ProceduralTextureGenerator;

public class FoggyTerrain extends SimpleGame {
    private static final Logger logger = Logger.getLogger(FoggyTerrain.class.getName());

    private Node m_character;
    private ChaseCamera chaser;
   
   // This will be my mouse
   AbsoluteMouse am;
   private TerrainPage page;
   private WalkController walk;
   private Vector3f spawnPoint = new Vector3f(-30, 0, -50);
   
  /**
   * Entry point for the test,
   *
   * @param args
   */
  public static void main(String[] args) {
      try {
          JoystickInput.setProvider(InputSystem.INPUT_SYSTEM_LWJGL);
      } catch (Exception e) {
          logger.logp(Level.SEVERE, FoggyTerrain.class.toString(),
                  "main(args)", "Exception", e);
      }
      FoggyTerrain app = new FoggyTerrain();
      app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);
      app.start();
  }

 
 
  /**
   * builds the trimesh.
   *
   * @see com.jme.app.SimpleGame#initGame()
   */
 
  protected void simpleInitGame() {
   
      display.setTitle("My Dumb Game");
      setupCharacter();
      setupTerrain();
      setupChaseCamera();
      setupInput();
      setupJoystick();    
      setupMouse();
 
  }
 

   protected void simpleUpdate() {
        chaser.update(tpf);
        float camMinHeight = page.getHeight(cam.getLocation()) + 2f;
        if (!Float.isInfinite(camMinHeight) && !Float.isNaN(camMinHeight)
                && cam.getLocation().y <= camMinHeight) {
            cam.getLocation().y = camMinHeight;
            cam.update();
        }

        float characterMinHeight = page.getHeight(m_character.getLocalTranslation())+((BoundingBox)m_character.getWorldBound()).yExtent;
        if (!Float.isInfinite(characterMinHeight) && !Float.isNaN(characterMinHeight)) {m_character.getLocalTranslation().y = characterMinHeight;
        }
      
        updateMouse();
   }


    private void setupCharacter() {
       
        // Point to a URL of my model
        URL model=FoggyTerrain.class.getClassLoader().getResource("jmetest/data/model/maggie.obj");

        // Create something to convert .obj format to .jme
        FormatConverter converter=new ObjToJme();
        // Point the converter to where it will find the .mtl file from
        converter.setProperty("mtllib",model);

        // This byte array will hold my .jme file
        ByteArrayOutputStream BO=new ByteArrayOutputStream();
        try {
            // Use the format converter to convert .obj to .jme
            converter.convert(model.openStream(), BO);
            Node Tim=(Node)BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
            // shrink this baby down some
            Tim.setLocalScale(.1f);
            m_character = new Node("char node");
            m_character.attachChild(Tim);
            m_character.setModelBound(new BoundingBox());
            m_character.updateModelBound();
            m_character.updateWorldBound(); // We do this to allow the camera setup access to the world bound in our setup code.
            m_character.getLocalTranslation().set(spawnPoint);
            rootNode.attachChild(m_character);
              walk = new WalkController(m_character);
          walk.setSpeed(20);
          m_character.addController(walk);
        } catch (IOException e) {   // Just in case anything happens
            logger.logp(Level.SEVERE, this.getClass().toString(),
                    "simpleInitGame()", "Exception", e);
            System.exit(0);
        }
    }
   
    private void setupTerrain() {
        rootNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
        fpsNode.setRenderQueueMode(Renderer.QUEUE_ORTHO);

        display.getRenderer().setBackgroundColor(
                new ColorRGBA(0.5f, 0.5f, 0.5f, 1));

        DirectionalLight dr = new DirectionalLight();
        dr.setEnabled(true);
        dr.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
        dr.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
        dr.setDirection(new Vector3f(0.5f, -0.5f, 0));

        CullState cs = display.getRenderer().createCullState();
        cs.setCullMode(CullState.CS_BACK);
        cs.setEnabled(true);
        rootNode.setRenderState(cs);

        lightState.detachAll();
        lightState.attach(dr);

        FaultFractalHeightMap heightMap = new FaultFractalHeightMap(257, 32, 0,
                255, 0.75f);
        Vector3f terrainScale = new Vector3f(10, 1, 10);
        heightMap.setHeightScale(0.001f);
        page = new TerrainPage("Terrain", 33, heightMap.getSize(),
                terrainScale, heightMap.getHeightMap(), false);

        page.setDetailTexture(1, 16);
        rootNode.attachChild(page);

        ProceduralTextureGenerator pt = new ProceduralTextureGenerator(
                heightMap);
        pt.addTexture(new ImageIcon(FoggyTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/grassb.png")), -128, 0, 128);
        pt.addTexture(new ImageIcon(FoggyTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/dirt.jpg")), 0, 128, 255);
        pt.addTexture(new ImageIcon(FoggyTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/highest.jpg")), 128, 255,
                384);

        pt.createTexture(512);

        TextureState ts = display.getRenderer().createTextureState();
        ts.setEnabled(true);
        Texture t1 = TextureManager.loadTexture(pt.getImageIcon().getImage(),
                Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR, true);
        ts.setTexture(t1, 0);

        Texture t2 = TextureManager.loadTexture(FoggyTerrain.class
                .getClassLoader()
                .getResource("jmetest/data/texture/Detail.jpg"),
                Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
        ts.setTexture(t2, 1);
        t2.setWrap(Texture.WM_WRAP_S_WRAP_T);

        t1.setApply(Texture.AM_COMBINE);
        t1.setCombineFuncRGB(Texture.ACF_MODULATE);
        t1.setCombineSrc0RGB(Texture.ACS_TEXTURE);
        t1.setCombineOp0RGB(Texture.ACO_SRC_COLOR);
        t1.setCombineSrc1RGB(Texture.ACS_PRIMARY_COLOR);
        t1.setCombineOp1RGB(Texture.ACO_SRC_COLOR);
        t1.setCombineScaleRGB(1.0f);

        t2.setApply(Texture.AM_COMBINE);
        t2.setCombineFuncRGB(Texture.ACF_ADD_SIGNED);
        t2.setCombineSrc0RGB(Texture.ACS_TEXTURE);
        t2.setCombineOp0RGB(Texture.ACO_SRC_COLOR);
        t2.setCombineSrc1RGB(Texture.ACS_PREVIOUS);
        t2.setCombineOp1RGB(Texture.ACO_SRC_COLOR);
        t2.setCombineScaleRGB(1.0f);
        rootNode.setRenderState(ts);

        FogState fs = display.getRenderer().createFogState();
        fs.setDensity(0.5f);
        fs.setEnabled(true);
        fs.setColor(new ColorRGBA(0.5f, 0.5f, 0.5f, 0.5f));
        fs.setEnd(1000);
        fs.setStart(500);
        fs.setDensityFunction(FogState.DF_LINEAR);
        fs.setApplyFunction(FogState.AF_PER_VERTEX);
        rootNode.setRenderState(fs);
    }

    private void setupChaseCamera() {
        Vector3f targetOffset = new Vector3f();
        // targetOffset.y = ((BoundingBox) m_character.getWorldBound()).yExtent * 1.5f;
        targetOffset.y = 1.5f;
        chaser = new ChaseCamera(cam, m_character);
        chaser.setTargetOffset(targetOffset);
        chaser.getMouseLook().setEnabled(false); 
    }

    private void setupInput() {
        HashMap<String, Object> handlerProps = new HashMap<String, Object>();
        handlerProps.put(ThirdPersonHandler.PROP_ROTATEONLY, "false");
        handlerProps.put(ThirdPersonHandler.PROP_DOGRADUAL, "true");
        handlerProps.put(ThirdPersonHandler.PROP_TURNSPEED, ""+(1.0f * FastMath.PI));
        handlerProps.put(ThirdPersonHandler.PROP_LOCKBACKWARDS, "false");
        handlerProps.put(ThirdPersonHandler.PROP_CAMERAALIGNEDMOVE, "true");
        input = new ThirdPersonHandler(m_character, cam, handlerProps);
        input.setActionSpeed(100f);
    }
   
    private void setupJoystick() {
        ArrayList<Joystick> joys = JoystickInput.get().findJoysticksByAxis("X Axis", "Y Axis", "Z Axis", "Z Rotation");
        Joystick joy = joys.size() >= 1 ? joys.get(0) : null;
        if (joy != null) {
            ThirdPersonJoystickPlugin plugin = new ThirdPersonJoystickPlugin(joy, joy.findAxis("X Axis"), joy.findAxis("Y Axis"), joy.findAxis("Z Axis"), joy.findAxis("Z Rotation"));
            ((ThirdPersonHandler)input).setJoystickPlugin(plugin);
            chaser.getMouseLook().setJoystickPlugin(plugin);
        }
    }

    private void setupMouse(){
        //mouse stuff
       // Create a new mouse. Restrict its movements to the display screen.
       am = new AbsoluteMouse("The Mouse", display.getWidth(), display.getHeight());
       // Get a picture for my mouse.
       TextureState mCursor = display.getRenderer().createTextureState();
        URL cursorLoc = FoggyTerrain.class.getClassLoader().getResource( "jmetest/data/cursor/cursor1.png" );
        Texture t = TextureManager.loadTexture(cursorLoc, Texture.MM_LINEAR, Texture.FM_LINEAR);
       mCursor.setTexture(t);
       am.setRenderState(mCursor);

       // Make the mouse's background blend with what's already there
       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);

       // Get the mouse input device and assign it to the AbsoluteMouse
       // Move the mouse to the middle of the screen to start with
       am.setLocalTranslation(new Vector3f(display.getWidth() / 2, display
             .getHeight() / 2, 0));
       // Assign the mouse to an input handler
        am.registerWithInputHandler( input );

       // Attach Children
       rootNode.attachChild(am); 
    }
   
    private void updateMouse() {   
       // Get the mouse input device from the jME mouse
       // Is button 0 down? Button 0 is left click
       if (MouseInput.get().isButtonDown(0)) {
          Vector2f screenPos = new Vector2f();
          // Get the position that the mouse is pointing to
          screenPos.set(am.getHotSpotPosition().x, am.getHotSpotPosition().y);
          // Get the world location of that X,Y value
          Vector3f worldCoords = display.getWorldCoordinates(screenPos, 0);
          Vector3f worldCoords2 = display.getWorldCoordinates(screenPos, 1);
          logger.info( worldCoords.toString() );
          // 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).normalizeLocal());
          // m_character.setLocalTranslation(worldCoords);
          walk.setDesiredLocation(worldCoords2.subtractLocal(worldCoords).normalizeLocal());
       }
    }
   
   
   
   private static class WalkController extends Controller {
      private static final long serialVersionUID = 1L;
      
      private Spatial controlled;
      private Vector3f desiredLocation;
      private boolean moving = false;
      private Vector3f temp = new Vector3f();
      
      public WalkController(Spatial controlled) {
         this.controlled = controlled;
      }
      
      public void setDesiredLocation(Vector3f location) {
         desiredLocation = location;
         moving = true;
      }
      
      public Vector3f getDesiredLocation() {
         return desiredLocation;
      }
      
      public void update(float time) {
         if (!moving) return;
         
         Vector3f directional = desiredLocation.subtract(
               controlled.getLocalTranslation(), temp);
         float length = directional.length();
         if (length < FastMath.ZERO_TOLERANCE) {
            moving = false;
            return;
         }
         
         float walkDistance = getSpeed()*time;
         if (walkDistance > length) walkDistance = length;
         directional.multLocal(walkDistance/length);
         
         controlled.getLocalTranslation().addLocal(directional);
      }
   }  
}

You should pass the final location you want your object to walk to, not the direction. To do that you need to obtain the desired location from the mouse click. However your updateMouse() function obtains world coordinates that lie on the plane that represents your screen, in other words: if you were holding a camera, the location you are getting is a spot on the length of the camera.

There is an example on mouse picking somewhere in jmetest folder.

This is just an issue with using the engine and is not directly related to Java… but if you want to learn Java, here is a good place to start: http://java.sun.com/docs/books/tutorial/.

lex said:

You should pass the final location you want your object to walk to, not the direction. To do that you need to obtain the desired location from the mouse click. However your updateMouse() function obtains world coordinates that lie on the plane that represents your screen, in other words: if you were holding a camera, the location you are getting is a spot on the length of the camera.
There is an example on mouse picking somewhere in jmetest folder.
This is just an issue with using the engine and is not directly related to Java... but if you want to learn Java, here is a good place to start: http://java.sun.com/docs/books/tutorial/.


Yup, One problem I realized was that I left parts of the picking out, I added it back in and am still not for sure how to i guess "convert" the pick results to be able to use it with the walk.setDesiredLocation()  ....  I do not understand how to make the pick data relative to the Vector3f that the walk.setDesiredLocation is trying to use to move the character.

    private void updateMouse() {   
      // Get the mouse input device from the jME mouse
      // Is button 0 down? Button 0 is left click
      if (MouseInput.get().isButtonDown(0)) {
         Vector2f screenPos = new Vector2f();
         // Get the position that the mouse is pointing to
         screenPos.set(am.getHotSpotPosition().x, am.getHotSpotPosition().y);
         // Get the world location of that X,Y value
         Vector3f worldCoords = display.getWorldCoordinates(screenPos, 0);
         Vector3f worldCoords2 = display.getWorldCoordinates(screenPos, 1);
            logger.info( worldCoords.toString() );
            // 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).normalizeLocal());
         // Does the mouse's ray intersect the box's world bounds?
         pr.clear();
         rootNode.findPick(mouseRay, pr);

         for (int i = 0; i < pr.getNumber(); i++) {
            pr.getPickData(i);
         }
      }
    }

This seems to work. You might want to optimize it to recycle some objects.



   private void updateMouse() {  
//      Get the mouse input device from the jME mouse
//      Is button 0 down? Button 0 is left click
      if (MouseInput.get().isButtonDown(0)) {
         Vector2f screenPos = new Vector2f();
//         Get the position that the mouse is pointing to
         screenPos.set(am.getHotSpotPosition().x, am.getHotSpotPosition().y);
//         Get the world location of that X,Y value
         Vector3f worldCoords = display.getWorldCoordinates(screenPos, 0);
         Vector3f worldCoords2 = display.getWorldCoordinates(screenPos, 1);
         logger.info( worldCoords.toString() );
         // 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).normalizeLocal());
//         Does the mouse's ray intersect the box's world bounds?
         PickResults pr = new TrianglePickResults();
         pr.setCheckDistance(true);
         rootNode.findPick(mouseRay, pr);

         if (pr.getNumber() >= 1) {
            Vector3f worldResult = mouseRay.getOrigin().add(
                  mouseRay.getDirection().mult(
                        pr.getPickData(0).getDistance()));
            walk.setDesiredLocation(worldResult);
         }
         
      }
   }