Floating cube using physics

Oh well, after some hours of reading the forum and trying to solve this,

I just can't :slight_smile:



I'm testing the physics with a cube, that should go around the terrain, but for any reason

the cube looks like a bird feather LOL. and for worst a ghost feather that goes in the terrain !!



Here is the video of the ghosty cube !! http://www.youtube.com/watch?v=yJiIkmvG1NA



Follow the code that I'm using, most of it is merged code from the samples.



thx !!



Client.java


import com.jmex.editors.swing.settings.GameSettingsPanel;
import com.jmex.game.StandardGame;
import com.jmex.game.state.GameStateManager;
import com.jmex.physics.util.states.PhysicsGameState;

public class Client {

   public StandardGame game;

   public PhysicsGameState physicsState;

   /**
    * @param args
    */
   public static void main(String[] args) {
      try {
         new Client();
      } catch (Exception ex) {
         ex.printStackTrace();
         System.exit(1);
      }
   }

   public Client() throws Exception {
      game = new StandardGame("SIOTI");

      // Show settings screen
      if (GameSettingsPanel.prompt(game.getSettings())) {
         // Start StandardGame, it will block until it has initialized
         // successfully, then return
         game.start();
         physicsState = new SiotiPhysicsState("game state");
         GameStateManager.getInstance().attachChild(physicsState);
         physicsState.setActive(true);
      }

   }

}



SiotiPhysicsState.java


import java.util.HashMap;

import org.odejava.Odejava;

import com.jme.bounding.BoundingBox;
import com.jme.input.ChaseCamera;
import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.ThirdPersonHandler;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.input.util.SyntheticButton;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.pass.BasicPassManager;
import com.jme.renderer.pass.ShadowedRenderPass;
import com.jme.scene.Node;
import com.jme.scene.state.CullState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.util.Timer;
import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.PhysicsNode;
import com.jmex.physics.PhysicsSpace;
import com.jmex.physics.PhysicsUpdateCallback;
import com.jmex.physics.contact.ContactInfo;
import com.jmex.physics.material.Material;
import com.jmex.physics.util.states.PhysicsGameState;
import com.jmex.terrain.TerrainPage;
import com.sioti.client.CameraManager;
import com.sioti.client.LightManager;
import com.sioti.client.Player;
import com.sioti.client.TerrainManager;

public class SiotiPhysicsState extends PhysicsGameState {

   protected ChaseCamera chaser;

   private Vector3f normal = new Vector3f();

   private Camera camera;

   protected Node player;

   protected TerrainPage terrain;

   protected DynamicPhysicsNode playerNode;

   private BasicPassManager passManager;

   protected PhysicsNode terrainNode;

   boolean playerOnFloor = true;

   private DisplaySystem display;

   public InputHandler input;

   private Timer timer;

   private static ShadowedRenderPass shadowPass = new ShadowedRenderPass();

   public SiotiPhysicsState(String name) {
      super(name);
      display = DisplaySystem.getDisplaySystem();
      camera = DisplaySystem.getDisplaySystem().getRenderer().getCamera();
      //timer = Timer.getTimer();

      initGame();
   }

   private void initGame() {

      initZBufferState();
      initLight();
      initCulling();

      terrain = TerrainManager.buildSampleTerrain(display);
      terrain.setModelBound(new BoundingBox());
      terrain.updateModelBound();

      player = new Player("the player", display);
      
      // evita que o personagem fique embaixo do chao...
      // iniciando no ar ...
      // TODO identificar como colocar exatamente sobre o chao
      player.getLocalTranslation().y = terrain.getHeight(player
            .getLocalTranslation().x, player.getLocalTranslation().z) +10;

      player.setModelBound(new BoundingBox());
      player.updateModelBound();

      initInput();
      initShadows();
      initPhysics();

      chaser = CameraManager.buildChaseCamera(camera, player, 80, 200);

      getRootNode().updateGeometricState(0.0F, true);
      getRootNode().updateRenderState();

      makeKeyBindings();
   }

   private void initPhysics() {
      // make supernodes
      playerNode = getPhysicsSpace().createDynamicNode();
      terrainNode = getPhysicsSpace().createStaticNode();

      // attach
      playerNode.attachChild(player);
      terrainNode.attachChild(terrain);

      // physics stuff
      terrainNode.generatePhysicsGeometry();
      playerNode.generatePhysicsGeometry();

      // move the center of mass down to let the box land on its 'feet'
//      playerNode.setMass(1400);
//      playerNode.computeMass();
//      playerNode.setAffectedByGravity(true);

      playerNode.setCenterOfMass(new Vector3f(0f, -0.5f, 0f));
      
      Odejava.setMaxContactGeomsPerNearcallback( 50 );
      
      // playerNode.
      // this box keeps the default material

      // to move the player around we create a special material for it
      // and apply surface motion on it
      final Material playerMaterial = new Material("player material");
      
      playerNode.setMaterial(playerMaterial);
      // the actual motion is applied in the MoveAction (see below)

      // attach the player & terrain
      getRootNode().attachChild(playerNode);
      getRootNode().attachChild(terrainNode);
   }

