Skip to content

Repository files navigation

B3J: Box3D for Java

License: MIT

Box3D Logo

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.destroyWorld
  • lwjgl is used for rendering with OpenGL > 4.3
  • Start them via ./gradlew :examples:run or the instance main methods

B3J vs Box3D

  • JOML is used for fast math and vector operations.

    • A Matrix4f is used as a rigid transform instead of b3Transform.
    • Vector3f and Quaternionf are used for b3Vec3 and b3Quat, Matrix3f for b3Matrix3.
    • JOML is already widely used in the Java (game) ecosystem and can offer more than Box3D's math.
  • 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_SetTransform becomes b3.bodySetTransform.
    • UserData and PointerData can be used as a replacement for userData to associate data with Box3D objects.
    • WorldDef has some differences to b3WorldDef:
      • TaskScheduler replaces enqueueTask, finishTask, userTaskContext and workerCount.
      • DebugShapeCallbacks replaces createDebugShape and destroyDebugShape.
        • It requires a UserData.OfShape<T> to be passed to store the userShape for each shape.
        • Provide the same UserData.OfShape<T> to DebugDraw so the drawShapeFcn can be called with the correct userShape.
    • JointID has a generic type for the JointType. This is only a hint, so you can use jointId.reinterpret(JointType) to freely reinterpret it.
  • 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 B3 between threads.
      • If you operate on a single thread, you can just declare a B3 instance as a global once.
  • Memory Efficient

    • B3J allocates as few objects as possible and reuses existing objects.
    • Some method require a dest parameter 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 null when 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, HeightFieldData and DynamicTree are 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 Region parameter 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 @Nullable annotations 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.
  • 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.

Compatibility

B3J currently supports Windows and Linux, with support for MacOS planned.

B3J currently builds with Java 25.

License

Box3D is developed by Erin Catto and uses the MIT license.

This Project also uses the MIT license.

Sponsorship

Support the original Box3D through Github Sponsors.

Consider giving this repository a star and dont forget to star the original.

LLM Usage

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.

About

Handwritten Java bindings for Box3D

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages