Difference between revisions of "Physics"

From JPCT
Jump to: navigation, search
 
(6 intermediate revisions by one other user not shown)
Line 15: Line 15:
  
 
'''Things to know:'''
 
'''Things to know:'''
 +
 
''Vecmath.jar''. jBullet uses java3D vecmath library. You will need this. Fortunetly it is packed with the jBullet download.
 
''Vecmath.jar''. jBullet uses java3D vecmath library. You will need this. Fortunetly it is packed with the jBullet download.
  
 
''Scaling'' in jBullet. unit scale by default is a 1:1 meter. Though feel free to scale it as 1 Unit as needed. For safety issues don't create objects less than 0.01. Really big objects also tend to start causing simulation problems if landing on top of smaller objects. IIRC up to 10 is safe.
 
''Scaling'' in jBullet. unit scale by default is a 1:1 meter. Though feel free to scale it as 1 Unit as needed. For safety issues don't create objects less than 0.01. Really big objects also tend to start causing simulation problems if landing on top of smaller objects. IIRC up to 10 is safe.
  
jBullet uses a YZ axis and coordinates that are more in line with OpenGL. This is important as JPCT has opposing YZ.
+
''Axis'' jBullet uses a YZ axis and coordinates that are more in line with OpenGL. This is important as JPCT has opposing YZ.
  
 
''Linear'' or the term used in Bullet. This represents XYZ movement. So a Constraint that limits the Linear will prevent movement.
 
''Linear'' or the term used in Bullet. This represents XYZ movement. So a Constraint that limits the Linear will prevent movement.
Line 175: Line 176:
  
 
'''
 
'''
 +
 
== Test Code ==
 
== Test Code ==
 
'''
 
'''
Line 180: Line 182:
 
First the basic demo code. This demo code is super basic and was written to run on a AMD 2.6mhz dual core running the software renderer. This runs 400 boxes decently. Feel free to implement the hardware renderer and modify some values to see how many boxes you can get going. If your running on less it will run slower. If it's a big different drop the numBoxes value near the top.  
 
First the basic demo code. This demo code is super basic and was written to run on a AMD 2.6mhz dual core running the software renderer. This runs 400 boxes decently. Feel free to implement the hardware renderer and modify some values to see how many boxes you can get going. If your running on less it will run slower. If it's a big different drop the numBoxes value near the top.  
 
<pre>
 
<pre>
 
 
package jpctbullet;
 
package jpctbullet;
  
Line 228: Line 229:
 
private Object3D box; // only something for the camera to focus on.
 
private Object3D box; // only something for the camera to focus on.
 
private JFrame frame;
 
private JFrame frame;
    
+
   private JPCTDebugDraw iDebugDraw;
 +
 
   public DiscreteDynamicsWorld dynamicWorld;
 
   public DiscreteDynamicsWorld dynamicWorld;
 
   public int maxSubSteps;
 
   public int maxSubSteps;
Line 239: Line 241:
 
private DefaultCollisionConfiguration collisionConfiguration;
 
private DefaultCollisionConfiguration collisionConfiguration;
 
    
 
    
 +
 
   private Vector<RigidBody> boxList;
 
   private Vector<RigidBody> boxList;
 
 
Line 292: Line 295:
 
Vector3f worldAabbMax = new Vector3f(10000,10000,10000);
 
Vector3f worldAabbMax = new Vector3f(10000,10000,10000);
 
AxisSweep3 overlappingPairCache = new AxisSweep3(worldAabbMin, worldAabbMax);
 
AxisSweep3 overlappingPairCache = new AxisSweep3(worldAabbMin, worldAabbMax);
//SimpleBroadphase overlappingPairCache = new SimpleBroadphase(1026);
 
 
SequentialImpulseConstraintSolver solver = new SequentialImpulseConstraintSolver();
 
SequentialImpulseConstraintSolver solver = new SequentialImpulseConstraintSolver();
 
 
Line 321: Line 323:
 
//buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
 
//buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
 
//frame.add(canvas);
 
//frame.add(canvas);
 +
 +
iDebugDraw = new JPCTDebugDraw(world, buffer);
 +
iDebugDraw.setDebugMode(DebugDrawModes.DRAW_WIREFRAME);
 +
dynamicWorld.setDebugDrawer(iDebugDraw);
 
 
 
while (frame.isShowing()) {    
 
while (frame.isShowing()) {    
Line 331: Line 337:
 
world.renderScene(buffer);
 
world.renderScene(buffer);
 
world.draw(buffer);
 
world.draw(buffer);
buffer.update();
+
 +
  buffer.update();
 +
 +
  dynamicWorld.debugDrawWorld();
 +
 
//buffer.displayGLOnly();
 
//buffer.displayGLOnly();
 
//canvas.repaint();
 
//canvas.repaint();
Line 349: Line 359:
 
  BoxShape shape = new BoxShape(new Vector3f(2,2,2));
 
  BoxShape shape = new BoxShape(new Vector3f(2,2,2));
 
  JPCTBulletMotionState ms;
 
  JPCTBulletMotionState ms;
  float mass = 2;
+
  float mass = 5;
  //MotionState ms;
+
 
  Vector3f localInertia = new Vector3f(0,0,0);
 
  Vector3f localInertia = new Vector3f(0,0,0);
 
  shape.calculateLocalInertia(mass, localInertia);
 
  shape.calculateLocalInertia(mass, localInertia);
Line 360: Line 369:
 
    boxgfx.setTexture("box");
 
    boxgfx.setTexture("box");
 
    boxgfx.setEnvmapped(Object3D.ENVMAP_ENABLED);
 
    boxgfx.setEnvmapped(Object3D.ENVMAP_ENABLED);
 +
    boxgfx.translate(0, (strHeight+(i * 4)) * -1,(float) 50 - i/3);
 
    boxgfx.build();
 
    boxgfx.build();
 +
    boxgfx.rotateY((float) Math.PI/4f);
 +
    boxgfx.rotateMesh();
 +
    boxgfx.getRotationMatrix().setIdentity();
 
    world.addObject(boxgfx);
 
    world.addObject(boxgfx);
 
     
 
     
 
    transform = new Transform();
 
    transform = new Transform();
 
    transform.setIdentity();
 
    transform.setIdentity();
    //transform.origin.set(0, strHeight+(i * 4),(float) (-(numBoxes / 3.3)) + i/2);
 
    transform.origin.set(0, strHeight+(i * 4),(float) -50 + i/3);
 
 
     
 
     
    //ms = new DefaultMotionState(transform);
+
    ms = new JPCTBulletMotionState(boxgfx);
    ms = new JPCTBulletMotionState(boxgfx, transform);
+
 
     
 
     
 
    rbInfo = new RigidBodyConstructionInfo(mass, ms, shape, localInertia);
 
    rbInfo = new RigidBodyConstructionInfo(mass, ms, shape, localInertia);
Line 384: Line 394:
  
 
}
 
}
 
 
}
 
}
 +
 