   private void initZBufferState() {
      ZBufferState buf = display.getRenderer().createZBufferState();
      buf.setEnabled(true);
      buf.setFunction(ZBufferState.CF_LEQUAL);
      getRootNode().setRenderState(buf);
   }

   private void initLight() {
      LightManager.addDefaultLight(getRootNode(), display);
      LightManager.addFog(getRootNode(), display);
   }

   private void initCulling() {
      CullState cs = display.getRenderer().createCullState();
      cs.setCullMode(CullState.CS_BACK);
      cs.setEnabled(true);
      getRootNode().setRenderState(cs);
   }

   private void initInput() {
      HashMap handlerProps = new HashMap();
      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(player, camera, handlerProps);
      input.setActionSpeed(10f);

      KeyBindingManager.getKeyBindingManager().set("exit",
            KeyInput.KEY_ESCAPE);

   }

   private void initShadows() {
      passManager = new BasicPassManager();

      shadowPass.add(getRootNode());
      shadowPass.addOccluder(player);
      shadowPass.setRenderShadows(true);
      shadowPass.setEnabled(true);
      shadowPass.setLightingMethod(ShadowedRenderPass.MODULATIVE);
      passManager.add(shadowPass);
   }

   public void render(float arg0) {
      display.getRenderer().clearBuffers();
      display.getRenderer().draw(getRootNode());
      passManager.renderPasses(display.getRenderer());
   }

   public void update(float f) {
      chaser.update(f);
      input.update(f);

      if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit",
            false)) {
         System.exit(0);
      }

      // We don't want the chase camera to go below the world, so always keep
      // it 2 units above the level.
      if (camera.getLocation().y < (terrain.getHeight(camera.getLocation()) + 2)) {
         camera.getLocation().y = terrain.getHeight(camera.getLocation()) + 2;
         camera.update();
      }

      // inclina o personagem de acordo com o terreno
      //
      // get the normal of the terrain at our current location. We then apply
      // it to the up vector
      // of the player.
      terrain.getSurfaceNormal(player.getLocalTranslation(), normal);
      if (normal != null) {
         player.rotateUpTo(normal);
      }

      // Because we are changing the scene (moving the skybox and player) we
      // need to update
      // the graph.
      getRootNode().updateGeometricState(f, true);

   }

   private void makeKeyBindings() {

      // we map the MoveAction to the keys DELETE and PAGE DOWN for
      // forward and backward
      input.addAction(new MoveAction(new Vector3f(0f, 0f, 2f)),
            InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_UP,
            InputHandler.AXIS_NONE, false);
      input.addAction(new MoveAction(new Vector3f(0f, 0f, -2f)),
            InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_DOWN,
            InputHandler.AXIS_NONE, false);

      input.addAction(new RotateAction(new Vector3f(0, 1, 0)),
            InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_LEFT,
            InputHandler.AXIS_NONE, true);
      input.addAction(new RotateAction(new Vector3f(0, -1, 0)),
            InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_RIGHT,
            InputHandler.AXIS_NONE, true);

      input.addAction(new MoveAction(new Vector3f(2f, 0f, 0f)),
            InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_S,
            InputHandler.AXIS_NONE, false);
      input.addAction(new MoveAction(new Vector3f(2f, 0f, 0f)),
            InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_D,
            InputHandler.AXIS_NONE, false);

      // now the player should be able to jump
      // we do this by applying a single force vector when the HOME key is
      // pressed
      // this should happen only while the player is touching the floor
      // (we determine that below)
      input.addAction(new InputAction() {
         public void performAction(InputActionEvent evt) {
            if (playerOnFloor && evt.getTriggerPressed()) {
               playerNode.addForce(new Vector3f(0, 500, 0));
            }
         }
      }, InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_SPACE,
            InputHandler.AXIS_NONE, false);

      // ok finally detect when the player is touching the ground:
      // a simple way to do this is making a boolean variable
      // (playerOnFloor) which is
      // set to true on collision with the floor and to false before each
      // physics computation

      // collision events analoguous to Lesson8
      SyntheticButton playerCollisionEventHandler = playerNode
            .getCollisionEventHandler();
      input.addAction(new InputAction() {
         public void performAction(InputActionEvent evt) {
            ContactInfo contactInfo = (ContactInfo) evt.getTriggerData();
            if (contactInfo.getNode1() == terrainNode
                  || contactInfo.getNode2() == terrainNode) {
               playerOnFloor = true;
            }
         }
      }, playerCollisionEventHandler.getDeviceName(),
            playerCollisionEventHandler.getIndex(), InputHandler.AXIS_NONE,
            true);
      
      // when physics supports
      // release events

      // and a very simple callback to set the variable to false before
      // each step
      getPhysicsSpace().addToUpdateCallbacks(new PhysicsUpdateCallback() {
         public void beforeStep(PhysicsSpace space, float time) {
            playerOnFloor = false;
         }

         public void afterStep(PhysicsSpace space, float time) {

         }
      });
   }

   /**
    * Action called on key input for applying movement of the player.
    */
   private class MoveAction extends InputAction {
      /**
       * store direction this action instance will move.
       */
      private Vector3f direction;

      /**
       * @param direction
       *            direction this action instance will move
       */
      public MoveAction(Vector3f direction) {
         this.direction = direction;
      }

      public void performAction(InputActionEvent evt) {
         if (evt.getTriggerPressed()) {
            // key goes down - apply motion
            playerNode.getMaterial().setSurfaceMotion(direction);
         } else {
            // key goes up - stand still
            playerNode.getMaterial().setSurfaceMotion(Vector3f.ZERO);
            // note: for a game we usually won't want zero motion on key
            // release but be able to combine
            // keys in some way
         }
      }
   }

   private class RotateAction extends InputAction {
      public Vector3f direction;

      public RotateAction(Vector3f direction) {
         this.direction = direction;
      }

      public void performAction(InputActionEvent evt) {
         // key goes down - apply motion
         playerNode.addTorque(direction);
      }
   }

}



