More Physics with Clojure, JBullet and Processing
Over the weekend, I was playing with the following snippet to get a feel for the JBullet library. JBullet is Java port of Bullet Physics Library. Their wiki contains a sample Hello World Application which covers everything about this snippet so I am not going to copy/paste it here, the only difference is that instead of dropping a sphere onto a surface, this example stacks some boxes on top of each other and shoots them with a sphere. You can grab JBullet from Clojars.
(ns ball.core
(:use [incanter.core :only [view]])
(:use [incanter.processing :exclude [box sphere]])
(:import
(javax.vecmath Vector3f Quat4f)
(com.bulletphysics.dynamics DiscreteDynamicsWorld
RigidBodyConstructionInfo
RigidBody)
(com.bulletphysics.collision.dispatch DefaultCollisionConfiguration
CollisionDispatcher)
(com.bulletphysics.dynamics.constraintsolver
SequentialImpulseConstraintSolver)
(com.bulletphysics.collision.broadphase AxisSweep3)
(com.bulletphysics.linearmath Transform DefaultMotionState)
(com.bulletphysics.collision.shapes StaticPlaneShape
BoxShape
SphereShape)))
(defn world []
(let [maxProxies 1024
worldAabbMin (Vector3f. -10000 -10000 -10000)
worldAabbMax (Vector3f. 10000 10000 10000)
cc (DefaultCollisionConfiguration.)]
(doto (DiscreteDynamicsWorld.
(CollisionDispatcher. cc)
(AxisSweep3. worldAabbMin worldAabbMax maxProxies)
(SequentialImpulseConstraintSolver.)
cc)
(.setGravity (Vector3f. 0 60 0)))))
(defn surface [world]
(let [shape (StaticPlaneShape. (Vector3f. 0 -1 0) 1)
motion-state (DefaultMotionState.
(doto (Transform.)
(-> .origin (.set (Vector3f. 0 330 0)))
(.setRotation (Quat4f. 0 0 0 1))))
construction-info (RigidBodyConstructionInfo.
0 motion-state shape (Vector3f. 0 0 0))
rigid-body (RigidBody. construction-info)]
(.addRigidBody world rigid-body)
rigid-body))
(defn box [world [x y]]
(let [fall-mass 1
fall-inertia (Vector3f. 10 0 0)
shape (doto (BoxShape. (Vector3f. 3 30 15))
(.calculateLocalInertia fall-mass fall-inertia))
motion-state (DefaultMotionState.
(doto (Transform.)
(-> .origin (.set (Vector3f. x y 0)))
(.setRotation (Quat4f. 0 0 0 1))))
construction-info (RigidBodyConstructionInfo.
fall-mass motion-state shape fall-inertia)
rigid-body (RigidBody. construction-info)]
(.addRigidBody world rigid-body)
rigid-body))
(defn sphere [world [x y]]
(let [fall-mass 1
fall-inertia (Vector3f. 10 0 0)
shape (doto (SphereShape. 10.0)
(.calculateLocalInertia fall-mass fall-inertia))
motion-state (DefaultMotionState.
(doto (Transform.)
(-> .origin (.set (Vector3f. x y 0)))
(.setRotation (Quat4f. 0 0 0 1))))
construction-info (RigidBodyConstructionInfo.
fall-mass motion-state shape fall-inertia)
rigid-body (RigidBody. construction-info)]
(.addRigidBody world rigid-body)
rigid-body))
(defn coords [body]
(let [transform (Transform.)]
(-> (.getMotionState body) (.getWorldTransform transform))
[(-> transform .origin .x)
(-> transform .origin .y)
(-> transform .origin .z)
(-> transform .basis .m00)
(-> transform .basis .m01)
(-> transform .basis .m02)
(-> transform .basis .m10)
(-> transform .basis .m11)
(-> transform .basis .m12)
(-> transform .basis .m20)
(-> transform .basis .m21)
(-> transform .basis .m22)]))
(defn draw-body [applet body]
(let [[x y z m00 m01 m02 m10 m11 m12 m20 m21 m22] (coords body)]
(doto applet
(.pushMatrix)
(.translate x y z)
(.applyMatrix m00 m01 m02 0
m10 m11 m12 0
m20 m21 m22 0
0 0 0 1))
(if (instance? BoxShape (.getCollisionShape body))
(.box applet 6 60 30)
(.sphere applet 10))
(.popMatrix applet)))
(defn frame []
(let [fps 24
world (world)
surface (surface world)
bodies (ref (map #(box world %) (for [x (range 200 500 60)
y (range 300 1 -60.1)]
[x y])))]
(sketch
(setup []
(doto this
(size 640 400 processing.core.PConstants/P3D)
(.noStroke)
(framerate fps)
smooth))
(draw []
(.stepSimulation world (/ 1 fps) 8)
(doto this
(.background 50)
(.lights))
(doseq [body @bodies]
(draw-body this body)))
(mousePressed [e]
(let [cords [(.mouseX this) (.mouseY this)]
sphere (sphere world cords)]
(.setLinearVelocity sphere (Vector3f. -2000 0 0))
(dosync (alter bodies conj sphere)))))))
;;(view (frame) :size [640 400])