</pre>
 
</pre>
  
  
  
Heres is the magic code. I must warn that this code is still in work of progress. There is a problem with the rotation. You will see the problem in execution. Future implementation will include support for seamless kinematic state integration. ie update your Object3D to see the effect done to the physics side. If anyone knows how to fix the rotation problem please post the code on the forums or message me. I will update this page with future fixes.
+
Heres is the magic code. I must warn that this code is still in work of progress. The current version of JPCTMotionState supports Kinematic RigidBody's and RigidBody placement automatically by Graphical Object3D.
<pre>
+
Pending centerOfMass / Pivot rotation.
 
+
<pre>package jpctbullet;
package jpctbullet;
+
  
 
import javax.vecmath.Vector3f;
 
import javax.vecmath.Vector3f;
import javax.vecmath.Quat4f;
 
 
import javax.vecmath.Matrix3f;
 
import javax.vecmath.Matrix3f;
 
import javax.vecmath.Matrix4f;
 
import javax.vecmath.Matrix4f;
 
  
 
import com.threed.jpct.SimpleVector;
 
import com.threed.jpct.SimpleVector;
Line 407: Line 414:
 
import com.bulletphysics.linearmath.MotionState;
 
import com.bulletphysics.linearmath.MotionState;
 
import com.bulletphysics.linearmath.Transform;
 
import com.bulletphysics.linearmath.Transform;
 +
import com.bulletphysics.linearmath.MatrixUtil;
  
  
 
public class JPCTBulletMotionState implements MotionState{
 
public class JPCTBulletMotionState implements MotionState{
public final Transform graphicsWorldTrans = new Transform();
 
 
public final Transform centerOfMassOffset = new Transform();
 
public final Transform centerOfMassOffset = new Transform();
public final Transform startWorldTrans = new Transform();
 
 
 
   private Object3D obj3d;
 
   private Object3D obj3d;
 
    
 
    
Line 419: Line 424:
 
   {
 
   {
 
     obj3d = obj;
 
     obj3d = obj;
graphicsWorldTrans.setIdentity();
+
    centerOfMassOffset.setIdentity();
centerOfMassOffset.setIdentity();
+
startWorldTrans.setIdentity();
+
 
   }
 
   }
 
    
 
    
Line 427: Line 430:
 
   {
 
   {
 
     obj3d = obj;
 
     obj3d = obj;
     this.graphicsWorldTrans.set(startTrans);
+
     setGraphicFromTransform(startTrans);
 
centerOfMassOffset.setIdentity();
 
centerOfMassOffset.setIdentity();
this.startWorldTrans.set(startTrans);
 
 
}   
 
}   
 
    
 
    
Line 435: Line 437:
 
   {
 
   {
 
     obj3d = obj;
 
     obj3d = obj;
     this.graphicsWorldTrans.set(startTrans);
+
     setGraphicFromTransform(startTrans);
 
this.centerOfMassOffset.set(centerOfMassOffset);
 
this.centerOfMassOffset.set(centerOfMassOffset);
this.startWorldTrans.set(startTrans);
 
 
}   
 
}   
 
+
  //public void getWorldTransform(Transform worldTrans){
+
 
   public Transform getWorldTransform(Transform worldTrans){
 
   public Transform getWorldTransform(Transform worldTrans){
//worldTrans.set(graphicsWorldTrans);
+
     setTransformFromGraphic(worldTrans);
     //worldTrans.inverse(centerOfMassOffset);
+
    //worldTrans.set(centerOfMassOffset);
+
//worldTrans.mul(graphicsWorldTrans);
+
 
+
worldTrans.inverse(centerOfMassOffset);
+
worldTrans.mul(graphicsWorldTrans);
+
+
//Matrix4f matrix4 = new Matrix4f(obj3d.getRotationMatrix().getDump());
+
//matrix4.rotX((float)Math.PI);
+
//matrix4.getRotationScale(worldTrans.basis);// want to place matrix in argument
+
 
     return worldTrans;
 
     return worldTrans;
 
   }
 
   }
 
    
 
    
 
   public void setWorldTransform(Transform worldTrans)
 
   public void setWorldTransform(Transform worldTrans)
 +
  {
 +
    setGraphicFromTransform(worldTrans);
 +
  }
 +
 
 +
 
