CompoundShape Jbullet

Does anyone has any idea, how to improve performance with Compoundshapes?



My problem is, that as soon as larger object compounds (190 for example) comes near another compound with a large size as well, jbullet eats enourmous amounts of processingpower, without even any collisions happening.

The only way to do it would be to create a mesh out of it, instead of a compoundshape.  The problem with compound shapes, is that you might just as well be putting EACH of those shapes SEPARATELY into the scene.  JBullet still does collision detection on each shape, and does NOTHING to actually merge the whole shape down into one single mesh.  The only benefit of a compound shape of 100+ vs 100+ individual shapes that is that JBullet can lump them all into a single broadphase sweep.  Unfortunately, once you get into nearphase, you're comparing 100+ shapes to 100+ other shapes, and there's really no way to get around an exponential growth of comparisons there.



If you create a Mesh shape, however, even though it's going through the collision detection algorithms for each triangle, it's STILL only ONE shape.



The only reason to use a compound shape instead of a trimesh, (other than that it's a wee bit easier) is if you need to be able to tell on collision, which shapes were hit.  And that's even only possible with some evil JBullet hacking.  (which, BTW, is coded NICELY into the JMEPhysics JBullet integration.  wink)



So . . . that's the long answer.  The short answer, unfortunately, is:



nope.



-Falken

Well then I seem I have a problem XD



I need to get where to collision ocur (wich does my integration as well btw) however it seems like I'm forced to use a trimesh based one. (However only Projectiles need to do this, seems like I need to add a raytrace for them and get the node they hit if a collision occured)



My question is, how do you build a triangleshape in your implemenntation?

Are there any tips you might give me?



(Btw the main reason why I'm not using your implementation is the missing support for shared shapes)

Well, what I would suggest is breaking the rigid body up into several TriMesh shapes, which will be your 'collision detection zones'.  e.g. if you are making a space game and need to know whether the ship got hit on the top, bottom, front or back, break your model up into those 4 pieces, and make a compound shape with those pieces.



Secondly, I'm questioning whether you really need collision detection out to 100+ shapes per model, or if there's some rather simple 'close enough' generalizing you could do.



The rough part is that without hardware acceleration, pretty much ANY physics engine you find is gonna start having problems doing 10,000+ nearphase comparisons.



As far as buildng a trimesh, it should be the same as for any other implementation.  Create a 'TriMesh' object and hand it the appropriate vertices.  Then create a PhysicsMesh shape and hand it the TriMesh you built.  Or, take that TriMesh, attach it to a physics node, then call 'generatePhysicsGeometry()'.  Either one should take care of it.



As far as building a mesh shape in JBullet, you can look at my JBullet implementation classes (JBulletMesh,MeshProxy) to see how to construct a JBullet BvhTriangleMeshShape.



Also . . . I'm curious what you mean by the missing support for shared shapes.  I thought I DID have that, but now I'm sure I must be misunderstanding what you're meaning.  :(  I really want to make this an implementation ppl will use, and while some of the joint support will NEVER quite be there, I really had hoped the basics were down.  Feedback is welcome.



-Falken

I'm missing a way to precache PhysicShapes, as far as I see the only way to get some in JmePhysic is the generategeometries stuff.


package Core;

import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Scanner;

import javax.vecmath.Quat4f;
import javax.vecmath.Vector3f;

import com.bulletphysics.collision.shapes.BU_Simplex1to4;
import com.bulletphysics.collision.shapes.BoxShape;
import com.bulletphysics.collision.shapes.CollisionShape;
import com.bulletphysics.collision.shapes.CompoundShape;
import com.bulletphysics.collision.shapes.ConeShape;
import com.bulletphysics.collision.shapes.CylinderShape;
import com.bulletphysics.collision.shapes.SphereShape;
import com.bulletphysics.linearmath.Transform;
import com.jme.math.Quaternion;
import com.jme.scene.Node;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Cone;
import com.jme.scene.shape.Cylinder;
import com.jme.scene.shape.Sphere;

public class PhysicsCache {
   private static HashMap<String,CollisionShape> shapecache = new HashMap<String,CollisionShape>();
   
   static public void Store(String name,CollisionShape shape){
      synchronized(shapecache){
         synchronized(shapecache){
            shapecache.put(name,shape);
         }
      }
   }
   
   static public CollisionShape getShape(String name){
      CollisionShape returnvalue = shapecache.get(name);
      if(returnvalue == null){
         System.out.println("Failed to getPhysic " + name);
         System.exit(3);
      }
      return returnvalue;
   }
   
   public static void loadPhysicsFromFile(URL file,String name){
      try {
         CompoundShape shape = new CompoundShape();
         
         Scanner scanner = new Scanner(file.openStream());
         while(scanner.hasNextLine()){
            String currentline = scanner.nextLine();
            System.out.println(currentline);
            if(currentline.substring(0,2).equals("//")){
               continue;
            }
            String[] currentsplit = currentline.split(";");
            String type = currentsplit[0];
            //depending on type read values
            Transform transform = new Transform();
            transform.setIdentity();
            if(type.equals("boxshape")){
               getPosition(transform,currentsplit);
               Vector3f size = getVector("size",currentsplit);
               getRotation(transform,currentsplit);
               BoxShape boxshape = new BoxShape(size);
               shape.addChildShape(transform,boxshape);
            }else if(type.equals("coneshape")){
               getPosition(transform,currentsplit);
               getRotation(transform,currentsplit);
               float radius = getFloatValue("radius",currentsplit);
               float height = getFloatValue("height",currentsplit);
               ConeShape coneshape = new ConeShape(radius,height);
               shape.addChildShape(transform,coneshape);
            }else if(type.equals("cylindershape")){
               getPosition(transform,currentsplit);
               Vector3f size = getVector("size",currentsplit);
               getRotation(transform,currentsplit);
               CylinderShape cylindershape = new CylinderShape(size);
               shape.addChildShape(transform,cylindershape);
            }else if(type.equals("sphereshape")){
               getPosition(transform,currentsplit);
               float radius = getFloatValue("radius",currentsplit);
               SphereShape sphereshape = new SphereShape(radius);
               shape.addChildShape(transform,sphereshape);
            }else if(type.equals("4vertex")){
               Vector3f vert1 = getVector("vert1",currentsplit);
               Vector3f vert2 = getVector("vert2",currentsplit);
               Vector3f vert3 = getVector("vert3",currentsplit);
               Vector3f vert4 = getVector("vert4",currentsplit);
               BU_Simplex1to4 vertshape = new BU_Simplex1to4(vert1,vert2,vert3,vert4);
               shape.addChildShape(transform,vertshape);
            }
         }
         Store(name,shape);
      } catch (IOException e) {
         e.printStackTrace();
      }

      
   }
   
   public static Node loadPhysicsFromFiletoNode(URL file,String name){
      Node vrep = new Node();
      try {
         Scanner scanner = new Scanner(file.openStream());
         while(scanner.hasNext()){
            String currentline = scanner.nextLine();
            if(currentline.substring(0,2).equals("//")){
               continue;
            }
            System.out.println(currentline);
            String[] currentsplit = currentline.split(";");
            String type = currentsplit[0];
            //depending on type read values
            Transform transform = new Transform();
            transform.setIdentity();
            if(type.equals("boxshape")){
               getPosition(transform,currentsplit);
               Vector3f size = getVector("size",currentsplit);
               getRotation(transform,currentsplit);
               
               Box graficbox = new Box("PhysicsBox",new com.jme.math.Vector3f(0,0,0),size.x,size.y,size.z);
               graficbox.setLocalTranslation(transform.origin.x,transform.origin.y,transform.origin.z);
               
               Quat4f rotationcache = new Quat4f();
               transform.getRotation(rotationcache);
               Quaternion rotation = new com.jme.math.Quaternion();
               rotation.w = rotationcache.w;
               rotation.x = rotationcache.x;
               rotation.y = rotationcache.y;
               rotation.z = rotationcache.z;
               graficbox.setLocalRotation(rotation);
               
               vrep.attachChild(graficbox);
            }else if(type.equals("coneshape")){
               getPosition(transform,currentsplit);
               getRotation(transform,currentsplit);
               float radius = getFloatValue("radius",currentsplit);
               float height = getFloatValue("height",currentsplit);
               Cone cone = new Cone("Physiscone",16,16,radius,height);
               Quat4f rotationcache = new Quat4f();
               transform.getRotation(rotationcache);
               Quaternion rotation = new com.jme.math.Quaternion();
               rotation.w = rotationcache.w;
               rotation.x = rotationcache.x;
               rotation.y = rotationcache.y;
               rotation.z = rotationcache.z;
               cone.setLocalRotation(rotation);
               cone.setLocalTranslation(transform.origin.x,transform.origin.y,transform.origin.z);
               vrep.attachChild(cone);
            }else if(type.equals("cylindershape")){
               getPosition(transform,currentsplit);
               Vector3f size = getVector("size",currentsplit);
               getRotation(transform,currentsplit);
               Cylinder cylinder = new Cylinder("PhysicsCylinder",16,16,size.x,size.y);
               Quat4f rotationcache = new Quat4f();
               transform.getRotation(rotationcache);
               Quaternion rotation = new com.jme.math.Quaternion();
               rotation.w = rotationcache.w;
               rotation.x = rotationcache.x;
               rotation.y = rotationcache.y;
               rotation.z = rotationcache.z;
               cylinder.setLocalRotation(rotation);
               cylinder.setLocalTranslation(transform.origin.x,transform.origin.y,transform.origin.z);
               vrep.attachChild(cylinder);
            }else if(type.equals("sphereshape")){
               getPosition(transform,currentsplit);
               float radius = getFloatValue("radius",currentsplit);
               Sphere sphere = new Sphere("PhysicsSphere",16,16,radius);
               sphere.setLocalTranslation(transform.origin.x,transform.origin.y,transform.origin.z);
               vrep.attachChild(sphere);
            }else if(type.equals("4vertex")){
               Vector3f vert1 = getVector("vert1",currentsplit);
               Vector3f vert2 = getVector("vert2",currentsplit);
               Vector3f vert3 = getVector("vert3",currentsplit);
               Vector3f vert4 = getVector("vert4",currentsplit);
               BU_Simplex1to4 vertshape = new BU_Simplex1to4(vert1,vert2,vert3,vert4);
               
            }
         }
         
      } catch (IOException e) {
         e.printStackTrace();
      }
      return vrep;
   }

   private static float getFloatValue(String name, String[] currentsplit) {
      for(String keyvalue:currentsplit){
         String[] splited = keyvalue.split("=");
         String key = splited[0];
         if(key.equals(name)){
            String value = splited[1];
            return Float.parseFloat(value);
         }
      }
      return 0;
   }

   private static void getRotation(Transform transform, String[] currentsplit) {
      for(String keyvalue:currentsplit){
         String[] splited = keyvalue.split("=");
         String key = splited[0];
         if(key.equals("rotation")){
            String value = splited[1];
            String[] angelparts = value.split(",");
            Quaternion rotation = new Quaternion();
            rotation.fromAngles(Float.parseFloat(angelparts[1]),Float.parseFloat(angelparts[2]),Float.parseFloat(angelparts[0]));
            Quat4f rotation2 = new Quat4f();
            rotation2.w = rotation.w;
            rotation2.x = rotation.x;
            rotation2.y = rotation.y;
            rotation2.z = rotation.z;
            transform.setRotation(rotation2);
         }
      }
   }

   private static Vector3f getVector(String name,String[] currentsplit) {
      Vector3f returnvalue = new Vector3f();
      for(String keyvalue:currentsplit){
         String[] splited = keyvalue.split("=");
         String key = splited[0];
         if(key.equals(name)){
            String value = splited[1];
            String[] vecparts = value.split(",");
            returnvalue.x = Float.parseFloat(vecparts[0]);
            returnvalue.y = Float.parseFloat(vecparts[1]);
            returnvalue.z = Float.parseFloat(vecparts[2]);
         }
      }
      return returnvalue;
   }

   private static void getPosition(Transform transform, String[] currentsplit) {
      for(String keyvalue:currentsplit){
         String[] splited = keyvalue.split("=");
         String key = splited[0];
         if(key.equals("position")){
            String value = splited[1];
            String[] vecparts = value.split(",");
            transform.origin.x = Float.parseFloat(vecparts[0]);
            transform.origin.y = Float.parseFloat(vecparts[1]);
            transform.origin.z = Float.parseFloat(vecparts[2]);
            System.out.println(transform.origin);
            
         }
      }
   }
}



Is my currently used precache system, the idea is the use shared shapes for every object with the same model (wich makes perfetly sense when used with geometry instancing) That way I can create dozens of physical objects with only one shape and one visiblegeometry


Back to the real problem, are there any other ways of optimizing it?
(Tried to use a Treestructur for compoundshapes, well it were to easy if it worked (I have mostly a few hotspots where are most of the primitives, the idea was to build own aabb for these hotspots, so they could be sorted out faster in the process, however obiviously jbullet don't like tree structures))

I will try to implemente the same behaviour now with trimeshes in a compound shape,(where each trimesh contains one hotspot)