Feasibility of smooth collition response with jme?


Hey,


it seems not to be possible to implement a simple Sphere/Quad Collision Response like it is done in any First Person shooter.

I'm playing around with things (physics 2 and standard collisions) for a few hours now and i have done a lot of forum search, but there doesn't seem to exist a straight forward solution without a lot of nasty hacking.

Let me make clear what my intention is:

Take a player in a First Person Shooter for example which runs into a wall. Everyone knows what will happen: The player's direction vector will be fixed so that he will "slide" without bumps or jumps along the wall.

I have tried to figure out how that could be done with standard jME resources, but i haven't found a solution yet.

1) Standard collisions
It is no problem to obtain the objects which have collided, but it is not possible (?) to obtain a intersection point. Therefore, you can't calculate further steps which are required to manually add the response i mentioned above.

What i have tried to do is described in that PDF: http://www.peroxide.dk/papers/collision/collision.pdf
Look for the response chapter.

2) Physics 2
Okay, that one will give me the things i need in 1), but that comes at a high price:

- It is not possible to restrict the player movement like rotations etc. or to disable collision response at all
- If the player (sphere) will collide with the wall, it won't slide, it will bump along the wall
- it would be not very effective to use physics for every dynamic object in the game like bullets etc., it would be too slow when using hundreds of objects
- For networking, physics is not really the best solution

I think it would be VERY nice to implement such functionality (perhaps as a tutorial), because i think this is needed very often in games.

If you have some ideas or hints how to solve that issue, i will try to implement a few testcases and see what i could do about it in my spare time.

Thank you for ANY suggestions about that,
- Maurice

You can use this snip of code to help you along point (1). Its a little old and some of the calls may be deprecated/removed, but it might help you get over your current hurdle. Note: I believe the bug that was posted for was later resolved.



Fine, i will take a look at that and try using it in my app.

Hi!

I've been going through the tutorials and trying to wrap my head around jME (which seems to be a lovely engine, BTW  :)). Right now I'm stuck at trying to figure out collisions with walls. I want to do the sliding thing, so I'm trying to do something similar to what xaoC was doing. Unfortunately the code that nymon links to isn't working anymore. The line


Triangle[] triangles = mesh.getMeshAsTriangles(triData.getTargetBatchId(), null);



in the getCollisionNormal() method of the code is what's causing the problem. Is there a new way of getting the colliding triangles from the target mesh?

Please, is there no way to get the triangles involved in a collision? I can't find any documentation or examples of this.

aznan said:

Please, is there no way to get the triangles involved in a collision? I can't find any documentation or examples of this.

try this

read that  :)

The code snippet is jME1 code, as nymon mentioned some of the calls are different.

Your problem is just that geom batches have been removed so you just have to take that argument out.



import java.util.ArrayList;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.intersection.BoundingCollisionResults;
import com.jme.intersection.CollisionData;
import com.jme.intersection.TriangleCollisionResults;
import com.jme.math.Triangle;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.scene.TriMesh;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.MaterialState;

public class CollisionTest extends SimpleGame {

   private Sphere character;
   private Node collisionGeoms;
   private BoundingCollisionResults boundCollision;
   private TriangleCollisionResults triCollision;
   private Vector3f prevLoc;
   private Vector3f temp;
   private Vector3f delta;

   /**
   * Entry point for the test,
   *
   * @param args
   */
   public static void main(String[] args) {

      CollisionTest app = new CollisionTest();
      app.setConfigShowMode(ConfigShowMode.AlwaysShow);
      app.start();
   }

   public CollisionTest() {

      super();
   }

   @Override
   protected void simpleInitGame() {

      collisionGeoms = new Node("collisionGeoms");
      rootNode.attachChild(collisionGeoms);

      prevLoc = new Vector3f();
      temp = new Vector3f();
      delta = new Vector3f();

      boundCollision = new BoundingCollisionResults();
      triCollision = new TriangleCollisionResults();

      initBoxes();

      KeyBindingManager.getKeyBindingManager().set("left", KeyInput.KEY_COMMA);
      KeyBindingManager.getKeyBindingManager().set("right", KeyInput.KEY_PERIOD);
      KeyBindingManager.getKeyBindingManager().set("up", KeyInput.KEY_I);
      KeyBindingManager.getKeyBindingManager().set("down", KeyInput.KEY_K);
      KeyBindingManager.getKeyBindingManager().set("reset", KeyInput.KEY_SPACE);
   }