 +
  private void setTransformFromGraphic(Transform tran)
 +
  {
 +
  SimpleVector p = obj3d.getTransformedCenter();
 +
tran.origin.set(p.x, -p.y, -p.z); // not sure if translation or position
 +
 +
Matrix matrixGfx = obj3d.getRotationMatrix();
 +
//matrixGfx.rotateX((float)Math.PI);
 +
MatrixUtil.getOpenGLSubMatrix(tran.basis, matrixGfx.getDump());
 +
  }
 +
 
 +
  private void setGraphicFromTransform(Transform tran)
 
   {
 
   {
 
     SimpleVector pos = obj3d.getTransformedCenter();
 
     SimpleVector pos = obj3d.getTransformedCenter();
   
+
     obj3d.translate(tran.origin.x - pos.x,
     obj3d.translate(worldTrans.origin.x - pos.x,  
+
  (-tran.origin.y) - pos.y,  
  (-worldTrans.origin.y) - pos.y,  
+
  (-tran.origin.z) - pos.z);
  (-worldTrans.origin.z) - pos.z);
+
 
   
+
   
+
   
+
    // following rotation may or maynot work. no working rotation to
+
    // find out and fix.
+
 
     float[] ma = new float[4];
 
     float[] ma = new float[4];
     float[] dump = new float[16]; //obj3d.getRotationMatrix().getDump();
+
     float[] dump = obj3d.getRotationMatrix().getDump(); //new float[16];  
    Matrix4f matrix4 = new Matrix4f();
+
 
     Matrix matrixGfx = new Matrix();
 
     Matrix matrixGfx = new Matrix();
 +
    MatrixUtil.getOpenGLSubMatrix(tran.basis, dump);
 
      
 
      
    matrix4.set(worldTrans.basis); // want to set current matrix by arg
 
   
 
    matrix4.getRow(0, ma);
 
    dump[0] = ma[0]; dump[1] = ma[1]; dump[2] = ma[2]; dump[3] = ma[3];
 
    matrix4.getRow(1, ma);
 
    dump[4] = ma[0]; dump[5] = ma[1]; dump[6] = ma[2]; dump[7] = ma[3];
 
    matrix4.getRow(2, ma);
 
    dump[8] = ma[0]; dump[9] = ma[1]; dump[10] = ma[2]; dump[11] = ma[3];
 
    matrix4.getRow(3, ma);
 
    dump[12] = ma[0]; dump[13] = ma[1]; dump[14] = ma[2]; dump[15] = ma[3];
 
   
 
    /*
 
    Matrix3f matrix3 = worldTrans.basis;
 
    matrix3.normalize();
 
    matrix3.getRow(0, ma);
 
    dump[0] = ma[0]; dump[1] = ma[1]; dump[2] = ma[2];// dump[3] = ma[3];
 
    matrix3.getRow(1, ma);
 
    dump[4] = ma[0]; dump[5] = ma[1]; dump[6] = ma[2];// dump[7] = ma[3];
 
    matrix3.getRow(2, ma);
 
    dump[8] = ma[0]; dump[9] = ma[1]; dump[10] = ma[2];// dump[11] = ma[3];
 
    */
 
 
     matrixGfx.setDump(dump);
 
     matrixGfx.setDump(dump);
 
     matrixGfx.rotateX((float)Math.PI);
 
     matrixGfx.rotateX((float)Math.PI);
 
      
 
      
 
     obj3d.setRotationMatrix(matrixGfx);
 
     obj3d.setRotationMatrix(matrixGfx);
 +
  }
 +
 +
}
 +
</pre>
 +
JPCTMotionState updates RigidBody by the Object3D when getWorldTransform() is called. This means that a startTransform isn't required. You can set the Object3D position and rotation. This also means that Kinematic states are now supported.
 +
Example:<pre>
 +
Object3D.translate(X, Y, Z);
 +
JPCTMotionState ms = new JPCTMotionState(Object3D);
 +
</pre>
 +
Update:
 +
Changed class name to JPCTMotionState to reduce typing overhead.
 +
 +
Here is the beginning of implementing IDebugDraw. It is not complete at this time and may have bad graphical drawings in some cases.
 +
<pre>
 +
package jpctbullet;
 +
 +
import java.awt.Color;
 +
import java.awt.Graphics;
 +
import javax.vecmath.Vector3f;
 +
 +
import com.threed.jpct.*;
 +
 +
import com.bulletphysics.linearmath.IDebugDraw;
 +
import com.bulletphysics.linearmath.VectorUtil;
 +
 +
 +
 +
public class JPCTDebugDraw extends IDebugDraw{
 +
  private int _debugMode;
 +
  private World _world;
 +
  private FrameBuffer _buffer;
 +
 
 +
  private SimpleVector Vec3fToSVec(Vector3f vector)
 +
  {
 +
    // JPCT uses a inverted ZY axis and coordinates so flip them
 +
    return new SimpleVector(vector.x, -vector.y, -vector.z);
 +
  }
 +
 
 +
 
 +
  public JPCTDebugDraw(World w, FrameBuffer b)
 +
  {
 +
    _world = w;
 +
    _buffer = b;
 +
  }
 +
 
 +
 
 +
  public void setDebugMode(int debugMode)
 +
  {
 +
    _debugMode = debugMode;
 +
  }
 +
 
 +
  public int getDebugMode()
 +
  {
 +
    return _debugMode;
 +
  }
 +
 
 +
 
