Generating physics for bumpy terrain?

Hi , i have just started out with jme physics and am trying to make a simple game where you roll a ball over bumpy terrain (much like the tutorials). I have made a model terrain in blender and imported it fine however the genratePhysicsGeometry() method simply creates a large sphere around the terrain which the ball rolls on. Is there a way to do it so the physics geometry is exactly the same shape as the terrain, allowing the ball to roll down hills etc?



this is what i have so far (A jumble of different tutorial classes!!)


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

import com.jme.app.AbstractGame;
import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingSphere;
import com.jme.input.FirstPersonHandler;
import com.jme.input.InputHandler;
import com.jme.input.KeyInput;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.TriMesh;
import com.jme.scene.shape.Sphere;
import com.jme.util.export.binary.BinaryImporter;
import com.jmex.model.converters.ObjToJme;
import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.PhysicsSpace;
import com.jmex.physics.PhysicsUpdateCallback;
import com.jmex.physics.StaticPhysicsNode;
import com.jmex.physics.util.SimplePhysicsGame;

public class TestObjJmeWrite extends SimplePhysicsGame {

   TriMesh model;
    private DynamicPhysicsNode dynamicNode;
    private InputHandler physicsStepInputHandler;
   
   public static void main(String[] args) {
      TestObjJmeWrite app = new TestObjJmeWrite();
      app
            .setDialogBehaviour(AbstractGame.FIRSTRUN_OR_NOCONFIGFILE_SHOW_PROPS_DIALOG);
      app.start();
   }

   protected void simpleInitGame() {

      
      ObjToJme converter = new ObjToJme();
      try {
         URL objFile = TestObjJmeWrite.class.getClassLoader().getResource(
               "world.obj");
         converter.setProperty("mtllib", objFile);
                        converter.setProperty("texdir",objFile);
         ByteArrayOutputStream BO = new ByteArrayOutputStream();
         System.out.println("Starting to convert .obj to .jme");
         converter.convert(objFile.openStream(), BO);
                        //load as a TriMesh if single object
         model = (TriMesh) BinaryImporter.getInstance().load(
               new ByteArrayInputStream(BO.toByteArray()));
                        //load as a node if multiple objects
                        //Node model=(Node)BinaryImporter.getInstance().load(
                        //                new ByteArrayInputStream(BO.toByteArray()));
         model.setModelBound(new BoundingSphere());
         model.updateModelBound();

      } catch (IOException e) {
         e.printStackTrace();
      }
      
      StaticPhysicsNode staticNode = getPhysicsSpace().createStaticNode();
        rootNode.attachChild( staticNode );
        model.getLocalScale().set( 20, 10f, 20 );
        staticNode.attachChild(model);
        staticNode.generatePhysicsGeometry();
       
        dynamicNode = getPhysicsSpace().createDynamicNode();
        rootNode.attachChild( dynamicNode );
        Sphere bSphere = new Sphere("ball sphere", new Vector3f(), 20, 20, 3f);
        dynamicNode.attachChild(bSphere);
        dynamicNode.generatePhysicsGeometry();
        dynamicNode.getLocalTranslation().set(0, 50, 0);
       
     // we want to take in account now what was already mentioned in Lesson3:
        // forces must be applied for each physics step if you want a constant force applied
        // thus we create an input handler that gets invoked each physics step
        physicsStepInputHandler = new InputHandler();
        getPhysicsSpace().addToUpdateCallbacks( new PhysicsUpdateCallback() {
            public void beforeStep( PhysicsSpace space, float time ) {
                physicsStepInputHandler.update( time );
            }
            public void afterStep( PhysicsSpace space, float time ) {

            }
        } );

        physicsStepInputHandler.addAction( new MyInputAction( new Vector3f( 70, 0, 0 ) ),
                InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_L, InputHandler.AXIS_NONE, true );

        physicsStepInputHandler.addAction( new MyInputAction( new Vector3f( -70, 0, 0 ) ),
                InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_J, InputHandler.AXIS_NONE, true );

        physicsStepInputHandler.addAction( new MyInputAction( new Vector3f( 0, 0, -70 ) ),
                InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_K, InputHandler.AXIS_NONE, true );

        physicsStepInputHandler.addAction( new MyInputAction( new Vector3f( 0, 0, 70 ) ),
                InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_I, InputHandler.AXIS_NONE, true );
       
        showPhysics = true;
      
      display.getRenderer().setBackgroundColor(ColorRGBA.gray);

      //needed for specular lighting(reflective light)
      lightState.setSeparateSpecular(true);
   
   }
   