   private void initBoxes() {

      character = new Sphere("character", 10, 10, 1f);
      character.setModelBound(new BoundingBox());
      character.setLocalTranslation(new Vector3f(0, 0, -10));
      MaterialState ms = display.getRenderer().createMaterialState();
      ms.setAmbient(ColorRGBA.blue);
      character.setRenderState(ms);
      character.setIsCollidable(true);
      character.updateRenderState();
      character.updateModelBound();
      character.updateGeometricState(0.0f, true);

      Box coll1 = new Box("coll1", new Vector3f(), 5, 5, 1);
      coll1.setModelBound(new BoundingBox());
      coll1.setLocalTranslation(new Vector3f(-6, 5, -10));
      //coll1.getLocalRotation().fromAngleAxis(-FastMath.PI / 4, new Vector3f(0, 1, 0));
      ms = display.getRenderer().createMaterialState();
      ms.setAmbient(ColorRGBA.red);
      coll1.setRenderState(ms);
      coll1.setIsCollidable(true);
      coll1.updateRenderState();
      coll1.updateModelBound();
      coll1.updateGeometricState(0.0f, true);

      Box coll2 = new Box("coll2", new Vector3f(), 2, 5, 1);
      coll2.setModelBound(new BoundingBox());
      coll2.setLocalTranslation(new Vector3f(-6, 0, -10));
      //coll2.getLocalRotation().fromAngleAxis(FastMath.PI / 2, new Vector3f(0, 0, 1));
      ms = display.getRenderer().createMaterialState();
      ms.setAmbient(ColorRGBA.yellow);
      coll2.setRenderState(ms);
      coll2.setIsCollidable(true);
      coll2.updateRenderState();
      coll2.updateModelBound();
      coll2.updateGeometricState(0.0f, true);

      rootNode.attachChild(character);
      rootNode.updateGeometricState(0.0f, true);

      collisionGeoms.attachChild(coll1);
      collisionGeoms.attachChild(coll2);
      collisionGeoms.updateGeometricState(0.0f, true);
   }

   protected void simpleUpdate() {

      prevLoc.set(character.getLocalTranslation());

      if(KeyBindingManager.getKeyBindingManager().isValidCommand("left", true)) {
         character.getLocalTranslation().addLocal(-0.01f, 0, 0);
      }
      if(KeyBindingManager.getKeyBindingManager().isValidCommand("right", true)) {
         character.getLocalTranslation().addLocal(0.01f, 0, 0);
      }
      if(KeyBindingManager.getKeyBindingManager().isValidCommand("up", true)) {
         character.getLocalTranslation().addLocal(0, 0.01f, 0);
      }
      if(KeyBindingManager.getKeyBindingManager().isValidCommand("down", true)) {
         character.getLocalTranslation().addLocal(0, -0.01f, 0);
      }
      if(KeyBindingManager.getKeyBindingManager().isValidCommand("reset", false)) {
         character.getLocalTranslation().zero();
      }

      delta.set(character.getLocalTranslation().subtract(prevLoc));

      checkForCollision();
   }

   private void checkForCollision() {

      boundCollision.clear();
      character.calculateCollisions(collisionGeoms, boundCollision);

      // check for bounding collision
      if(boundCollision.getNumber() > 0) {

         triCollision.clear();
         character.calculateCollisions(collisionGeoms, triCollision);

         // check for actual triangle collision
         if(triCollision.getNumber() > 0) {

            temp.zero();

            for(int index = 0; index < triCollision.getNumber(); index++)
               temp.addLocal(getCollisionNormal(triCollision.getCollisionData(index)));

            temp.normalizeLocal();
            temp.multLocal(delta.length());
            temp.addLocal(delta);

            character.setLocalTranslation(prevLoc.add(temp));
         }
      }
   }