 +
  public void drawLine(Vector3f from, Vector3f to, Vector3f color)
 +
  {
 +
    if(_debugMode > 0)
 +
    {
 +
      Camera cam = _world.getCamera();
 +
      Graphics g = _buffer.getGraphics();
 +
      Color lastColour = g.getColor();
 +
 +
      SimpleVector pointA = Interact2D.project3D2D(cam, _buffer, Vec3fToSVec(from));
 +
      SimpleVector pointB = Interact2D.project3D2D(cam, _buffer, Vec3fToSVec(to));
 +
     
 +
      g.setColor(new Color(color.x, color.y, color.z));
 +
     
 +
      // do not know why, but some times pointB is null
 +
      if(pointA != null && pointB != null){
 +
        g.drawLine((int)pointA.x, (int)pointA.y, (int)pointB.x, (int)pointB.y);
 +
 +
      }
 +
      g.setColor(lastColour);
 +
    }
 +
  }
 +
 
 +
 
 +
  public void draw3dText(Vector3f location, String textString)
 +
  {
 +
    Camera cam = _world.getCamera();
 +
    Graphics g = _buffer.getGraphics();
 +
    SimpleVector loc = Interact2D.project3D2D(cam, _buffer, Vec3fToSVec(location));
 +
   
 +
    g.drawString(textString, (int)loc.x, (int)loc.y);
 +
  }
 +
 
 +
 
 +
  public void drawContactPoint(Vector3f pointOnB, Vector3f normalOnB, float distance, int lifeTime, Vector3f color)
 +
  {
 +
    Camera cam = _world.getCamera();
 +
    Graphics g = _buffer.getGraphics();
 +
    Color lastColour = g.getColor();
 +
   
 +
    Vector3f sum = new Vector3f();
 +
    VectorUtil.add(sum, pointOnB, normalOnB);
 +
   
 +
    SimpleVector pointA = Interact2D.project3D2D(cam, _buffer, Vec3fToSVec(pointOnB));
 +
    //SimpleVector pointB = Interact2D.project3D2D(cam, _buffer, Vec3fToSVec(normalOnB));
 +
    SimpleVector to = new SimpleVector(sum.x*distance, sum.y*distance, sum.z*distance);
 +
   
 +
    g.setColor(new Color(color.x, color.y, color.z));
 +
    g.drawLine((int)pointA.x, (int)pointA.y, (int)to.x, (int)to.y);
 +
   
 +
    g.setColor(lastColour);
 +
    draw3dText(pointOnB, "" + lifeTime);
 +
  }
 +
 
 +
 
