B3J brings Box3D to Java with a thin, high-level API.
It is designed to be efficient yet convenient, staying close to the original Box3D API.
jextract bindings are shipped with B3J and can be used directly. You can just use B3J to provide and load the shared libraries and jextract bindings.
Quick start
B3 b3 = B3.get();
try (var region = Region.ofConfined()) {
var worldDef = new WorldDef();
worldDef.gravity().set(0, -10f, 0);
var worldID = b3.createWorld(region, worldDef);
{
var boxDef = new BodyDef();
boxDef.position().set(0, -1f, 0);
var ground = b3.createBody(region, worldID, boxDef);
var hull = b3.makeBoxHull(50.0f, 1.0f, 50.0f);
b3.createHullShape(ground, new ShapeDef(), hull.base());
}
{
var boxDef = new BodyDef();
boxDef.type(BodyType.DYNAMIC);
boxDef.position().set(0, 4f, 0);
var box = b3.createBody(region, worldID, boxDef);
var shapeDef = new ShapeDef();
shapeDef.density(1.0f);
shapeDef.baseMaterial().friction(0.3f);
var hull = b3.makeCubeHull(1f);
b3.createHullShape(box, shapeDef, hull.base());
}
for (int i = 0; i < 90; ++i) {
var timeStep = 1.0f / 60.0f;
var subStepCount = 4;
b3.worldStep(worldID, timeStep, subStepCount);
}
} // calls b3.destroyBody & b3.destroyWorldlwjglis used for rendering with OpenGL > 4.3- Start them via
./gradlew :examples:runor the instance main methods
-
JOML is used for fast math and vector operations.
- A
Matrix4fis used as a rigid transform instead ofb3Transform. Vector3fandQuaternionfare used forb3Vec3andb3Quat,Matrix3fforb3Matrix3.- JOML is already widely used in the Java (game) ecosystem and can offer more than Box3D's math.
- A
-
Similar API to Box3D, but not identical.
- Constructors are used, instead of
b3Default...methods and zero-initialized structs. - All other methods are found on the B3 class.
- Types & Methods are not prefixed with 'b3' and follow Java naming conventions.
- E.g.
b3Body_SetTransformbecomesb3.bodySetTransform.
- E.g.
- UserData and PointerData
can be used as a replacement for
userDatato associate data with Box3D objects. - WorldDef has some differences to
b3WorldDef:- TaskScheduler replaces
enqueueTask,finishTask,userTaskContextandworkerCount. - DebugShapeCallbacks replaces
createDebugShapeanddestroyDebugShape.- It requires a
UserData.OfShape<T>to be passed to store theuserShapefor each shape. - Provide the same
UserData.OfShape<T>to DebugDraw so thedrawShapeFcncan be called with the correctuserShape.
- It requires a
- TaskScheduler replaces
JointIDhas a generic type for theJointType. This is only a hint, so you can usejointId.reinterpret(JointType)to freely reinterpret it.
- Constructors are used, instead of
-
Thread safety
- B3J allocates a small amount of memory upfront for efficient c calls.
B3.get()will give you a thread unique instance.- Dont share a instance of
B3between threads. - If you operate on a single thread, you can just declare a
B3instance as a global once.
- Dont share a instance of
-
Memory Efficient
- B3J allocates as few objects as possible and reuses existing objects.
- Some method require a
destparameter to be filled in. E.g.Vector3f bodyGetPosition(Vector3f dest, BodyID bodyId). In that case, you can just pass in a new object using the default constructor, or reuse a existing one. - Arguments of callbacks or custom Iterators can be mutable. Make sure to copy the values if you need the data beyond the lifetime of the callback or iterator. All mutable objects have a copy constructor that you can use, although it will be cheaper to just copy the fields you need.
- Some arguments can be
nullwhen they are not needed. - Some methods that require callbacks are implemented in c. All arguments are recorded and replayed in java to avoid the overhead of upcalls.
-
Fearless Resource Management
- A Region can be used to manage resources.
BodyID,WorldID,HullData,MeshData,HeightFieldDataandDynamicTreeare managed by regions.- Objects registered to a region will be destroyed when the region is closed.
- Joints will be destroyed when any attached body is destroyed (default Box3D behavior).
- Shapes will be destroyed when the owning body is destroyed (default Box3D behavior).
- So unless a
Regionparameter is required to construct an object, you don't have to worry about lifetimes. - You can destroy all objects manually and fully opt out of region management.
- See Region for more details and available regions.
-
Null Safety
- B3J uses
@Nullableannotations for values that can be null. Otherwise, assume non-null. Dont break this contract! Be aware that@Nullable ShapeID[]means that the elements can be null, not the array itself.
- B3J uses
-
Unsigned values are annotated with
@Unsigned- Make sure you operate with the correct types. Read more
Everything else follows the Box3D API & Documentation.
Make sure to read it to understand how Box3D works.
B3J currently supports Windows and Linux, with support for MacOS planned.
B3J currently builds with Java 25.
Box3D is developed by Erin Catto and uses the MIT license.
This Project also uses the MIT license.
Support the original Box3D through Github Sponsors.
Consider giving this repository a star and dont forget to star the original.
I used LLMs in the following areas:
- Code reviews
- Repetitive, simple function implementations
All other code is developed and written by me and i take responsibility for every line of code.