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])