   private Vector3f getCollisionNormal(CollisionData data) {

      Vector3f normal = new Vector3f();

      if(data.getTargetTris().size() > 0) {

         for(int colIndex = 0; colIndex < triCollision.getNumber(); colIndex++) {

            CollisionData triData = triCollision.getCollisionData(colIndex);
            TriMesh mesh = (TriMesh) triData.getTargetMesh();
            Triangle[] triangles = mesh.getMeshAsTriangles(null);
            ArrayList<Integer> triIndex = triData.getTargetTris();

            for(Integer i : triIndex) {

               Triangle t = triangles[i];
               t.calculateNormal();
               normal.addLocal(triData.getTargetMesh().getLocalRotation().mult(t.getNormal()));
            }
         }
      }
      
      if((normal.x != 0) || (normal.y != 0) || (normal.z != 0))
         System.out.println("Collision Surface Normal: " + normal.normalize());
      
      return normal.normalizeLocal();
   }
}

mcbeth - I tried to get the simple physics thing before, but the link in the original post gets a bandwidth limit exceeded error (apparently it's very popular).



Thanks alot, both of you!

I tried the example code that was provided, and it seemed to work great for the rectangular walls in my game, but not so great for irregularly shaped objects (I was able to slowly move through them). Any thoughts about how to get around this? Would it be possible to do collision detection on the bounding boxes instead of the actual triangles?


Alric said:

The code snippet is jME1 code, as nymon mentioned some of the calls are different.
Your problem is just that geom batches have been removed so you just have to take that argument out.


import java.util.ArrayList;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.intersection.BoundingCollisionResults;
import com.jme.intersection.CollisionData;
import com.jme.intersection.TriangleCollisionResults;
import com.jme.math.Triangle;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.scene.TriMesh;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.MaterialState;

public class CollisionTest extends SimpleGame {

   private Sphere character;
   private Node collisionGeoms;
   private BoundingCollisionResults boundCollision;
   private TriangleCollisionResults triCollision;
   private Vector3f prevLoc;
   private Vector3f temp;
   private Vector3f delta;

   /**
   * Entry point for the test,
   *
   * @param args
   */
   public static void main(String[] args) {

      CollisionTest app = new CollisionTest();
      app.setConfigShowMode(ConfigShowMode.AlwaysShow);
      app.start();
   }

   public CollisionTest() {

      super();
   }

   @Override
   protected void simpleInitGame() {

      collisionGeoms = new Node("collisionGeoms");
      rootNode.attachChild(collisionGeoms);

      prevLoc = new Vector3f();
      temp = new Vector3f();
      delta = new Vector3f();

      boundCollision = new BoundingCollisionResults();
      triCollision = new TriangleCollisionResults();

      initBoxes();

      KeyBindingManager.getKeyBindingManager().set("left", KeyInput.KEY_COMMA);
      KeyBindingManager.getKeyBindingManager().set("right", KeyInput.KEY_PERIOD);
      KeyBindingManager.getKeyBindingManager().set("up", KeyInput.KEY_I);
      KeyBindingManager.getKeyBindingManager().set("down", KeyInput.KEY_K);
      KeyBindingManager.getKeyBindingManager().set("reset", KeyInput.KEY_SPACE);
   }

   private void initBoxes() {

      character = new Sphere("character", 10, 10, 1f);
      character.setModelBound(new BoundingBox());
      character.setLocalTranslation(new Vector3f(0, 0, -10));
      MaterialState ms = display.getRenderer().createMaterialState();
      ms.setAmbient(ColorRGBA.blue);
      character.setRenderState(ms);
      character.setIsCollidable(true);
      character.updateRenderState();
      character.updateModelBound();
      character.updateGeometricState(0.0f, true);

      Box coll1 = new Box("coll1", new Vector3f(), 5, 5, 1);
      coll1.setModelBound(new BoundingBox());
      coll1.setLocalTranslation(new Vector3f(-6, 5, -10));
      //coll1.getLocalRotation().fromAngleAxis(-FastMath.PI / 4, new Vector3f(0, 1, 0));
      ms = display.getRenderer().createMaterialState();
      ms.setAmbient(ColorRGBA.red);
      coll1.setRenderState(ms);
      coll1.setIsCollidable(true);
      coll1.updateRenderState();
      coll1.updateModelBound();
      coll1.updateGeometricState(0.0f, true);

      Box coll2 = new Box("coll2", new Vector3f(), 2, 5, 1);
      coll2.setModelBound(new BoundingBox());
      coll2.setLocalTranslation(new Vector3f(-6, 0, -10));
      //coll2.getLocalRotation().fromAngleAxis(FastMath.PI / 2, new Vector3f(0, 0, 1));
      ms = display.getRenderer().createMaterialState();
      ms.setAmbient(ColorRGBA.yellow);
      coll2.setRenderState(ms);
      coll2.setIsCollidable(true);
      coll2.updateRenderState();
      coll2.updateModelBound();
      coll2.updateGeometricState(0.0f, true);

      rootNode.attachChild(character);
      rootNode.updateGeometricState(0.0f, true);

      collisionGeoms.attachChild(coll1);
      collisionGeoms.attachChild(coll2);
      collisionGeoms.updateGeometricState(0.0f, true);
   }

   protected void simpleUpdate() {

      prevLoc.set(character.getLocalTranslation());

      if(KeyBindingManager.getKeyBindingManager().isValidCommand("left", true)) {
         character.getLocalTranslation().addLocal(-0.01f, 0, 0);
      }
      if(KeyBindingManager.getKeyBindingManager().isValidCommand("right", true)) {
         character.getLocalTranslation().addLocal(0.01f, 0, 0);
      }
      if(KeyBindingManager.getKeyBindingManager().isValidCommand("up", true)) {
         character.getLocalTranslation().addLocal(0, 0.01f, 0);
      }
      if(KeyBindingManager.getKeyBindingManager().isValidCommand("down", true)) {
         character.getLocalTranslation().addLocal(0, -0.01f, 0);
      }
      if(KeyBindingManager.getKeyBindingManager().isValidCommand("reset", false)) {
         character.getLocalTranslation().zero();
      }

      delta.set(character.getLocalTranslation().subtract(prevLoc));

      checkForCollision();
   }

   private void checkForCollision() {

      boundCollision.clear();
      character.calculateCollisions(collisionGeoms, boundCollision);

      // check for bounding collision
      if(boundCollision.getNumber() > 0) {

         triCollision.clear();
         character.calculateCollisions(collisionGeoms, triCollision);

         // check for actual triangle collision
         if(triCollision.getNumber() > 0) {

            temp.zero();

            for(int index = 0; index < triCollision.getNumber(); index++)
               temp.addLocal(getCollisionNormal(triCollision.getCollisionData(index)));

            temp.normalizeLocal();
            temp.multLocal(delta.length());
            temp.addLocal(delta);

            character.setLocalTranslation(prevLoc.add(temp));
         }
      }
   }

   private Vector3f getCollisionNormal(CollisionData data) {

      Vector3f normal = new Vector3f();

      if(data.getTargetTris().size() > 0) {

         for(int colIndex = 0; colIndex < triCollision.getNumber(); colIndex++) {

            CollisionData triData = triCollision.getCollisionData(colIndex);
            TriMesh mesh = (TriMesh) triData.getTargetMesh();
            Triangle[] triangles = mesh.getMeshAsTriangles(null);
            ArrayList<Integer> triIndex = triData.getTargetTris();

            for(Integer i : triIndex) {

               Triangle t = triangles[i];
               t.calculateNormal();
               normal.addLocal(triData.getTargetMesh().getLocalRotation().mult(t.getNormal()));
            }
         }
      }
      
      if((normal.x != 0) || (normal.y != 0) || (normal.z != 0))
         System.out.println("Collision Surface Normal: " + normal.normalize());
      
      return normal.normalizeLocal();
   }
}