CameraManager.java


package com.sioti.client;

import java.util.HashMap;

import com.jme.bounding.BoundingBox;
import com.jme.input.ChaseCamera;
import com.jme.input.thirdperson.ThirdPersonMouseLook;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.scene.Node;

public class CameraManager {

   public static ChaseCamera buildChaseCamera(Camera camera, Node target,
         float minDistance, float maxDistance) {

      Vector3f targetOffset = new Vector3f();
      targetOffset.y = ((BoundingBox) target.getWorldBound()).yExtent * 1.5f;
      HashMap props = new HashMap();
      props.put(ThirdPersonMouseLook.PROP_MAXROLLOUT, "20");
      props.put(ThirdPersonMouseLook.PROP_MINROLLOUT, "10");
      props.put(ThirdPersonMouseLook.PROP_MAXASCENT, "" + 45
            * FastMath.DEG_TO_RAD);
      props.put(ThirdPersonMouseLook.PROP_MOUSEBUTTON_FOR_LOOKING,
            new Integer(2));
      
      props.put(ChaseCamera.PROP_INITIALSPHERECOORDS, new Vector3f(5, 0,
            30 * FastMath.DEG_TO_RAD));
      props.put(ChaseCamera.PROP_TARGETOFFSET, targetOffset);
      ChaseCamera chaser = new ChaseCamera(camera, target, props);
      chaser.setMaxDistance(200);
      chaser.setMinDistance(80);

      return chaser;
   }

}



LightManager


package com.sioti.client;

import com.jme.light.DirectionalLight;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.scene.state.FogState;
import com.jme.scene.state.LightState;
import com.jme.system.DisplaySystem;

public class LightManager {

   public static void addDefaultLight(Node node, DisplaySystem display) {

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

      /** Attach the light to a lightState and the lightState to rootNode. */
      LightState lightState = display.getRenderer().createLightState();
      lightState.setEnabled(true);
      lightState.setGlobalAmbient(new ColorRGBA(.2f, .2f, .2f, 1f));
      lightState.attach(light);
      node.setRenderState(lightState);
   }

   public static void addFog(Node node, DisplaySystem display) {
      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);
      node.setRenderState(fs);
   }

}



Player


package com.sioti.client;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;

import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jme.util.export.binary.BinaryImporter;
import com.jmex.model.converters.Md2ToJme;
import com.sioti.util.Log;

public class Player extends Node {

   private static final long serialVersionUID = 1L;

   public Player(String name, DisplaySystem display) {
      super(name);

       Box box = new Box("box", new Vector3f(0, 3, 0), 2f, 2f, 2f);
      //Sphere box = new Sphere("ball", 25, 25, 4f);
      box.setSolidColor(ColorRGBA.red);
      box.setModelBound(new BoundingBox());
      box.updateModelBound();

      attachChild(box);
      // attachChild(loadModel(display));
      updateWorldBound();
   }

   private Node loadModel(DisplaySystem display) {
      Md2ToJme converter = new Md2ToJme();
      ByteArrayOutputStream BO = new ByteArrayOutputStream();

      URL textu = Player.class.getClassLoader().getResource(
            "media/models/alita/alita2.jpg");
      URL freak = Player.class.getClassLoader().getResource(
            "media/models/alita/tris.md2");
      Node freakmd2 = null;

      try {
         long time = System.currentTimeMillis();
         converter.convert(freak.openStream(), BO);
         Log.debug("Time to convert from md2 to .jme:"
               + (System.currentTimeMillis() - time));
      } catch (IOException e) {
         Log.debug("damn exceptions:" + e.getMessage());
      }

      try {
         long time = System.currentTimeMillis();
         freakmd2 = (Node) BinaryImporter.getInstance().load(
               new ByteArrayInputStream(BO.toByteArray()));
         Log.debug("Time to convert from .jme to SceneGraph:"
               + (System.currentTimeMillis() - time));
      } catch (IOException e) {
         Log.warning("damn exceptions:" + e.getMessage());
      }

      TextureState ts = display.getRenderer().createTextureState();
      ts.setEnabled(true);
      ts.setTexture(TextureManager.loadTexture(textu,
            Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR));
      freakmd2.setRenderState(ts);
      freakmd2.setLocalScale(.3f);
      return freakmd2;
   }

}



TerrainManager


package com.sioti.client;

import javax.swing.ImageIcon;

import com.jme.image.Texture;
import com.jme.math.Vector3f;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jmex.terrain.TerrainPage;
import com.jmex.terrain.util.FaultFractalHeightMap;
import com.jmex.terrain.util.ProceduralTextureGenerator;

public class TerrainManager {

