[quote]I’m not a physics guy Smiley
[/quote]
Haha, I wasn’t initially either. I’ll look it up.
I’m not involved with Bullet, but I’m pretty sure it uses most of the same techniques (at the core) as Box2d - they both solve constraints with sequential impulses and warm starting. Forces should be available quite easily by pulling the warm starting values (there’s probably a function to get the constraint force, or maybe you can get it from a callback), and redundant constraints should require no special treatment (iterative solvers don’t have problems with redundant or inconsistent constraints because they apply constraints one at a time, though this can become unstable in some cases). The integrator is probably symplectic Euler, though I could definitely be wrong there.
If and when continuous collision detection comes back, Bullet’s approach is conservative advancement using GJK for nearest point distance calculations. Can’t wait for that to show up (or rather, be brought back - it used to work, I think), it’s the killer feature that makes me love Box2d over all other 2d physics engines, it will be awesome to have it in 3d, too!
Yeah your right. Its sequential impulse whatsit.
Bullet does not offer me a way of getting at the forces exerted by the constraint though (that I could find anyway). Its not as easy as unwrapping it from the solver though, because the info at the solver level has allready integrated geometry information. So you would have to transform the info back out again to determine the force exerted by an anchor at a specific location (not a hard transformation admittedly, but someone has to write the transformation and the callback option for the solver).
Shame to hear the curvey collision detection is not currently working. That was the thing I liked most about Bullet.
Bullet constraint solver is based on iterative sequential impulses, which is the very similar to ODE quickstep solver.
You should be able to get the applied impulse (=timestep*applied force) from the btManifoldPoint member m_appliedImpulse (in the C++ version at least).
What do you mean by ‘curvey’ collision detection? The btCollisionWorld::convexSweepQuery should be working fine. We’ll try to make some basic CCD motion clamp support for Bullet 2.72.
Thanks,
Erwin
By the way, in the other thread they report some showstopper/issues on JBullet, lack of translational constraints? Aren’t the btSliderJoint and such ported yet?
http://www.java-gaming.org/index.php/topic,19108.msg150571.html#msg150571
It would be good to get those remaining issues fixed.
New version available on JBullet homepage.
Changes in release 20080917:
- Documented many classes and adjusted existing descriptions
- Minor API changes
- Changed method names for callbacks to be more descriptive
- Improved BulletGlobals
- Added SliderConstraint
- Added ForkLiftDemo
Great work with the JBullet adaptation. I may use it in the near future.
Hi jezek2,
I have decided to port my game from using ODE to use JBullet. I do not want any native libs in my game; I don’t won’t security dialogs to pop up.
Are you familiar with Kenji Hiranabe’s version of javax.vecmath?
(http://www.objectclub.jp/download/vecmath_e)
It is compatible with Sun’s vecmath, at least earlier versions of Sun’s vecmath. It uses “No ‘new’ operator at all except for GMatrix, GVecator and error exceptions.” ( http://java-house.jp/ml/archive/j-h-b/017987.html )
My game API uses my own modified version of Hiranabe’s vecmath (the API allows people to create their own Artificial Intelligence modules). It provides mutable, read only and immutable Vector3f:s, Matrix3f:s, etc., with a small memory footprint and, I think, no significant impact on performance. I think it is largely backwards compatible with Hiranabe’s and Sun’s versions.
I will probably maintain my own vendor branch of JBullet, where I have replaced Sun’s vecmath with my own modified version of Hiranabe’s vecmath. (Vendor branches: http://svnbook.red-bean.com/en/1.1/ch07s05.html )
Thanks,
Magnus
Nice to hear
Yeah I know it. I’ll probably distribute it instead of the Sun’s mainly because of license (Sun doesn’t allow GPLv3 usage with it’s GPLv2 only+classpath exception and usage of the older non-GPL license with GPL is in grey area).
I’m interested in your approach. I’ve been myself experimenting with different ways how to handle vecmath library, but in the end found Sun’s vecmath approach the best. Could you share JavaDoc or something (can be privately by e-mail)?
[quote=“LeoMaheo,post:107,topic:31221”]
That’s probably best option, the other option is to convert between Sun’s vecmath and yours (which is not much problem as physics tends to be quite separated from other things anyway). There exist many vecmath libraries (mostly private/custom) and it would be impossible to support them.
I’d be happy to. Please find below usage examples, performance notes and the actual implementation.
Notes:
Memory overhead: New class field: Object key'' Performance overhead:
if != null’’ tests.
Concerning memory overhead:
According to the Netbeans 6.5 profiler:
For Quat4f and Vector3f, 33% more memory is required to store an instance.
For Matrix3f, no additional memory (?HotSpot happens to align memory so there’s room fore Sealable.key anyway?)
For Matrix4f, 11% more memory is required to store an instance.
Usage examples:
Immutable variables (sealForever()):
public class Vector3f extends Tuple3f implements Serializable {
public static final Vector3f PLUS_X = new Vector3f(1, 0, 0).sealForever();
public static final Vector3f PLUS_Y = new Vector3f(0, 1, 0).sealForever();
...
Read-only variables (seal() and unseal()).
Can actually be written to by the one with the appropriate key.
This is exemplified by interpolate(…) below (`limbRotations’ is used as key).
public class KeyFrame<L extends Enum<L> & BodyPart> {
// ...
/**
* Also seals the Quat4f instances. */
private final EnumMap<L, Quat4f> limbRotations;
/**
* Sets the rotation of {@code limb} to a normalized clone of {@code rotation}.
*/
public void setRotation(L limb, Quat4f rotation) {
limbRotations.put(limb, new Quat4f(rotation).normalize().seal(limbRotations));
}
/**
* Returns the rotation of {@code limb}, for read access only.
*/
public Quat4f getRotation(L limb) {
return limbRotations.get(limb);
}
/**
* Interpolates this {@code KeyFrame} and {@code target},
* and places the result into this instance.
* @param amount how close to {@code target} the resulting key frame should be. In [0, 1].
*/
public void interpolate(KeyFrame<L> target, float amount) {
for (Entry<L, Quat4f> entry : limbRotations.entrySet()) {
L limb = entry.getKey();
Quat4f quatThis = entry.getValue();
Quat4f quatTarget = target.limbRotations.get(limb);
quatThis.unseal(limbRotations);
quatThis.interpolate(quatTarget, amount);
quatThis.seal(limbRotations);
}
}
Implementation:
package madmath;
public abstract class Sealable {
/**
* Read-only forever tag.
*/
private static final Object IMMUTABLE = new Object();
/**
* Read-only lock.
*/
private transient Object key = null;
public Sealable seal(Object key) {
if (this.key != null && this.key != key)
throw new IllegalArgumentException(getClass().getSimpleName() +
" instance already sealed with other key");
if (key == null)
throw new NullPointerException("key is null");
this.key = key;
return this;
}
public Sealable sealForever() {
return seal(IMMUTABLE); // cannot remove this seal
}
public Sealable unseal(Object key) {
if (this.key != null && this.key != key)
throw new IllegalArgumentException("Wrong key");
this.key = null;
return this;
}
void _checkWritable() {
if (key != null)
throw new IllegalStateException(getClass().getSimpleName() + " instance is " +
(key == IMMUTABLE ? "immutable" : "read only"));
}
}
Before modification, _checkWritable() is invoked.
For example,
public abstract class Tuple3f extends Sealable implements Serializable {
public final void set(float x, float y, float z) {
_checkWritable();
this.x = x;
this.y = y;
this.z = z;
}
Here is the mem test program. I run it, and check the memory usage reported by the Netbans profiler. Hope results are reliable…
public class MemTest {
static class SmallVec3f {
float x, y, z;
}
static class SmallQuat4f {
float x, y, z, w;
}
static class SmallMat3f {
float m00, m01, m02,
m10, m11, m12,
m20, m21, m22;
}
static class SmallMat4f {
float m00, m01, m02, m03,
m10, m11, m12, m13,
m20, m21, m22, m23,
m30, m31, m32, m33;
}
public static void main(String args[]) {
int num = 10000;
ArrayList<Object> objs = new ArrayList<Object>();
for (int i = 0; i < num; ++i) {
objs.add(new SmallVec3f());
objs.add(new SmallQuat4f());
objs.add(new SmallMat3f());
objs.add(new SmallMat4f());
// These 4 below have an additional field ``Object key''.
objs.add(new Vector3f());
objs.add(new Quat4f());
objs.add(new Matrix3f());
objs.add(new Matrix4f());
}
while (true) {
// check profiler results
}
}
}
Regards, Magnus
That’s really interesting approach
I think that the overhead is minimal and shouldn’t be problem because if you need to have many vectors (like 10000 or so), it’s better to store them in float[] array or FloatBuffer and do bulk operations on it anyway.
I think it would be better to move the exception generation in _checkWritable() to own private method, so you get better chance to inline in HotSpot. Also for better clarity, it would be better to always use descriptive private static final field as key, instead of reusing some array, as it can get messy when the code evolves.
How do you handle direct operation on x,y,z fields? Did you make them protected and provide setter/getters or are they naked as in original vecmath?
I see slight problem with side effects in your approach. For example you return some vector, the caller is using it for some computation and calls some other method which changes the same instance and suddendly caller is using different data. This kind of unintuitive behaviour is very dangerous. Or when they store the instance somewhere else for later usage
On the other hand it gives nice degree of safety for performance sensitive code and could be fighted by stating everywhere that the returned vectors are meant only for intermediate usage.
Personally I still like better the output parameters and shifting up the care of allocation to the caller, as it’s more intuitive and less error-prone, though some more copying will happen and you need to pass the out parameter which can be annoying sometimes
Package private, and getters and setters.
(But I just made them public again, for now, since JBullet does lots of direct access to x,y,z.)
That’s a good point.
I don’t, however, expose such function calls to the end users of my application (= artificial intelligence (AI) module writers).
((The KeyFrame code snippet in my previous post is from a game internal class (private class loader).))
However, the values of read-only variables (read-only from the AI module’s point of view) do change. But at well-defined points in time:
In my application, the simulation state (rotations, locations etc. - lots of Matrix4f) is copied to a snapshot. The AI modules then do computations based on that snapshot of the simulation. After a while, the AI modules are done thinking. The snapshot is then updated with new simulation data: The same instance of e.g. a Vector3f is updated with new simulation data, although it is read only all the time, from the AI modules’ point of view.
And you’re right that other users of the math lib could write unintuitive code in the way you described.
Personally, when constructing game internal stuff visible to no one but me:
Were it not for read only instances, I might, for the sake of performance, or for the sake of laziness, expose mutable Vector3f:s etc., for read access only (without enforcing read-access-only). I believe I here would find the lock-unlock-lock approach safer.
Yes, I might actually also like the original vecmath approach better. After all, x,y,z are still directly accessible, and the code is simpler to understand.
Actually, since JBullet does lots of direct access to x,y,z and it seems to take long to rewrite and perhaps maintain all such changes in a separate vendor branch,
I’ll probably ditch the read-only etc. stuff.
Then I’ll save lots of time; I haven’t really started using that read-only fancy-fancy stuff yet, only at one place (the code snippet in my previous post).
And in the future, when HotSpot does escape analysis, I suppose all such read only stuff will be fairly pointless.
hi guys,
I tried to add some restrictions to rigidBodies within an InternalTickCallback.
Especially I tried to restrict the Z-movement of moving sphere on a surface ground to avoid jumping because of xy-torque or gravity in combination with full restitution. Because I need full restitution for collsion with static objects with the xy-layer, but not in the z-layer.
But my tries-outs didn’t work not or causing strange collision detection behavior after changing the interpolated transform of a rigid body. Perhaps the strange effects are caused, because the rigidBodies are overlaping before the moment of the transform is changed.
My current work around solution is to restrict the active transform not within the internal tick callback, instead I simply perform rigidBody.setWorldTransfrom() as postStep.
short reproducing source code snippet:
world.setInternalTickCallback(
new InternalTickCallback() {
public void internalTick(DynamicsWorld world, float timeStep) {
// any body that overlaps ...
RigidBody rigidBody = ... ;
Transform activeTransform = new Transform();
rigidBody.getInterpolationWorldTransform(activeTransform);
activeTransform.origin.set(activeTransform.origin.x, activeTransform.origin.y, 0);
// DOES NOTHING:
//rigidBody.setWorldTransform(activeTransform);
//rigidBody.getMotionState().setWorldTransform(activeTransform);
//rigidBody.predictIntegratedTransform(1, activeTransform);
//rigidBody.predictIntegratedTransform(timeStep, activeTransform);
// CAUSES STRANGE COLLISION BEHAVIOR
rigidBody.setInterpolationWorldTransform(activeTransform);
}
}
};
Q: How to change the active transform of a rigid body correctly within an internal tick callback (and probably at the situation that ridigBodies can overlap before I change the transform)?
thank you for any help or hint!
Hi citizen.cane,
Perhaps setWorldTransform would work from inside the internal tick callback if you cease using MotionStates.
It seems to me that MotionStates are not intended for usage from within then internal step callback.
Details follow.
Concerning predictIntegratedTransform.
I thing predictIntegratedTransform is a Bullet internal function that you should/need not use.
Concerning getMotionState().setWorldTransform.
Directly after internalSingleStepSimulation(),
(from which
internalTickCallback.internalTick(this, timeStep);
is invoked),
synchronizeMotionStates();
is invoked
– and it overwites whatever you’ve written to your motion states.
So I think you cannot use getMotionState().setWorldTransform(activeTransform) from inside the internal tick callback.
Concerning CollisionObject.setWorldTransform.
It seems that before [the first time the actual step is done, and your-internal-tick-callback is invoked] each DynamicsWorld.step,
the CollisionObject.worldTransform is overwritten with the MotionState value (if you use MotionStates).
From inside DiscreteDynamicsWorld.saveKinematicState(…).
So, if you use MotionStates, and invoke CollisionObject.setWorldTransform from inside the internal tick callback, then I think the CollisionObject.setWorldTransform call has no effect (unless many internal ticks happens each DynamicsWorld.step; then, it’d have effects sometimes only), since whatever you’ve written to CollisionObject.worldTransform will be overwirtten by the MotionState’s world transform in the next call to DynamicsWorld.step.
But it’s late now and time to sleep, so I might be wrong.
Regards and sleep well,
Magnus
[quote]org.lwjgl.LWJGLException: Could not choose visual
[/quote]
I got this too. I changed the color depth from 24 to 16 in the LWGL.main() to fix it. I use a linux laptop without a working 3D gfx card so I use the software openGL implementation.
Tom
CollisionDetectionDemo - Please can you help a complete newbie to get this code working
I have tried to implement the bullet CollisionDetectionDemo.cpp as a java demo along the lines of the other jbullet demos. Unfortunately I am new to jbullet, don’t know either c++ or openGL, hence I am stuck!
Here is my code:
/*
Bullet Continuous Collision Detection and Physics Library
Copyright © 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
This software is provided ‘as-is’, without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
- The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
- Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
- This notice may not be removed or altered from any source distribution.
*/
///
/// CollisionInterfaceDemo shows high level usage of the Collision Detection.
///
import com.bulletphysics.collision.shapes.BoxShape;
import javax.vecmath.Quat4f;
import javax.vecmath.Vector3f;
import javax.vecmath.Vector3d;
import javax.vecmath.Matrix3f;
import javax.media.j3d.Transform3D;
import com.bulletphysics.collision.dispatch.CollisionDispatcher;
import com.bulletphysics.collision.dispatch.CollisionWorld;
import com.bulletphysics.collision.dispatch.CollisionObject;
import com.bulletphysics.collision.narrowphase.VoronoiSimplexSolver;
import com.bulletphysics.collision.broadphase.AxisSweep3;
import com.bulletphysics.collision.narrowphase.ManifoldPoint;
import com.bulletphysics.collision.narrowphase.SimplexSolverInterface;
import com.bulletphysics.collision.dispatch.CollisionDispatcher;
import com.bulletphysics.collision.narrowphase.PersistentManifold;
import com.bulletphysics.collision.dispatch.DefaultCollisionConfiguration;
import com.bulletphysics.linearmath.Transform;
import static com.bulletphysics.demos.opengl.IGL.;
import com.bulletphysics.demos.opengl.GLDebugDrawer;
import com.bulletphysics.demos.opengl.IGL;
import com.bulletphysics.demos.opengl.LWJGL;
import org.lwjgl.LWJGLException;
import com.bulletphysics.demos.opengl.DemoApplication;
import com.bulletphysics.demos.opengl.;
public class CollisionInterfaceDemo extends DemoApplication {
float yaw=0.f;
float pitch=0.f;
float roll=0.f;
int maxNumObjects = 4;
int numObjects = 2;
GLDebugDrawer debugDrawer;
CollisionObject[] objects = new CollisionObject[maxNumObjects];
CollisionWorld collisionWorld;
static VoronoiSimplexSolver sGjkSimplexSolver; // ?????????????
SimplexSolverInterface gGjkSimplexSolver = sGjkSimplexSolver; //??????????????
//GL_Simplex1to4 simplex; ???
public CollisionInterfaceDemo(IGL gl){
super(gl);
setDebugMode(8);
}
public static void main(String[] args) throws LWJGLException {
CollisionInterfaceDemo collisionInterfaceDemo = new CollisionInterfaceDemo(LWJGL.getGL());
collisionInterfaceDemo.initPhysics();
collisionInterfaceDemo.clientResetScene();
LWJGL.main(args, 800, 600, "Collision Interface Demo",collisionInterfaceDemo);
}
public void initPhysics(){
//m_debugMode |= btIDebugDraw::DBG_DrawWireframe;
Matrix3f basisA = new Matrix3f();
basisA.setIdentity();
Matrix3f basisB = new Matrix3f();
basisB.setIdentity();
objects[0] = new CollisionObject();
objects[1] = new CollisionObject();
objects[0].getWorldTransform(new Transform()).set(basisA);
objects[1].getWorldTransform(new Transform()).set(basisB);
//btPoint3 points0[3]={btPoint3(1,0,0),btPoint3(0,1,0),btPoint3(0,0,1)};
//btPoint3 points1[5]={btPoint3(1,0,0),btPoint3(0,1,0),btPoint3(0,0,1),btPoint3(0,0,-1),btPoint3(-1,-1,0)};
BoxShape boxA = new BoxShape(new Vector3f(1,1,1));
BoxShape boxB = new BoxShape(new Vector3f(0.5f,0.5f,0.5f));
//ConvexHullShape hullA(points0,3);
//hullA.setLocalScaling(btVector3(3,3,3));
//ConvexHullShape hullB(points1,4);
//hullB.setLocalScaling(btVector3(4,4,4));
objects[0].setCollisionShape(boxA);//&hullA;
objects[1].setCollisionShape(boxB);//&hullB;
DefaultCollisionConfiguration collisionConfiguration = new DefaultCollisionConfiguration();
CollisionDispatcher dispatcher = new CollisionDispatcher(collisionConfiguration);
Vector3f worldAabbMin = new Vector3f(-1000,-1000,-1000);
Vector3f worldAabbMax = new Vector3f(1000,1000,1000);
AxisSweep3 broadphase = new AxisSweep3(worldAabbMin,worldAabbMax);
//SimpleBroadphase is a brute force alternative, performing N^2 aabb overlap tests
//SimpleBroadphase* broadphase = new btSimpleBroadphase;
collisionWorld = new CollisionWorld(dispatcher,broadphase,collisionConfiguration);
collisionWorld.addCollisionObject(objects[0]);
collisionWorld.addCollisionObject(objects[1]);
for (int i=0;i<numObjects;i++){
System.out.println(“Object=” + objects[i]);
System.out.println(“Object collisionShape=” + objects[i].getCollisionShape());
System.out.println(“Object worldTransform=” + objects[i].getWorldTransform(new Transform()));
}
}
//to be implemented by the demo
public void clientMoveAndDisplay(){
displayCallback();
}
public void displayCallback() {
gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
gl.glDisable(GL_LIGHTING);
collisionWorld.setDebugDrawer(new GLDebugDrawer(gl));
if (collisionWorld != null) collisionWorld.performDiscreteCollisionDetection();
///one way to draw all the contact points is iterating over contact manifolds / points:
int numManifolds = collisionWorld.getDispatcher().getNumManifolds();
System.out.println(“numManifolds =” + numManifolds);
for (int i=0;i<numManifolds;i++)
{
PersistentManifold contactManifold = collisionWorld.getDispatcher().getManifoldByIndexInternal(i);
System.out.println(“ContactManifold =” + contactManifold);
CollisionObject obA = (CollisionObject)(contactManifold.getBody0());
CollisionObject obB = (CollisionObject)(contactManifold.getBody1());
contactManifold.refreshContactPoints(obA.getWorldTransform(new Transform()),obB.getWorldTransform(new Transform()));
int numContacts = contactManifold.getNumContacts();
System.out.println(“numContacts =” + numContacts);
for (int j=0;j<numContacts;j++)
{
ManifoldPoint pt = contactManifold.getContactPoint(j);
System.out.println(“Manifold pt=” + pt);
gl.glBegin(GL_LINES);
gl.glColor3f(1, 0, 1);
Vector3f ptA = pt.getPositionWorldOnA(new Vector3f());
Vector3f ptB = pt.getPositionWorldOnB(new Vector3f());
gl.glVertex3f(ptA.x,ptA.y,ptA.z);
gl.glVertex3f(ptB.x,ptB.y,ptB.z);
gl.glEnd();
}
//you can un-comment out this line, and then all points are removed
//contactManifold->clearManifold();
}
//GL_ShapeDrawer::drawCoordSystem();
for (int i=0;i<numObjects;i++){
GLShapeDrawer.drawOpenGL(gl,objects[i].getWorldTransform(new Transform()),objects[i].getCollisionShape(),new Vector3f(1,1,1),getDebugMode());
}
Quat4f orn = new Quat4f();
Transform3D t=new Transform3D();
t.setEuler(new Vector3d(new Vector3f(yaw,pitch,roll)));
t.set(orn);
Vector3f origin1 = objects[1].getWorldTransform(new Transform()).origin;
origin1.add(new Vector3f(0f,-0.01f,0f));
objects[1].getWorldTransform(new Transform()).transform(origin1);
objects[0].getWorldTransform(new Transform()).setRotation(orn);
pitch += 0.005f;
yaw += 0.01f;
//gl.glFlush();
//gl.glutSwapBuffers();
}
public void clientResetScene(){
objects[0].getWorldTransform(new Transform()).transform(new Vector3f(0.0f,3.f,0.f));
objects[1].getWorldTransform(new Transform()).transform(new Vector3f(0.0f,9.f,0.f));
}
}
Hi Jezek2
Any plans to upgrade JBullet to Bullet 2.74?
(That would mean lots of work, I suppose (?))
(Otherwise, current JBullet / Bullet 2.70-beta1 works just fine and seems very stable )
Regards,
Magnus
Hi all,
to improve JBullet development I’ve decided to raise funds using donations (using PayPal). This way I’ll be able to work on JBullet more and catch-up with latest versions provided there will be enough money raised. For more information visit JBullet’s homepage here.
Current plan is to port changes from current 2.70-beta1 to 2.70 final (this includes some classes still on 2.66). The next step will be porting changes from 2.70 to 2.74. This will very likely also include porting of soft bodies feature.
You initial aim (€1000) will probably only be reached when devs absolutely need this work in they day job, and can convince their boss to invest in a risk: you might or might not be able to ‘fix’ it, in the timespan allocated for a specific phase in their project.
Don’t get me wrong, (and I very well might be wrong…), but it might take a few… years?
As a reference, you might want to take a look here: http://lwjgl.org/donations.php - it’s not exactly pouring in, unfortunately.
I certainly hope those generous devs find you and invest in your project!
“The author created this library and is doing the porting for a reason: he uses it in his commercial projects.”
May I ask, are those projects going well? You don’t work with those projects for a living do you? (But on your spare time)
If you have a Web page it would be interesting and fun to read about them.
(But I understand that you might not want to publish information on those projects before they’re finished.)
Best wishes with fund-raising anyway
Yes they’re going well so far (ok by schedule with “development overhead constant” accounted, which is about 2.5x than time previously thought, but as we’re development company and have plenty of experience in other projects we knew this from beginning ) and I do them for living although we have to finance them from other income as they’re in development few years and not really usable until nearly finished.
Currently no public information exist, but it’s game related so I’ll announce it here too when done