 +
  public void reportErrorWarning(String warningString)
 +
  {
 +
    System.out.println(warningString);
 
   }
 
   }
  
Line 507: Line 602:
 
Here is the source. I have limited bandwidth so don't download to much please :) Also server sometimes goes down. So just be patient and it will be up. Usually less than a day.
 
Here is the source. I have limited bandwidth so don't download to much please :) Also server sometimes goes down. So just be patient and it will be up. Usually less than a day.
 
[http://sre.hopto.org/share/jBulletTest.zip]http://sre.hopto.org/share/jBulletTest.zip]
 
[http://sre.hopto.org/share/jBulletTest.zip]http://sre.hopto.org/share/jBulletTest.zip]
 +
 +
Update:
 +
jBulletTest.zip has been updates for source code changes and the addition of the IDebugDraw. It also has a new compiled version of jBullet from the SVN as of January 21 2010.
 +
 +
[[Category:jPCT]]

Latest revision as of 00:33, 2 February 2013

Physics

Apart from the methods for Collision detection , there's no real support for physics in jPCT. But you can use JBullet for example or any other physics library out there. There is a demo that shows JBullet in combination with jPCT on the demos page, but it's really a hack to make the JBullet example work with jPCT at a minimum effort. There are some sources for this posted in the forum, but beware of the hackiness: Simple JBullet port



jBullet, A simple how to guide

--.jayderyu 23:47, 18 January 2010 (UTC)

[This is a work in progress code and guide. Feel free to make changes and code corrections.]

Since you have decided to use jBullet I suggest going here http://jbullet.advel.cz/ first. Download the latest version. Integrating straight forward raw physics is easy and won't cause any kind of hair pulling :) at least I've already dealt with part of that hair pulling for you :)

Things to know:

Vecmath.jar. jBullet uses java3D vecmath library. You will need this. Fortunetly it is packed with the jBullet download.

Scaling in jBullet. unit scale by default is a 1:1 meter. Though feel free to scale it as 1 Unit as needed. For safety issues don't create objects less than 0.01. Really big objects also tend to start causing simulation problems if landing on top of smaller objects. IIRC up to 10 is safe.

Axis jBullet uses a YZ axis and coordinates that are more in line with OpenGL. This is important as JPCT has opposing YZ.

Linear or the term used in Bullet. This represents XYZ movement. So a Constraint that limits the Linear will prevent movement.

Multithreading or lack there of. At this time jBullet does not support multi threading though Bullet has it in the works. Do not try to support multi threading at this time. If however anyone wishes to port over Bullet multithreading feel free. I'm sure many would love to see it :)

Kinematic Bodies. There are going to be cases where you will want manual control over your Object3D/RigidBody. A RigidBody can be set to Kinematic state. This will allow for manual updates. *guilty*JPCTBulletMotionState will allow for a seemless integration, but it's not done :P Cases of Kinematic bodies would be characters, paddles and some forms of direct controlled objects.

GhostObjects. These are RigidBody's that do not collide with other Bodies. They are meant to cause Action callbacks only. The Bullet Manual(I suggest reading) suggest to use them for characters, explosions and sensors. At the time of this writing GhostObjects are in jBullet svn, not in the distributed version.

Deterministic Physics. There will be a time when you want your physics simulation to behave the same way time and time again. This is called Deterministic. This type of physics is very important for networking or cases of desired repeated results. Tutorials can be found in further reading.

For further reading I suggest [1] and [2]. If you have a simple question about jBullet your better off emailing the porter than asking of the forums. If you have complicated questions ask on the forums.


The guide


Step 1 jBullet uses a dynamic world to handle physics simulation. This is very similar to JPCT world setup. The following code below shows how to get a basic jBullet dynamic world ready. I would explain how this means, but I'm using some one else's API for the sake of time rather than learning how it works. So I honestly I can not tell you how this all works. Some of the naming conventions should give an idea though.

		collisionConfiguration = new DefaultCollisionConfiguration();
		dispatcher = new CollisionDispatcher(collisionConfiguration);
		Vector3f worldAabbMin = new Vector3f(-10000,-10000,-10000);
		Vector3f worldAabbMax = new Vector3f(10000,10000,10000);
		AxisSweep3 overlappingPairCache = new AxisSweep3(worldAabbMin, worldAabbMax);
		SequentialImpulseConstraintSolver solver = new SequentialImpulseConstraintSolver();
		
		dynamicWorld = new DiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
		dynamicWorld.setGravity(new Vector3f(0,-10,0));
		dynamicWorld.getDispatchInfo().allowedCcdPenetration = 0f;

That's it. Yes it is a little heftier than getting a JPCT world ready, but it's not that bad.

Step 2 We are going to work with a sample physics test. So we are going to need some objects to collide against. At this time jBullet doesn't support Bullet static plane. So we are going to create a satic box that doesn't move that will serve as our ground. Obviously if your projects are in space you won't need this :)

		CollisionShape groundShape = new BoxShape(new Vector3f(100.f, 50.f, 100.f));
		Transform groundTransform = new Transform();
		groundTransform.setIdentity();
		groundTransform.origin.set(new Vector3f(0.f, -56.f, 0.f));
		float mass = 0f;
		Vector3f localInertia = new Vector3f(0, 0, 0);
	  DefaultMotionState myMotionState = new DefaultMotionState(groundTransform);
		RigidBodyConstructionInfo rbInfo = new RigidBodyConstructionInfo(
					mass, myMotionState, groundShape, localInertia);
		RigidBody body = new RigidBody(rbInfo);
		dynamicWorld.addRigidBody(body);

Time to start explaining what's going on. Some parts are straight forward other parts not so much.

CollisionShape groundShape = new BoxShape(new Vector3f(100.f, 50.f, 100.f));

Similar to JPCT Box. This defines a simple collision shape. Shapes inform how jBullet handles collision. There are spheres and trimesh support if needed.

		Transform groundTransform = new Transform();
		groundTransform.setIdentity();
		groundTransform.origin.set(new Vector3f(0.f, -56.f, 0.f));

A Bullet Transform is a package of information. This stores both a rotation Matrix3x3 of row major and position or translation information. Transform.origin<Vector3f> is where you will find position. Transform.basis<Matrix3f> is where you will find the bodies rotation. When a new Transform is created make sure .setIdentity() is run to fill in 0 values.

		float mass = 0f;
		Vector3f localInertia = new Vector3f(0, 0, 0);

Make note of this. A mass of 0 means that the object is static. Static objects will not move or rotate in the simulation. Static objects do not need a starting inertia.

DefaultMotionState myMotionState = new DefaultMotionState(groundTransform);

A MotionState is a key component to integrating physics to your graphical world. The DefaultMotionState has methods you can call on to manually get the data for graphical updates. DefaultMotionState.graphicsWorldTransform.origin DefaultMotionState.graphicsWorldTransform.basis For our purposes though we are using DefaultMotionState for a static object since we don't need to update a graphical object.

		RigidBodyConstructionInfo rbInfo = new RigidBodyConstructionInfo(
					mass, myMotionState, groundShape, localInertia);
		RigidBody body = new RigidBody(rbInfo);
		dynamicWorld.addRigidBody(body);

Create a parameter object to create the RigidBody. A RigidBody is the equivalent of Object3D. It's important to keep track of it some form. When ready add your RigidBody to the physics world.


Step 3 Static objects are no fun to watch. They don't do anything. This step which is second to last is where coding get's more fun :)

             BoxShape shape = new BoxShape(new Vector3f(2,2,2));
 	     Vector3f localInertia = new Vector3f(0,0,0);
	     shape.calculateLocalInertia(mass, localInertia);

	    startTransform = new Transform();
	    startTransform.setIdentity();
	    startTransform.origin.set(0, 0, 0);
	    
	    ms = new JPCTBulletMotionState(yourObject3D, startTransform);
	    
	    rbInfo = new RigidBodyConstructionInfo(mass, ms, shape, localInertia);
	    body = new RigidBody(rbInfo);
	    body.setRestitution(0.1f);
	    body.setFriction(0.50f);
	    body.setDamping(0f, 0f);
	    
	    body.setUserPointer(boxgfx);

	    dynamicWorld.addRigidBody(body);

Theres the starting code for your cool fun RigidBody's :) First create a shape like above. In our sample we have a 2x2x2 Cube. Makes using JPCT Primitives easier.

 	     
             Vector3f localInertia = new Vector3f(0,0,0);
	     shape.calculateLocalInertia(mass, localInertia);

You don't need a separate inertia variable, but I left it in so you know the parameter arguments. The second line is VERY IMPORTANT Shape.calculateLocalInertia(float, Vector3f) MUST be called for your physics simulation to work properly. If you miss this after my warning don't rip out your hair for a week *cough* like I did *cough*. Just come back to the Wiki  :-) Create a new transform and set starting position and or rotation.

ms = new JPCTBulletMotionState(yourObject3D, startTransform);

Here is the magic where jBullet and JPCT will work together. You can find the code at the bottom(with warnings).

	    body.setRestitution(0.1f);
	    body.setFriction(0.50f);
	    body.setDamping(0f, 0f);

Fairly basic representation.

Resitution(0.0 to 1.0) represent a value of energy return on collision. Bullet doesn't naturally support infinite return. So setting a value of 1+ will not cause an object to bounce forever, but it will bounce for a long time.

Friction(0.0 to 1.0) roughness of the surface. This is slide of an object. 0 is as slick as possible while 1 should present no sliding. Think of it as Black Ice to very coarse sandpaper.

Damping(0.0 to 1.0) Linear and rotation/axis damping. This is best represented as atmosphere. Space has no atmosphere an thus objects will move and rotate forever. While water would be very dense and limit movement and rotation very fast without constant force being added.


body.setUserPointer(boxgfx);

You don't need this, but depending on how you end up tracking objects(by physics in this case). You can set a reference to an Object in the RigidBody(similar to Object3D.setUserObject()). That way you can call the Object later on with RigidBody.getUserPointer(). Just thought this might help.

Of course add the RigidBody when ready.

Step 4 Ok you have a static body to check collisions against. You have dynamic RigidBody's that can be watched(I know I didn't show graphic code). Well like JPCT some extra code to move the simulation forward is required.

		  float ms = clock.getTimeMicroseconds();
		  clock.reset();
		  dynamicWorld.stepSimulation(ms / 1000000f);

Here it is and it's simple. Put it before your render code. This is the basic HelloWorld sample. It uses a timer like clock and steps the simulation based on time difference.


Test Code

First the basic demo code. This demo code is super basic and was written to run on a AMD 2.6mhz dual core running the software renderer. This runs 400 boxes decently. Feel free to implement the hardware renderer and modify some values to see how many boxes you can get going. If your running on less it will run slower. If it's a big different drop the numBoxes value near the top.

package jpctbullet;

import com.threed.jpct.*;
import com.threed.jpct.util.Light;

import java.util.Vector;
import java.util.List;
import java.util.ArrayList;
import javax.vecmath.Vector3f;
import javax.swing.*;
import java.awt.*;
import javax.vecmath.Matrix3f;
import javax.vecmath.Matrix4f;

import com.bulletphysics.BulletGlobals;
import com.bulletphysics.collision.broadphase.AxisSweep3;
import com.bulletphysics.collision.dispatch.CollisionObject;
import com.bulletphysics.collision.dispatch.CollisionWorld;
import com.bulletphysics.collision.shapes.BoxShape;
import com.bulletphysics.dynamics.DynamicsWorld;
import com.bulletphysics.dynamics.constraintsolver.Point2PointConstraint;
import com.bulletphysics.dynamics.constraintsolver.TypedConstraint;

import com.bulletphysics.linearmath.*;

import com.bulletphysics.collision.broadphase.BroadphaseInterface;
import com.bulletphysics.collision.broadphase.SimpleBroadphase;
import com.bulletphysics.collision.dispatch.CollisionDispatcher;
import com.bulletphysics.collision.dispatch.DefaultCollisionConfiguration;
import com.bulletphysics.collision.shapes.BoxShape;
import com.bulletphysics.collision.shapes.CollisionShape;
import com.bulletphysics.collision.shapes.StaticPlaneShape;
import com.bulletphysics.dynamics.DiscreteDynamicsWorld;
import com.bulletphysics.dynamics.RigidBody;
import com.bulletphysics.dynamics.RigidBodyConstructionInfo;
import com.bulletphysics.dynamics.constraintsolver.ConstraintSolver;
import com.bulletphysics.dynamics.constraintsolver.SequentialImpulseConstraintSolver;

public class BulletTest {
  
  public int numBoxes = 400;
  public int strHeight= 10;
  
	private World world;
	private FrameBuffer buffer;
	private Object3D box; // only something for the camera to focus on.
	private JFrame frame;
  private JPCTDebugDraw iDebugDraw;
	
  public DiscreteDynamicsWorld dynamicWorld;
  public int maxSubSteps;
  public float timeStep, fixedTimeStep;
  protected Clock clock = new Clock(); 
	private List<CollisionShape> collisionShapes = new ArrayList<CollisionShape>();
	private BroadphaseInterface overlappingPairCache;
	private CollisionDispatcher dispatcher;
	private ConstraintSolver solver;
	private DefaultCollisionConfiguration collisionConfiguration;
  
	
  private Vector<RigidBody> boxList;
	
	public static void main(String[] args) throws Exception {
		new BulletTest().loop();
	}