   public static TerrainPage buildSampleTerrain(DisplaySystem display) {

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

      page.setDetailTexture(1, 16);

      ProceduralTextureGenerator pt = new ProceduralTextureGenerator(
            heightMap);
      pt.addTexture(new ImageIcon(TerrainManager.class.getClassLoader()
            .getResource("media/texture/grassb.png")), -128, 0, 128);
      pt.addTexture(new ImageIcon(TerrainManager.class.getClassLoader()
            .getResource("media/texture/dirt.jpg")), 0, 128, 255);
      pt.addTexture(new ImageIcon(TerrainManager.class.getClassLoader()
            .getResource("media/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(TerrainManager.class
            .getClassLoader().getResource("media/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);
      page.setRenderState(ts);

      return page;
   }

}

What about modifying TestTerrain instead to get you started? (probably only add your Move- And RotateActions, which look ok to me)

You are overwriting the GameStates update method in SiotiPhysicsState.

And because of that the physics is never updated :frowning:



    public void update(float f) {
        super.update(f);   < --- let jme physics do its work
//        getRootNode().updateGeometricState(f, true);   <---  aready done in super.update();



You also need to get rid of the ThirdPersonController because it always updates the localTranslation().
Better move the Box with forces, you did set up those actions already anyway.

With those changes the Box somehow moves around on its own, but at least it doesn't fall through the terrain anymore :)

ah, I didn't spot that - good work Core-Dump

Yeha realy good work ! now the cube isnt ghost anymore !!

now I got justa rebel cube, that I apply forces to it but it just don't move …

should I apply some kind a little jump and move forward ?



it spins like hell, but don't move forward.

any ideas ?

its always good if you post an example to play with, like you did :slight_smile:



right now i am not sure why the box sticks to the terrain and moves around like it does.

i would try to remove all actions , input handlers and such and then see f it behaves correctly.



And then add single action who add force to the box.

Its not so easy to move things around by forces and torque

it's true talk about the code, with the code is pretty much easier !

hey here is the video of the rebel cube, for who is not with the code, ans want to see the behavior …

http://www.youtube.com/watch?v=RszGvHKrkbg



When you say force and torque is this what you mean  ?



force

playerNode.addForce(new Vector3f(0, 500, 0));

or this ?

playerNode.getMaterial().setSurfaceMotion(direction);



torque

playerNode.addTorque(direction);


I made some tests in the MoveAction class (inner to the SiotiPhysicsState) to use addForce instead of

setSurfaceMotion() – just because I'm not what are the differences ehheheh, but the behavior remains the same.




      public void performAction(InputActionEvent evt) {
         if (evt.getTriggerPressed()) {
            // key goes down - apply motion
            [b]playerNode.addForce(direction);[/b]
            // playerNode.getMaterial().setSurfaceMotion(direction);
         } else {
            // key goes up - stand still
            playerNode.getMaterial().setSurfaceMotion(Vector3f.ZERO);
            // note: for a game we usually won't want zero motion on key
            // release but be able to combine
            // keys in some way
         }
      }

this seems to be the problematic line, if you set a positive value, the box isn't getting pushed around wildly

playerNode.setCenterOfMass(new Vector3f(0f, -0.5f, 0f));

based on it I made some code to change the center of mass during the tests …

didn't change the behaviour

tested from 2 to -2


      // Z raise center of mass
      input.addAction(new InputAction() {
         public void performAction(InputActionEvent evt) {
            if (evt.getTriggerPressed()) {
               Vector3f centerOfMass = new Vector3f();
               centerOfMass = playerNode.getCenterOfMass(centerOfMass);
               centerOfMass.y = centerOfMass.y + 0.5f;
               playerNode.setCenterOfMass(centerOfMass);
               System.out.println("center of mass = " + centerOfMass);
            }
         }
      }, InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_Z,
            InputHandler.AXIS_NONE, false);

      // X lower center of mass
      input.addAction(new InputAction() {
         public void performAction(InputActionEvent evt) {
            if (evt.getTriggerPressed()) {
               Vector3f centerOfMass = new Vector3f();
               centerOfMass = playerNode.getCenterOfMass(centerOfMass);
               centerOfMass.y = centerOfMass.y - 0.5f;
               playerNode.setCenterOfMass(centerOfMass);
               System.out.println("center of mass = " + centerOfMass);
            }
         }
      }, InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_X,
            InputHandler.AXIS_NONE, false);



isnt it need to make little jumps and move forward to allow the cube to move forward ? even I dont understand how the physics engine work, but push a box over an irregular floor would be a pain, as it is behaving, what you think ?

it depends on the material i guess, you can push ice easily but not rubber, but i didn't play with that much yet.

instead of using physics, is there a way to stick a Node to the floor ?



in the update method I have the height of the terrain, but I really dont know how to set the Node " over" the flloor, I already tried to set player.getLocalTranslation().y based on the terrain.getHeight(),

but it seems that when I do this I have the Node half in the floor half above.



I think about to use the height of the Node to increase the height with this, is there a more 3DWay of do this ? I heard about tracing something and move the Node to the position, but I dont know how it works.



And I'm getting sick of the torque/force/mass/material stuff … :slight_smile: and I need to move on on this prototype.



cheers

"I already tried to set player.getLocalTranslation().y based on the terrain.getHeight(),

but it seems that when I do this I have the Node half in the floor half above."



Add a few units to the cube's y – the cube is half in the floor and half out because the y being used is at the cube's origin in the center instead of a y on its bottom.  Adjust the y until the cube is settled nicely on the ground. 

thx ashtonv !



But I calculated the size of the Player Node and moved to the top of the terrain position …

now worked fine :slight_smile:



[edited] here is the video : http://www.youtube.com/watch?v=uOrRReoJIKw



Here is the code that I used for reference:



NodeUtil.java


public class NodeUtil {

   public static float getHeight(Node node) {

      float top = Float.MIN_VALUE;
      float bottom = Float.MAX_VALUE;

      Vector3f[] foo = new Vector3f[3];
      Stack toInspect = new Stack();
      toInspect.push(node);

      while (!toInspect.isEmpty()) {
         Node current = (Node) toInspect.pop();
         Iterator iterator = current.getChildren().iterator();

         while (iterator.hasNext()) {
            Spatial element = (Spatial) iterator.next();

            if (element instanceof Node) {
               toInspect.push((Node) element);
            } else {
               if (element instanceof TriMesh) {
                  TriMesh mesh = (TriMesh) element;

                  for (int i = 0; i < mesh.getTriangleCount(); i++) {
                     mesh.getTriangle(i, foo);
                     Log.debug("foo[0].y=" + foo[0].y);

                     if (foo[0].y > top)
                        top = foo[0].y;
                     if (foo[1].y > top)
                        top = foo[1].y;
                     if (foo[2].y > top)
                        top = foo[2].y;

                     if (foo[0].y < bottom)
                        bottom = foo[0].y;
                     if (foo[1].y < bottom)
                        bottom = foo[1].y;
                     if (foo[2].y < bottom)
                        bottom = foo[2].y;
                  }
               }
            } // if node

         }
      }

      float height = top - bottom;
      Log.debug("height = " + height);
      return height;
   }

}