    private class MyInputAction extends InputAction {
           private final Vector3f direction;
           private final Vector3f appliedForce = new Vector3f();

           /**
            * The action get the node it should move and the direction it should move in.
            *
            * @param direction force that should be applied on each invocation of the action
            */
           public MyInputAction( Vector3f direction ) {
               // simply remember in member variables
               this.direction = direction;
           }

           /**
            * This method gets invoked upon key event
            *
            * @param evt more data about the event (we don't need it)
            */
           public void performAction( InputActionEvent evt ) {
               appliedForce.set( direction ).multLocal( evt.getTime() );
               // the really important line: apply a force to the moved node
               dynamicNode.addForce( appliedForce );
           }
       }
   
    protected void simpleUpdate() {
         // as the user can steer the sphere only in one direction it will fall off the floor after a short time
         // we want to put it back up then
         if ( dynamicNode.getWorldTranslation().y < -20 ) {
             // ok it has definately fallen off the floor
             // clear speed and forces
             dynamicNode.clearDynamics();
             // then put it over the floor again
             dynamicNode.getLocalTranslation().set( 0, 5, 0 );
         }
     }
}



thanks for your help!

generatePhysicsGeometry( true );

  This generates 'trimesh accurate' geometries, although there may be a significant perfomance hit...

Any reason you can't use TerrainBlocks or TerrainPages like the testGenerateTerrain (under physics) does?

Hmmm i tried this code but when the ball meets the terrain the program crashes with this error:


#
# An unexpected error has been detected by Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x064498c4, pid=952, tid=1128
#
# Java VM: Java HotSpot(TM) Client VM (11.0-b16 mixed mode, sharing windows-x86)
# Problematic frame:
# C  [odejava.dll+0x498c4]
#
# An error report file with more information is saved as:
# C:UsersEmudDocumentsWorkspaceFulstromPhysicshs_err_pid952.log
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

[error occurred during error reporting , id 0xc0000005]



Haven't looked at that testgenerateterrain class, i assumed loading models was the easiest way, i'll look into it!

Argh i've hit the same problem when using terrain blocks, the second the ball lands on the terrain it crashes!

I've found a topic which had similar problems but the solution doesn't solve it for me…hmmm

Problem solved (sort of)

by using a 32x32 heightmap the crashes no longer occur and the ball is rolling!

however 32x32 don't look so good! There must be a better way to do this?

Try lowering your ball mesh count (try 8, 8 rather than 20, 20)…

basixs said:

Try lowering your ball mesh count (try 8, 8 rather than 20, 20)...

That shouldn't change anything, because the sphere is being created as a primitive physics collision geometry, not a trimesh collision geometry.

Just keep in mind trimesh collision detection is very expensive, and you should break complex terrains and object into smaller pieces, so the physics engine can handle them faster.

Hi !



I had the same problem:



HeightMap, heights etc generated are only vertices, ODE needs 3D blocks to compute volumes (with windows (it performs well under linux))… so you need to use generatephysics(true) and tweak a bit jme-physics to create a bounding volume per face. Please check generatePhysics() and set a default bounding volume if none found.



:slight_smile:

this is how TestGenerateTerrain does it:



        final StaticPhysicsNode staticNode = getPhysicsSpace().createStaticNode();

        Spatial terrain = createTerrain();
        staticNode.attachChild( terrain );

        staticNode.getLocalTranslation().set( 0, -150, 0 );

        rootNode.attachChild( staticNode );
        staticNode.generatePhysicsGeometry();


terrain = TerrainPage