	public BulletTest() throws Exception {
		frame=new JFrame("JPCTBullet Test");
		frame.setSize(800, 600);
		frame.setVisible(true);
		frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
		
	  boxList = new Vector();	  

    int cpu = Runtime.getRuntime().availableProcessors();
    if(cpu > 1){
      Config.useMultipleThreads = true;
      Config.maxNumberOfCores = cpu;
      Config.loadBalancingStrategy = 1;
    }
		world = new World();
		//World.setDefaultThread( Thread.currentThread() );
		world.setAmbientLight(120, 120, 120);

		TextureManager.getInstance().addTexture("box", new Texture("box.jpg"));

		box = Primitives.getBox(1f, 1f);
		box.translate(0, -50, 0);
		box.setTexture("box");
		box.setEnvmapped(Object3D.ENVMAP_ENABLED);
		box.build();
		world.addObject(box);
		
		Object3D ground = Primitives.getPlane(4,25);
		ground.setTexture("box");
		ground.setEnvmapped(Object3D.ENVMAP_ENABLED);
		ground.rotateX((float)-Math.PI);
  	ground.build();
		world.addObject(ground);
		
		world.getCamera().setPosition(150, -50, -5);
		world.getCamera().lookAt(box.getTransformedCenter());
		
		Light light = new Light(world); 
		light.setPosition(new SimpleVector(-200, -50 , 80));
		light.setIntensity(150,140,150);
		
		
		collisionConfiguration = new DefaultCollisionConfiguration();
		dispatcher = new CollisionDispatcher(collisionConfiguration);
		Vector3f worldAabbMin = new Vector3f(-10000,-10000,-10000);
		Vector3f worldAabbMax = new Vector3f(10000,10000,10000);
		AxisSweep3 overlappingPairCache = new AxisSweep3(worldAabbMin, worldAabbMax);
		SequentialImpulseConstraintSolver solver = new SequentialImpulseConstraintSolver();
		
		dynamicWorld = new DiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
		dynamicWorld.setGravity(new Vector3f(0,-10,0));
		dynamicWorld.getDispatchInfo().allowedCcdPenetration = 0f;
		
		CollisionShape groundShape = new BoxShape(new Vector3f(100.f, 50.f, 100.f));
		Transform groundTransform = new Transform();
		groundTransform.setIdentity();
		groundTransform.origin.set(new Vector3f(0.f, -56.f, 0.f));
		float mass = 0f;
		Vector3f localInertia = new Vector3f(0, 0, 0);
	  DefaultMotionState myMotionState = new DefaultMotionState(groundTransform);
		RigidBodyConstructionInfo rbInfo = new RigidBodyConstructionInfo(
					mass, myMotionState, groundShape, localInertia);
		RigidBody body = new RigidBody(rbInfo);
		dynamicWorld.addRigidBody(body);
		
		dynamicWorld.clearForces();
		
		initTestObects();
	}

	private void loop() throws Exception {
		buffer = new FrameBuffer(800, 600, FrameBuffer.SAMPLINGMODE_NORMAL);
		//Canvas canvas=buffer.enableGLCanvasRenderer();
		//buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
		//frame.add(canvas);
		
		iDebugDraw = new JPCTDebugDraw(world, buffer);
		iDebugDraw.setDebugMode(DebugDrawModes.DRAW_WIREFRAME);
		dynamicWorld.setDebugDrawer(iDebugDraw);
		
		while (frame.isShowing()) {		  
		  float ms = clock.getTimeMicroseconds();
		  clock.reset();
		  dynamicWorld.stepSimulation(ms / 1000000f);
		  
		  box.rotateY(0.01f); // it's rotating because it looks neater :P
			buffer.clear(java.awt.Color.BLUE);
			world.renderScene(buffer);
			world.draw(buffer);
			
  		buffer.update();
 			
  		dynamicWorld.debugDrawWorld();
						
			//buffer.displayGLOnly();
			//canvas.repaint();
			buffer.display(frame.getGraphics());
			//Thread.sleep(10);
		}
		buffer.disableRenderer(IRenderer.RENDERER_OPENGL);
		buffer.dispose();
		frame.dispose();
		System.exit(0);
	}
	
	
	public void initTestObects(){
	  Transform transform;
	  Object3D boxgfx;
	  BoxShape shape = new BoxShape(new Vector3f(2,2,2));
	  JPCTBulletMotionState ms;
	  float mass = 5;
	  Vector3f localInertia = new Vector3f(0,0,0);
	  shape.calculateLocalInertia(mass, localInertia);
	  RigidBodyConstructionInfo rbInfo;
	  RigidBody body;
	  
	  for(int i = 1; i < numBoxes; i++){
      boxgfx = Primitives.getCube(2);
	    boxgfx.setTexture("box");
	    boxgfx.setEnvmapped(Object3D.ENVMAP_ENABLED);
	    boxgfx.translate(0, (strHeight+(i * 4)) * -1,(float) 50 - i/3);
	    boxgfx.build();
	    boxgfx.rotateY((float) Math.PI/4f);
	    boxgfx.rotateMesh();
	    boxgfx.getRotationMatrix().setIdentity();
	    world.addObject(boxgfx);
	    
	    transform = new Transform();
	    transform.setIdentity();
	    
	    ms = new JPCTBulletMotionState(boxgfx);
	    
	    rbInfo = new RigidBodyConstructionInfo(mass, ms, shape, localInertia);
	    body = new RigidBody(rbInfo);
	    body.setRestitution(0.1f);
	    body.setFriction(0.50f);
	    body.setDamping(0f, 0f);
	    
	    body.setUserPointer(boxgfx);
	    boxList.add(body);
	    
	    dynamicWorld.addRigidBody(body);
	  } // end for loop

	}
}


Heres is the magic code. I must warn that this code is still in work of progress. The current version of JPCTMotionState supports Kinematic RigidBody's and RigidBody placement automatically by Graphical Object3D. Pending centerOfMass / Pivot rotation.

package jpctbullet;

import javax.vecmath.Vector3f;
import javax.vecmath.Matrix3f;
import javax.vecmath.Matrix4f;

import com.threed.jpct.SimpleVector;
import com.threed.jpct.Object3D;
import com.threed.jpct.Matrix;

import com.bulletphysics.linearmath.MotionState;
import com.bulletphysics.linearmath.Transform;
import com.bulletphysics.linearmath.MatrixUtil;


public class JPCTBulletMotionState implements MotionState{
	public final Transform centerOfMassOffset = new Transform();
  private Object3D obj3d;
  
  public JPCTBulletMotionState(Object3D obj)
  {
    obj3d = obj;
    centerOfMassOffset.setIdentity();
  }
  
	public JPCTBulletMotionState(Object3D obj, Transform startTrans)
  {
    obj3d = obj;
    setGraphicFromTransform(startTrans);
		centerOfMassOffset.setIdentity();
	}  
  
	public JPCTBulletMotionState(Object3D obj, Transform startTrans, Transform centerOfMassOffset)
  {
    obj3d = obj;
    setGraphicFromTransform(startTrans);
		this.centerOfMassOffset.set(centerOfMassOffset);
	}  
	
	
  public Transform getWorldTransform(Transform worldTrans){
    setTransformFromGraphic(worldTrans);
    return worldTrans;
  }
  
  public void setWorldTransform(Transform worldTrans)
  {
    setGraphicFromTransform(worldTrans);
  }
  
  
  private void setTransformFromGraphic(Transform tran)
  {
	  SimpleVector p = obj3d.getTransformedCenter();
		tran.origin.set(p.x, -p.y, -p.z); // not sure if translation or position
		
		Matrix matrixGfx = obj3d.getRotationMatrix();
		//matrixGfx.rotateX((float)Math.PI);
		MatrixUtil.getOpenGLSubMatrix(tran.basis, matrixGfx.getDump());
  }
  
  private void setGraphicFromTransform(Transform tran)
  {
    SimpleVector pos = obj3d.getTransformedCenter();
    obj3d.translate(tran.origin.x - pos.x,
		  (-tran.origin.y) - pos.y, 
		  (-tran.origin.z) - pos.z);

    float[] ma = new float[4];
    float[] dump = obj3d.getRotationMatrix().getDump(); //new float[16]; 
    Matrix matrixGfx = new Matrix();
    MatrixUtil.getOpenGLSubMatrix(tran.basis, dump);
    
    matrixGfx.setDump(dump);
    matrixGfx.rotateX((float)Math.PI);
    
    obj3d.setRotationMatrix(matrixGfx);
  }

}

JPCTMotionState updates RigidBody by the Object3D when getWorldTransform() is called. This means that a startTransform isn't required. You can set the Object3D position and rotation. This also means that Kinematic states are now supported.

Example:
Object3D.translate(X, Y, Z);
JPCTMotionState ms = new JPCTMotionState(Object3D);

Update: Changed class name to JPCTMotionState to reduce typing overhead.

Here is the beginning of implementing IDebugDraw. It is not complete at this time and may have bad graphical drawings in some cases.

package jpctbullet;

import java.awt.Color;
import java.awt.Graphics;
import javax.vecmath.Vector3f;

import com.threed.jpct.*;

import com.bulletphysics.linearmath.IDebugDraw;
import com.bulletphysics.linearmath.VectorUtil;



public class JPCTDebugDraw extends IDebugDraw{
  private int _debugMode;
  private World _world;
  private FrameBuffer _buffer;
  
  private SimpleVector Vec3fToSVec(Vector3f vector)
  {
    // JPCT uses a inverted ZY axis and coordinates so flip them
    return new SimpleVector(vector.x, -vector.y, -vector.z);
  }
  
  
  public JPCTDebugDraw(World w, FrameBuffer b)
  {
    _world = w;
    _buffer = b;
  }
  
  
  public void setDebugMode(int debugMode)
  {
    _debugMode = debugMode;
  }
  
  public int getDebugMode()
  {
    return _debugMode;
  }
  
  
  public void drawLine(Vector3f from, Vector3f to, Vector3f color)
  { 
    if(_debugMode > 0)
    {
      Camera cam = _world.getCamera();
      Graphics g = _buffer.getGraphics();
      Color lastColour = g.getColor();

      SimpleVector pointA = Interact2D.project3D2D(cam, _buffer, Vec3fToSVec(from));
      SimpleVector pointB = Interact2D.project3D2D(cam, _buffer, Vec3fToSVec(to));
      
      g.setColor(new Color(color.x, color.y, color.z));
      
      // do not know why, but some times pointB is null
      if(pointA != null && pointB != null){
        g.drawLine((int)pointA.x, (int)pointA.y, (int)pointB.x, (int)pointB.y);

      }
      g.setColor(lastColour);
    }
  }
  
  
  public void draw3dText(Vector3f location, String textString)
  {
    Camera cam = _world.getCamera();
    Graphics g = _buffer.getGraphics();
    SimpleVector loc = Interact2D.project3D2D(cam, _buffer, Vec3fToSVec(location));
    
    g.drawString(textString, (int)loc.x, (int)loc.y);
  }
  
  
  public void drawContactPoint(Vector3f pointOnB, Vector3f normalOnB, float distance, int lifeTime, Vector3f color)
  {
    Camera cam = _world.getCamera();
    Graphics g = _buffer.getGraphics();
    Color lastColour = g.getColor();
    
    Vector3f sum = new Vector3f();
    VectorUtil.add(sum, pointOnB, normalOnB);
    
    SimpleVector pointA = Interact2D.project3D2D(cam, _buffer, Vec3fToSVec(pointOnB));
    //SimpleVector pointB = Interact2D.project3D2D(cam, _buffer, Vec3fToSVec(normalOnB));
    SimpleVector to = new SimpleVector(sum.x*distance, sum.y*distance, sum.z*distance);
    
    g.setColor(new Color(color.x, color.y, color.z));
    g.drawLine((int)pointA.x, (int)pointA.y, (int)to.x, (int)to.y);
    
    g.setColor(lastColour);
    draw3dText(pointOnB, "" + lifeTime);
   }
  
  
  public void reportErrorWarning(String warningString)
  {
    System.out.println(warningString);
  }

}


Here is the source. I have limited bandwidth so don't download to much please :) Also server sometimes goes down. So just be patient and it will be up. Usually less than a day. [3]http://sre.hopto.org/share/jBulletTest.zip]

Update: jBulletTest.zip has been updates for source code changes and the addition of the IDebugDraw. It also has a new compiled version of jBullet from the SVN as of January 21 2010.