changeset 23:cab2da252494

split off the rest of cortex.org
author Robert McIntyre <rlm@mit.edu>
date Sun, 23 Oct 2011 23:54:26 -0700
parents 157b416152ea
children e965675ec4d0
files org/cortex.org org/eyes.org org/util.org org/world.org
diffstat 4 files changed, 663 insertions(+), 404 deletions(-) [+]
line wrap: on
line diff
     1.1 --- a/org/cortex.org	Sun Oct 23 23:35:04 2011 -0700
     1.2 +++ b/org/cortex.org	Sun Oct 23 23:54:26 2011 -0700
     1.3 @@ -9,414 +9,12 @@
     1.4  
     1.5  * Simulation Base
     1.6    
     1.7 -** Imports
     1.8 -jMonkeyEngine has a plethora of classes which can be overwhelming at
     1.9 -first. So that I one can get right to coding, it's good to take the
    1.10 -time right now and make a "import all" function which brings in all of
    1.11 -the important jme3 classes. Once I'm happy with the general structure
    1.12 -of a namespace I can deal with importing only the classes it actually
    1.13 -needs.
    1.14 -
    1.15 -#+srcname: import
    1.16 -#+begin_src clojure :results silent
    1.17 -(ns cortex.import
    1.18 -  (:require swank.util.class-browse))
    1.19 -
    1.20 -(defn permissive-import
    1.21 -  [classname]
    1.22 -  (eval `(try (import '~classname)
    1.23 -              (catch java.lang.Exception e#
    1.24 -                (println "couldn't import " '~classname))))
    1.25 -  classname)
    1.26 -
    1.27 -(defn jme-class? [classname]
    1.28 -  (and
    1.29 -   (.startsWith classname "com.jme3.")
    1.30 -   ;; Don't import the Lwjgl stuff since it can throw exceptions
    1.31 -   ;;  upon being loaded.
    1.32 -   (not (re-matches #".*Lwjgl.*" classname))))
    1.33 -
    1.34 -(defn jme-classes
    1.35 -  "returns a list of all jme3 classes"
    1.36 -  []
    1.37 -  (filter
    1.38 -   jme-class?
    1.39 -   (map :name
    1.40 -        swank.util.class-browse/available-classes)))
    1.41 -  
    1.42 -(defn mega-import-jme3
    1.43 -  "Import ALL the jme classes. For REPL use."
    1.44 -  []
    1.45 -  (doall
    1.46 -   (map (comp permissive-import symbol) (jme-classes))))
    1.47 -#+end_src  
    1.48 -
    1.49 -The =mega-import-jme3= is quite usefull for debugging purposes since
    1.50 -it allows completion for almost all of JME's classes.
    1.51 -
    1.52 -Out of curiousity, let's see just how many classes =mega-import-jme3=
    1.53 -imports:
    1.54 -
    1.55 -#+begin_src clojure :exports both
    1.56 -(clojure.core/count (cortex.import/jme-classes))
    1.57 -#+end_src
    1.58 -
    1.59 -#+results:
    1.60 -: 955
    1.61 -
    1.62 -** Simplification
    1.63 -*** World
    1.64 -
    1.65 -It is comvienent to wrap the JME elements that deal with creating a
    1.66 -world, creation of basic objects, and Keyboard input with a nicer
    1.67 -interface (at least for my purposes).
    1.68 -
    1.69 -#+srcname: world-inputs
    1.70 -#+begin_src clojure :results silent
    1.71 -(ns cortex.world)
    1.72 -(require 'cortex.import)
    1.73 -(use 'clojure.contrib.def)
    1.74 -(rlm.rlm-commands/help)
    1.75 -(cortex.import/mega-import-jme3)
    1.76 -
    1.77 -(defvar *app-settings*
    1.78 -  (doto (AppSettings. true)
    1.79 -    (.setFullscreen false)
    1.80 -    (.setTitle "Aurellem.")
    1.81 -    ;; disable 32 bit stuff for now
    1.82 -    ;;(.setAudioRenderer "Send")
    1.83 -    )
    1.84 -  "These settings control how the game is displayed on the screen for
    1.85 -   debugging purposes.  Use binding forms to change this if desired.
    1.86 -   Full-screen mode does not work on some computers.")
    1.87 -
    1.88 -(defn asset-manager
    1.89 -  "returns a new, configured assetManager" []
    1.90 -  (JmeSystem/newAssetManager
    1.91 -   (.getResource
    1.92 -    (.getContextClassLoader (Thread/currentThread))
    1.93 -    "com/jme3/asset/Desktop.cfg")))
    1.94 -
    1.95 -(defmacro no-exceptions
    1.96 -  "Sweet relief like I never knew."
    1.97 -  [& forms]
    1.98 -  `(try ~@forms (catch Exception e# (.printStackTrace e#))))
    1.99 -
   1.100 -(defn thread-exception-removal []
   1.101 -  (println "removing exceptions from " (Thread/currentThread))
   1.102 -  (.setUncaughtExceptionHandler
   1.103 -   (Thread/currentThread)
   1.104 -   (proxy [Thread$UncaughtExceptionHandler] []
   1.105 -     (uncaughtException
   1.106 -      [thread thrown]
   1.107 -      (println "uncaught-exception thrown in " thread)
   1.108 -      (println (.getMessage thrown))))))
   1.109 -
   1.110 -(def println-repl (bound-fn [& args] (apply println args)))
   1.111 -
   1.112 -(use '[pokemon [lpsolve :only [constant-map]]])
   1.113 -
   1.114 -(defn no-op [& _])
   1.115 -
   1.116 -(defn all-keys
   1.117 -  "Construct a map of strings representing all the manual inputs from
   1.118 -   either the keyboard or mouse." 
   1.119 -  []
   1.120 -  (let [inputs (constant-map KeyInput)]
   1.121 -    (assoc
   1.122 -	(zipmap (map (fn [field]
   1.123 -		       (.toLowerCase (re-gsub #"_" "-" field))) (vals inputs))
   1.124 -		(map (fn [val] (KeyTrigger. val)) (keys inputs)))
   1.125 -      ;;explicitly add mouse controls
   1.126 -      "mouse-left" (MouseButtonTrigger. 0)
   1.127 -      "mouse-middle" (MouseButtonTrigger. 2)
   1.128 -      "mouse-right" (MouseButtonTrigger. 1))))
   1.129 -
   1.130 -(defn initialize-inputs
   1.131 -  "more java-interop cruft to establish keybindings for a particular virtual world"
   1.132 -  [game  input-manager key-map]
   1.133 -  (doall (map (fn [[name trigger]]
   1.134 -		(.addMapping ^InputManager input-manager
   1.135 -			     name (into-array (class trigger) [trigger]))) key-map))
   1.136 -  (doall (map (fn [name] 
   1.137 -		(.addListener ^InputManager input-manager game
   1.138 -			      (into-array String  [name]))) (keys key-map))))
   1.139 -
   1.140 -#+end_src
   1.141 -
   1.142 -These functions are all for debug controlling of the world through
   1.143 -keyboard and mouse. 
   1.144 -
   1.145 -We reuse  =constant-map= from =pokemon.lpsolve= to get the numerical
   1.146 -values for all the keys defined in the =KeyInput= class. The
   1.147 -documentation for =constant-map= is:
   1.148 -
   1.149 -#+begin_src clojure :results output
   1.150 -(doc pokemon.lpsolve/constant-map)
   1.151 -#+end_src
   1.152 -
   1.153 -#+results:
   1.154 -: -------------------------
   1.155 -: pokemon.lpsolve/constant-map
   1.156 -: ([class])
   1.157 -:   Takes a class and creates a map of the static constant integer
   1.158 -:   fields with their names.  This helps with C wrappers where they have
   1.159 -:   just defined a bunch of integer constants instead of enums
   1.160 -
   1.161 -
   1.162 -Then, =all-keys= converts the constant names like =KEY_J= to the more
   1.163 -clojure-like =key-j=, and returns a map from these keys to
   1.164 -jMonkeyEngine KeyTrigger objects, the use of which will soon become
   1.165 -apparent. =all-keys= also adds the three mouse button controls to the
   1.166 -map.
   1.167 -
   1.168 -#+srcname: world
   1.169 -#+begin_src clojure :results silent
   1.170 -(in-ns 'cortex.world)
   1.171 -
   1.172 -(defn traverse
   1.173 -  "apply f to every non-node, deeply"
   1.174 -  [f node]
   1.175 -  (if (isa? (class node) Node)
   1.176 -    (dorun (map (partial traverse f) (.getChildren node)))
   1.177 -    (f node)))
   1.178 -
   1.179 -(def gravity (Vector3f. 0 -9.81 0))
   1.180 -
   1.181 -(defn world
   1.182 -  [root-node key-map setup-fn update-fn]
   1.183 -  (let [physics-manager (BulletAppState.)
   1.184 -	shadow-renderer (BasicShadowRenderer. (asset-manager) (int 256))
   1.185 -	;;maybe use a better shadow renderer someday!
   1.186 -	;;shadow-renderer (PssmShadowRenderer. (asset-manager) 256 1)
   1.187 -	]   	
   1.188 -  (doto
   1.189 -      (proxy [SimpleApplication ActionListener] []
   1.190 -	(simpleInitApp
   1.191 -	 []
   1.192 -	 (no-exceptions
   1.193 -	  (.setTimer this (IsoTimer. 60))
   1.194 -	  ;; Create key-map.
   1.195 -	  (.setFrustumFar (.getCamera this) 300)
   1.196 -	  (initialize-inputs this (.getInputManager this) (all-keys))
   1.197 -	  ;; Don't take control of the mouse
   1.198 -	  (org.lwjgl.input.Mouse/setGrabbed false)
   1.199 -	  ;; add all objects to the world
   1.200 -	  (.attachChild (.getRootNode this) root-node)
   1.201 -	  ;; enable physics
   1.202 -	  ;; add a physics manager
   1.203 -	  (.attach (.getStateManager this) physics-manager)
   1.204 -	  (.setGravity (.getPhysicsSpace physics-manager) gravity)
   1.205 -
   1.206 -
   1.207 -	  ;; go through every object and add it to the physics manager
   1.208 -	  ;; if relavant.
   1.209 -	   (traverse (fn [geom]
   1.210 -		       (dorun
   1.211 -			(for [n (range (.getNumControls geom))]
   1.212 -			  (do
   1.213 -			    (println-repl "adding control " (.getControl geom n))
   1.214 -			    (.add (.getPhysicsSpace physics-manager)
   1.215 -				  (.getControl geom n))))))
   1.216 -		     (.getRootNode this))
   1.217 -	  ;;(.addAll (.getPhysicsSpace physics-manager) (.getRootNode this))
   1.218 -
   1.219 -	  (setup-fn this)
   1.220 -	  (.setDirection shadow-renderer
   1.221 -			 (.normalizeLocal (Vector3f. -1 -1 -1)))
   1.222 -	  (.addProcessor (.getViewPort this) shadow-renderer)
   1.223 -	  (.setShadowMode (.getRootNode this) RenderQueue$ShadowMode/Off)
   1.224 -	  ))
   1.225 -	(simpleUpdate
   1.226 -	 [tpf]
   1.227 -	 (no-exceptions
   1.228 -	  (update-fn this tpf))) 
   1.229 -	(onAction
   1.230 -	 [binding value tpf]
   1.231 -	 ;; whenever a key is pressed, call the function returned from
   1.232 -	 ;; key-map.
   1.233 -	 (no-exceptions
   1.234 -	  (if-let [react (key-map binding)]
   1.235 -	    (react this value)))))
   1.236 -    ;; don't show a menu to change options.
   1.237 -    
   1.238 -    (.setShowSettings false)
   1.239 -    (.setPauseOnLostFocus false)
   1.240 -    (.setSettings *app-settings*))))
   1.241 -
   1.242 -(defn apply-map
   1.243 -  "Like apply, but works for maps and functions that expect an implicit map
   1.244 -   and nothing else as in (fn [& {}]).
   1.245 -------- Example -------
   1.246 - (defn jjj [& {:keys [www] :or {www \"oh yeah\"} :as env}] (println www))
   1.247 - (apply-map jjj {:www \"whatever\"})
   1.248 -  -->\"whatever\""
   1.249 -  [fn m]
   1.250 -  (apply fn (reduce #(into %1 %2) [] m)))
   1.251 -
   1.252 -#+end_src 
   1.253 -
   1.254 -
   1.255 -=world= is the most important function here.  
   1.256 -***  TODO more documentation
   1.257 -
   1.258 -#+srcname: world-shapes
   1.259 -#+begin_src clojure :results silent
   1.260 -(in-ns 'cortex.world)
   1.261 -(defrecord shape-description
   1.262 -  [name
   1.263 -   color
   1.264 -   mass
   1.265 -   friction
   1.266 -   texture
   1.267 -   material
   1.268 -   position
   1.269 -   rotation
   1.270 -   shape
   1.271 -   physical?])
   1.272 -
   1.273 -(def base-shape
   1.274 -     (shape-description.
   1.275 -      "default-shape"
   1.276 -      false
   1.277 -      ;;ColorRGBA/Blue
   1.278 -      1.0 ;; mass
   1.279 -      1.0 ;; friction
   1.280 -      ;; texture
   1.281 -      "Textures/Terrain/BrickWall/BrickWall.jpg"
   1.282 -      ;; material
   1.283 -      "Common/MatDefs/Misc/Unshaded.j3md"
   1.284 -      Vector3f/ZERO
   1.285 -      Quaternion/IDENTITY
   1.286 -      (Box. Vector3f/ZERO 0.5 0.5 0.5)
   1.287 -      true))
   1.288 -
   1.289 -(defn make-shape
   1.290 -  [#^shape-description d]
   1.291 -  (let [asset-manager (if (:asset-manager d) (:asset-manager d) (asset-manager))
   1.292 -        mat (Material. asset-manager (:material d))
   1.293 -	geom (Geometry. (:name d) (:shape d))]
   1.294 -    (if (:texture d)
   1.295 -      (let [key (TextureKey. (:texture d))]
   1.296 -	(.setGenerateMips key true)
   1.297 -	(.setTexture mat "ColorMap" (.loadTexture asset-manager key))))
   1.298 -    (if (:color d) (.setColor mat "Color" (:color d)))
   1.299 -    (.setMaterial geom mat)
   1.300 -    (if-let [rotation (:rotation d)] (.rotate geom rotation))
   1.301 -    (.setLocalTranslation geom (:position d))
   1.302 -    (if (:physical? d)
   1.303 -      (let [impact-shape (doto (GImpactCollisionShape. 
   1.304 -                                (.getMesh geom)) (.setMargin 0))
   1.305 -	    physics-control (RigidBodyControl.
   1.306 -			     ;;impact-shape ;; comment to disable 
   1.307 -			     (float (:mass d)))]
   1.308 -	(.createJmeMesh impact-shape)
   1.309 -	(.addControl geom physics-control)
   1.310 -	;;(.setSleepingThresholds physics-control (float 0) (float 0))
   1.311 -	(.setFriction physics-control (:friction d))))
   1.312 -    ;;the default is to keep this node in the physics engine forever.
   1.313 -    ;;these commands must come after the control is added to the geometry.
   1.314 -    ;;
   1.315 -    geom))
   1.316 -  
   1.317 -(defn box
   1.318 -  ([l w h & {:as options}]
   1.319 -     (let [options (merge base-shape options)]
   1.320 -       (make-shape (assoc options
   1.321 -		     :shape (Box. l w h)))))
   1.322 -  ([] (box 0.5 0.5 0.5)))
   1.323 -
   1.324 -(defn sphere
   1.325 -  ([r & {:as options}]
   1.326 -     (let [options (merge base-shape options)]
   1.327 -       (make-shape (assoc options
   1.328 -		     :shape (Sphere. 32 32 (float r)))))) 
   1.329 -  ([] (sphere 0.5)))
   1.330 -
   1.331 -(defn add-element
   1.332 -  ([game element node]
   1.333 -  (.addAll
   1.334 -   (.getPhysicsSpace
   1.335 -    (.getState
   1.336 -     (.getStateManager game)
   1.337 -     BulletAppState))
   1.338 -    element)
   1.339 -  (.attachChild node element))
   1.340 -  ([game element]
   1.341 -     (add-element game element (.getRootNode game))))
   1.342 -
   1.343 -
   1.344 -(defn set-gravity*
   1.345 -  [game gravity]
   1.346 -  (traverse
   1.347 -   (fn [geom]
   1.348 -     (if-let
   1.349 -	 [control (.getControl geom RigidBodyControl)]
   1.350 -       (do
   1.351 -	 (.setGravity control gravity)
   1.352 -	 (.applyImpulse control Vector3f/ZERO Vector3f/ZERO)
   1.353 -	 )))
   1.354 -   (.getRootNode game)))
   1.355 -
   1.356 -#+end_src
   1.357 -
   1.358 -These are convienence functions for creating JME objects and
   1.359 -manipulating a world.
   1.360 -
   1.361 -#+srcname: world-view
   1.362 -#+begin_src clojure :results silent
   1.363 -(in-ns 'cortex.world)
   1.364 -
   1.365 -(defprotocol Viewable
   1.366 -  (view [something]))
   1.367 -
   1.368 -(extend-type com.jme3.scene.Geometry
   1.369 -  Viewable
   1.370 -  (view [geo]
   1.371 -	(view (doto (Node.)(.attachChild geo)))))
   1.372 -
   1.373 -(extend-type com.jme3.scene.Node
   1.374 -  Viewable
   1.375 -  (view [node]
   1.376 -	(.start
   1.377 -	 (world node
   1.378 -		{}
   1.379 -		(fn [world]
   1.380 -		  (.enableDebug 
   1.381 -		   (.getPhysicsSpace
   1.382 -		    (.getState
   1.383 -		     (.getStateManager world)
   1.384 -		     BulletAppState))
   1.385 -		   (asset-manager))
   1.386 -		  (set-gravity* world Vector3f/ZERO)
   1.387 -;;		  (set-gravity* world (Vector3f. 0 (float -0.4) 0))
   1.388 -		  (let [sun (doto (DirectionalLight.)
   1.389 -			      (.setDirection (.normalizeLocal (Vector3f. 1 0 -2)))
   1.390 -			      (.setColor ColorRGBA/White))]
   1.391 -		    (.addLight (.getRootNode world) sun)))
   1.392 -		no-op))))
   1.393 -
   1.394 -(defn position-camera [game]
   1.395 -  (doto (.getCamera game)
   1.396 -    (.setLocation (Vector3f. 0 6 6))
   1.397 -    (.lookAt Vector3f/ZERO (Vector3f. 0 1 0))))
   1.398 -
   1.399 -#+end_src
   1.400 -
   1.401 -Here I make the =Viewable= protocol and extend it to JME's types.  Now
   1.402 -hello-world can be written as easily as:
   1.403 -
   1.404 -#+begin_src clojure :results silent
   1.405 -(cortex.world/view (cortex.world/box))
   1.406 -#+end_src
   1.407  
   1.408  ** Hello
   1.409  Here are the jmonkeyengine "Hello" programs translated to clojure.
   1.410  *** Hello Simple App
   1.411 -Here is the hello world example for jme3 in clojure.
   1.412 -It's a more or less direct translation from the java source
   1.413 -from
   1.414 -http://jmonkeyengine.org/wiki/doku.php/jme3:beginner:hello_simpleapplication.
   1.415 +Here is the hello world example for jme3 in clojure.  It's a more or
   1.416 +less direct translation from the java source [[http://jmonkeyengine.org/wiki/doku.php/jme3:beginner:hello_simpleapplication][here]].
   1.417  
   1.418  Of note is the fact that since we don't have access to the
   1.419  =AssetManager= via extendig =SimpleApplication=, we have to build one
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/org/eyes.org	Sun Oct 23 23:54:26 2011 -0700
     2.3 @@ -0,0 +1,243 @@
     2.4 +#+title: Eyes
     2.5 +#+author: Robert McIntyre
     2.6 +#+email: rlm@mit.edu
     2.7 +#+description: Simulating senses for AI research using JMonkeyEngine3
     2.8 +#+SETUPFILE: ../../aurellem/org/setup.org
     2.9 +#+INCLUDE: ../../aurellem/org/level-0.org
    2.10 +#+babel: :mkdirp yes :noweb yes :exports both
    2.11 +
    2.12 +
    2.13 +
    2.14 +** Eyes
    2.15 +
    2.16 +Ultimately I want to make creatures with eyes. Each eye can be
    2.17 +independely moved and should see its own version of the world
    2.18 +depending on where it is.
    2.19 +#+srcname: eyes
    2.20 +#+begin_src clojure 
    2.21 +(ns body.eye)
    2.22 +(use 'cortex.world)
    2.23 +(use 'cortex.import)
    2.24 +(use 'clojure.contrib.def)
    2.25 +(cortex.import/mega-import-jme3)
    2.26 +(rlm.rlm-commands/help)
    2.27 +(import java.nio.ByteBuffer)
    2.28 +(import java.awt.image.BufferedImage)
    2.29 +(import java.awt.Color)
    2.30 +(import java.awt.Dimension)
    2.31 +(import java.awt.Graphics)
    2.32 +(import java.awt.Graphics2D)
    2.33 +(import java.awt.event.WindowAdapter)
    2.34 +(import java.awt.event.WindowEvent)
    2.35 +(import java.awt.image.BufferedImage)
    2.36 +(import java.nio.ByteBuffer)
    2.37 +(import javax.swing.JFrame)
    2.38 +(import javax.swing.JPanel)
    2.39 +(import javax.swing.SwingUtilities)
    2.40 +(import javax.swing.ImageIcon)
    2.41 +(import javax.swing.JOptionPane)
    2.42 +(import java.awt.image.ImageObserver)
    2.43 +
    2.44 +
    2.45 +
    2.46 +(defn scene-processor
    2.47 +  "deals with converting FrameBuffers to BufferedImages so
    2.48 +   that the continuation function can be defined only in terms
    2.49 +   of what it does with BufferedImages"
    2.50 +  [continuation]
    2.51 +  (let [byte-buffer (atom nil)
    2.52 +	renderer (atom nil)
    2.53 +	image (atom nil)]
    2.54 +  (proxy [SceneProcessor] []
    2.55 +    (initialize
    2.56 +     [renderManager viewPort]
    2.57 +     (let [cam (.getCamera viewPort)
    2.58 +	   width (.getWidth cam)
    2.59 +	   height (.getHeight cam)]
    2.60 +       (reset! renderer (.getRenderer renderManager))
    2.61 +       (reset! byte-buffer
    2.62 +	     (BufferUtils/createByteBuffer
    2.63 +	      (* width height 4)))
    2.64 +       (reset! image (BufferedImage. width height
    2.65 +				     BufferedImage/TYPE_4BYTE_ABGR))))
    2.66 +    (isInitialized [] (not (nil? @byte-buffer)))
    2.67 +    (reshape [_ _ _])
    2.68 +    (preFrame [_])
    2.69 +    (postQueue [_])
    2.70 +    (postFrame
    2.71 +     [#^FrameBuffer fb]
    2.72 +     (.clear @byte-buffer)
    2.73 +     (.readFrameBuffer @renderer fb @byte-buffer)
    2.74 +     (Screenshots/convertScreenShot @byte-buffer @image)
    2.75 +     (continuation @image))
    2.76 +    (cleanup []))))
    2.77 +    
    2.78 +(defn add-eye
    2.79 +  "Add an eye to the world, and call continuation on
    2.80 +   every frame produced"
    2.81 +  [world camera continuation]
    2.82 +  (let [width (.getWidth camera)
    2.83 +	height (.getHeight camera)
    2.84 +	render-manager (.getRenderManager world)
    2.85 +	viewport (.createMainView render-manager "eye-view" camera)]
    2.86 +    (doto viewport
    2.87 +      (.setBackgroundColor ColorRGBA/Black)
    2.88 +      (.setClearFlags true true true)
    2.89 +      (.addProcessor (scene-processor continuation))
    2.90 +      (.attachScene (.getRootNode world)))))
    2.91 +
    2.92 +(defn make-display-frame [display width height]
    2.93 +  (SwingUtilities/invokeLater
    2.94 +   (fn []
    2.95 +     (.setPreferredSize display (Dimension. width height))
    2.96 +     (doto (JFrame. "Eye Camera!")
    2.97 +       (-> (.getContentPane) (.add display))
    2.98 +       (.setDefaultCloseOperation JFrame/DISPOSE_ON_CLOSE)
    2.99 +       (.pack)
   2.100 +       (.setLocationRelativeTo nil)
   2.101 +       (.setResizable false)
   2.102 +       (.setVisible true)))))
   2.103 +	   
   2.104 +(defn image-monitor [#^BufferedImage image]
   2.105 +  (proxy [JPanel] []
   2.106 +    (paintComponent 
   2.107 +     [g]
   2.108 +     (proxy-super paintComponent g)
   2.109 +     (locking image
   2.110 +       (.drawImage g image 0 0
   2.111 +		   (proxy [ImageObserver]
   2.112 +		       []
   2.113 +		     (imageUpdate
   2.114 +		      []
   2.115 +		      (proxy-super imageUpdate))))))))
   2.116 +
   2.117 +(defn movie-image []
   2.118 +  (let [setup
   2.119 +	(runonce
   2.120 +	 (fn [#^BufferedImage image]
   2.121 +	   (let [width (.getWidth image)
   2.122 +		 height (.getHeight image)
   2.123 +		 display (image-monitor image)
   2.124 +		 frame (make-display-frame display width height)]
   2.125 +	     display)))]
   2.126 +    (fn [#^BufferedImage image]
   2.127 +      (.repaint (setup image)))))
   2.128 +	     
   2.129 +
   2.130 +(defn observer
   2.131 +  "place thy eye!"
   2.132 +  [world camera]
   2.133 +  (let [eye camera
   2.134 +	width (.getWidth eye)
   2.135 +	height (.getHeight eye)]
   2.136 +    (no-exceptions
   2.137 +     (add-eye
   2.138 +      world
   2.139 +      eye
   2.140 +      (movie-image)))))
   2.141 +#+end_src
   2.142 +
   2.143 +#+srcname: test-vision
   2.144 +#+begin_src clojure
   2.145 +
   2.146 +(ns test.vision)
   2.147 +(use 'cortex.world)
   2.148 +(use 'cortex.import)
   2.149 +(use 'clojure.contrib.def)
   2.150 +(use 'body.eye)
   2.151 +(cortex.import/mega-import-jme3)
   2.152 +(rlm.rlm-commands/help)
   2.153 +(import java.nio.ByteBuffer)
   2.154 +(import java.awt.image.BufferedImage)
   2.155 +(import java.awt.Color)
   2.156 +(import java.awt.Dimension)
   2.157 +(import java.awt.Graphics)
   2.158 +(import java.awt.Graphics2D)
   2.159 +(import java.awt.event.WindowAdapter)
   2.160 +(import java.awt.event.WindowEvent)
   2.161 +(import java.awt.image.BufferedImage)
   2.162 +(import java.nio.ByteBuffer)
   2.163 +(import javax.swing.JFrame)
   2.164 +(import javax.swing.JPanel)
   2.165 +(import javax.swing.SwingUtilities)
   2.166 +(import javax.swing.ImageIcon)
   2.167 +(import javax.swing.JOptionPane)
   2.168 +(import java.awt.image.ImageObserver)
   2.169 +
   2.170 +
   2.171 +(def width 200)
   2.172 +(def height 200)
   2.173 +
   2.174 +(defn camera []
   2.175 +  (doto (Camera. width height)
   2.176 +    (.setFrustumPerspective 45 1 1 1000)
   2.177 +    (.setLocation (Vector3f. -3 0 -5))
   2.178 +    (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)))
   2.179 +
   2.180 +(defn camera2 []
   2.181 +  (doto (Camera. width height)
   2.182 +    (.setFrustumPerspective 45 1 1 1000)
   2.183 +    (.setLocation (Vector3f. 3 0 -5))
   2.184 +    (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)))
   2.185 +
   2.186 +(defn setup-fn [world]
   2.187 +  (let [eye (camera)
   2.188 +	width (.getWidth eye)
   2.189 +	height (.getHeight eye)]
   2.190 +    (no-exceptions
   2.191 +     (add-eye
   2.192 +      world
   2.193 +      eye
   2.194 +      (runonce visual))
   2.195 +     (add-eye
   2.196 +      world
   2.197 +      (camera2)
   2.198 +      (runonce visual)))))
   2.199 +
   2.200 +(defn spider-eye [position]
   2.201 +  (doto (Camera. 200 200 )
   2.202 +    (.setFrustumPerspective 45 1 1 1000)
   2.203 +    (.setLocation position)
   2.204 +    (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)))
   2.205 +  
   2.206 +(defn setup-fn* [world]
   2.207 +  (let [eye (camera)
   2.208 +	width (.getWidth eye)
   2.209 +	height (.getHeight eye)]
   2.210 +    ;;(.setClearFlags (.getViewPort world) true true true)
   2.211 +    (observer world (.getCamera world))
   2.212 +    (observer world (spider-eye (Vector3f.  3  0 -5)))
   2.213 +    ;;(observer world (spider-eye (Vector3f.  0  0 -5)))
   2.214 +    ;; (observer world (spider-eye (Vector3f. -3  0 -5)))
   2.215 +    ;; (observer world (spider-eye (Vector3f.  0  3 -5)))
   2.216 +    ;; (observer world (spider-eye (Vector3f.  0 -3 -5)))
   2.217 +    ;; (observer world (spider-eye (Vector3f.  3  3 -5)))
   2.218 +    ;; (observer world (spider-eye (Vector3f. -3  3 -5)))
   2.219 +    ;; (observer world (spider-eye (Vector3f.  3 -3 -5)))
   2.220 +    ;; (observer world (spider-eye (Vector3f. -3 -3 -5)))
   2.221 +
   2.222 +    )
   2.223 +  world)
   2.224 +
   2.225 +(defn test-world []
   2.226 +  (let [thing (box 1 1 1 :physical? false)]
   2.227 +    (world
   2.228 +     (doto (Node.)
   2.229 +       (.attachChild thing))
   2.230 +     {}
   2.231 +     setup-fn
   2.232 +     (fn [world tpf]
   2.233 +       (.rotate thing (* tpf 0.2) 0 0)
   2.234 +       ))))
   2.235 +
   2.236 +
   2.237 +#+end_src
   2.238 +
   2.239 +
   2.240 +#+results: eyes
   2.241 +: #'body.eye/test-world
   2.242 +
   2.243 +Note the use of continuation passing style for connecting the eye to a
   2.244 +function to process the output. The example code will create two
   2.245 +videos of the same rotating cube from different angles, sutiable for
   2.246 +stereoscopic vision.
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/org/util.org	Sun Oct 23 23:54:26 2011 -0700
     3.3 @@ -0,0 +1,110 @@
     3.4 +#+title: Helper Utilities
     3.5 +#+author: Robert McIntyre
     3.6 +#+email: rlm@mit.edu
     3.7 +#+description: Simulating senses for AI research using JMonkeyEngine3
     3.8 +#+SETUPFILE: ../../aurellem/org/setup.org
     3.9 +#+INCLUDE: ../../aurellem/org/level-0.org
    3.10 +#+babel: :mkdirp yes :noweb yes :exports both
    3.11 +
    3.12 +** Imports
    3.13 +jMonkeyEngine has a plethora of classes which can be overwhelming at
    3.14 +first. So that I one can get right to coding, it's good to take the
    3.15 +time right now and make a "import all" function which brings in all of
    3.16 +the important jme3 classes. Once I'm happy with the general structure
    3.17 +of a namespace I can deal with importing only the classes it actually
    3.18 +needs.
    3.19 +
    3.20 +#+srcname: import
    3.21 +#+begin_src clojure :results silent
    3.22 +(ns cortex.import
    3.23 +  (:require swank.util.class-browse))
    3.24 +
    3.25 +(defn permissive-import
    3.26 +  [classname]
    3.27 +  (eval `(try (import '~classname)
    3.28 +              (catch java.lang.Exception e#
    3.29 +                (println "couldn't import " '~classname))))
    3.30 +  classname)
    3.31 +
    3.32 +(defn jme-class? [classname]
    3.33 +  (and
    3.34 +   (.startsWith classname "com.jme3.")
    3.35 +   ;; Don't import the Lwjgl stuff since it can throw exceptions
    3.36 +   ;;  upon being loaded.
    3.37 +   (not (re-matches #".*Lwjgl.*" classname))))
    3.38 +
    3.39 +(defn jme-classes
    3.40 +  "returns a list of all jme3 classes"
    3.41 +  []
    3.42 +  (filter
    3.43 +   jme-class?
    3.44 +   (map :name
    3.45 +        swank.util.class-browse/available-classes)))
    3.46 +  
    3.47 +(defn mega-import-jme3
    3.48 +  "Import ALL the jme classes. For REPL use."
    3.49 +  []
    3.50 +  (doall
    3.51 +   (map (comp permissive-import symbol) (jme-classes))))
    3.52 +#+end_src  
    3.53 +
    3.54 +The =mega-import-jme3= is quite usefull for debugging purposes since
    3.55 +it allows completion for almost all of JME's classes.
    3.56 +
    3.57 +Out of curiousity, let's see just how many classes =mega-import-jme3=
    3.58 +imports:
    3.59 +
    3.60 +#+begin_src clojure :exports both
    3.61 +(clojure.core/count (cortex.import/jme-classes))
    3.62 +#+end_src
    3.63 +
    3.64 +#+results:
    3.65 +: 955
    3.66 +
    3.67 +** Simplification
    3.68 +#+srcname: world-view
    3.69 +#+begin_src clojure :results silent
    3.70 +(in-ns 'cortex.world)
    3.71 +
    3.72 +(defprotocol Viewable
    3.73 +  (view [something]))
    3.74 +
    3.75 +(extend-type com.jme3.scene.Geometry
    3.76 +  Viewable
    3.77 +  (view [geo]
    3.78 +	(view (doto (Node.)(.attachChild geo)))))
    3.79 +
    3.80 +(extend-type com.jme3.scene.Node
    3.81 +  Viewable
    3.82 +  (view [node]
    3.83 +	(.start
    3.84 +	 (world node
    3.85 +		{}
    3.86 +		(fn [world]
    3.87 +		  (.enableDebug 
    3.88 +		   (.getPhysicsSpace
    3.89 +		    (.getState
    3.90 +		     (.getStateManager world)
    3.91 +		     BulletAppState))
    3.92 +		   (asset-manager))
    3.93 +		  (set-gravity* world Vector3f/ZERO)
    3.94 +;;		  (set-gravity* world (Vector3f. 0 (float -0.4) 0))
    3.95 +		  (let [sun (doto (DirectionalLight.)
    3.96 +			      (.setDirection (.normalizeLocal (Vector3f. 1 0 -2)))
    3.97 +			      (.setColor ColorRGBA/White))]
    3.98 +		    (.addLight (.getRootNode world) sun)))
    3.99 +		no-op))))
   3.100 +
   3.101 +(defn position-camera [game]
   3.102 +  (doto (.getCamera game)
   3.103 +    (.setLocation (Vector3f. 0 6 6))
   3.104 +    (.lookAt Vector3f/ZERO (Vector3f. 0 1 0))))
   3.105 +
   3.106 +#+end_src
   3.107 +
   3.108 +Here I make the =Viewable= protocol and extend it to JME's types.  Now
   3.109 +hello-world can be written as easily as:
   3.110 +
   3.111 +#+begin_src clojure :results silent
   3.112 +(cortex.world/view (cortex.world/box))
   3.113 +#+end_src
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/org/world.org	Sun Oct 23 23:54:26 2011 -0700
     4.3 @@ -0,0 +1,308 @@
     4.4 +#+title: A world for the creatures to live
     4.5 +#+author: Robert McIntyre
     4.6 +#+email: rlm@mit.edu
     4.7 +#+description: Simulating senses for AI research using JMonkeyEngine3
     4.8 +#+SETUPFILE: ../../aurellem/org/setup.org
     4.9 +#+INCLUDE: ../../aurellem/org/level-0.org
    4.10 +#+babel: :mkdirp yes :noweb yes :exports both
    4.11 +
    4.12 +
    4.13 +
    4.14 +*** World
    4.15 +
    4.16 +It is comvienent to wrap the JME elements that deal with creating a
    4.17 +world, creation of basic objects, and Keyboard input with a nicer
    4.18 +interface (at least for my purposes).
    4.19 +
    4.20 +#+srcname: world-inputs
    4.21 +#+begin_src clojure :results silent
    4.22 +(ns cortex.world)
    4.23 +(require 'cortex.import)
    4.24 +(use 'clojure.contrib.def)
    4.25 +(rlm.rlm-commands/help)
    4.26 +(cortex.import/mega-import-jme3)
    4.27 +
    4.28 +(defvar *app-settings*
    4.29 +  (doto (AppSettings. true)
    4.30 +    (.setFullscreen false)
    4.31 +    (.setTitle "Aurellem.")
    4.32 +    ;; disable 32 bit stuff for now
    4.33 +    ;;(.setAudioRenderer "Send")
    4.34 +    )
    4.35 +  "These settings control how the game is displayed on the screen for
    4.36 +   debugging purposes.  Use binding forms to change this if desired.
    4.37 +   Full-screen mode does not work on some computers.")
    4.38 +
    4.39 +(defn asset-manager
    4.40 +  "returns a new, configured assetManager" []
    4.41 +  (JmeSystem/newAssetManager
    4.42 +   (.getResource
    4.43 +    (.getContextClassLoader (Thread/currentThread))
    4.44 +    "com/jme3/asset/Desktop.cfg")))
    4.45 +
    4.46 +(defmacro no-exceptions
    4.47 +  "Sweet relief like I never knew."
    4.48 +  [& forms]
    4.49 +  `(try ~@forms (catch Exception e# (.printStackTrace e#))))
    4.50 +
    4.51 +(defn thread-exception-removal []
    4.52 +  (println "removing exceptions from " (Thread/currentThread))
    4.53 +  (.setUncaughtExceptionHandler
    4.54 +   (Thread/currentThread)
    4.55 +   (proxy [Thread$UncaughtExceptionHandler] []
    4.56 +     (uncaughtException
    4.57 +      [thread thrown]
    4.58 +      (println "uncaught-exception thrown in " thread)
    4.59 +      (println (.getMessage thrown))))))
    4.60 +
    4.61 +(def println-repl (bound-fn [& args] (apply println args)))
    4.62 +
    4.63 +(use '[pokemon [lpsolve :only [constant-map]]])
    4.64 +
    4.65 +(defn no-op [& _])
    4.66 +
    4.67 +(defn all-keys
    4.68 +  "Construct a map of strings representing all the manual inputs from
    4.69 +   either the keyboard or mouse." 
    4.70 +  []
    4.71 +  (let [inputs (constant-map KeyInput)]
    4.72 +    (assoc
    4.73 +	(zipmap (map (fn [field]
    4.74 +		       (.toLowerCase (re-gsub #"_" "-" field))) (vals inputs))
    4.75 +		(map (fn [val] (KeyTrigger. val)) (keys inputs)))
    4.76 +      ;;explicitly add mouse controls
    4.77 +      "mouse-left" (MouseButtonTrigger. 0)
    4.78 +      "mouse-middle" (MouseButtonTrigger. 2)
    4.79 +      "mouse-right" (MouseButtonTrigger. 1))))
    4.80 +
    4.81 +(defn initialize-inputs
    4.82 +  "more java-interop cruft to establish keybindings for a particular virtual world"
    4.83 +  [game  input-manager key-map]
    4.84 +  (doall (map (fn [[name trigger]]
    4.85 +		(.addMapping ^InputManager input-manager
    4.86 +			     name (into-array (class trigger) [trigger]))) key-map))
    4.87 +  (doall (map (fn [name] 
    4.88 +		(.addListener ^InputManager input-manager game
    4.89 +			      (into-array String  [name]))) (keys key-map))))
    4.90 +
    4.91 +#+end_src
    4.92 +
    4.93 +These functions are all for debug controlling of the world through
    4.94 +keyboard and mouse. 
    4.95 +
    4.96 +We reuse  =constant-map= from =pokemon.lpsolve= to get the numerical
    4.97 +values for all the keys defined in the =KeyInput= class. The
    4.98 +documentation for =constant-map= is:
    4.99 +
   4.100 +#+begin_src clojure :results output
   4.101 +(doc pokemon.lpsolve/constant-map)
   4.102 +#+end_src
   4.103 +
   4.104 +#+results:
   4.105 +: -------------------------
   4.106 +: pokemon.lpsolve/constant-map
   4.107 +: ([class])
   4.108 +:   Takes a class and creates a map of the static constant integer
   4.109 +:   fields with their names.  This helps with C wrappers where they have
   4.110 +:   just defined a bunch of integer constants instead of enums
   4.111 +
   4.112 +
   4.113 +Then, =all-keys= converts the constant names like =KEY_J= to the more
   4.114 +clojure-like =key-j=, and returns a map from these keys to
   4.115 +jMonkeyEngine KeyTrigger objects, the use of which will soon become
   4.116 +apparent. =all-keys= also adds the three mouse button controls to the
   4.117 +map.
   4.118 +
   4.119 +#+srcname: world
   4.120 +#+begin_src clojure :results silent
   4.121 +(in-ns 'cortex.world)
   4.122 +
   4.123 +(defn traverse
   4.124 +  "apply f to every non-node, deeply"
   4.125 +  [f node]
   4.126 +  (if (isa? (class node) Node)
   4.127 +    (dorun (map (partial traverse f) (.getChildren node)))
   4.128 +    (f node)))
   4.129 +
   4.130 +(def gravity (Vector3f. 0 -9.81 0))
   4.131 +
   4.132 +(defn world
   4.133 +  [root-node key-map setup-fn update-fn]
   4.134 +  (let [physics-manager (BulletAppState.)
   4.135 +	shadow-renderer (BasicShadowRenderer. (asset-manager) (int 256))
   4.136 +	;;maybe use a better shadow renderer someday!
   4.137 +	;;shadow-renderer (PssmShadowRenderer. (asset-manager) 256 1)
   4.138 +	]   	
   4.139 +  (doto
   4.140 +      (proxy [SimpleApplication ActionListener] []
   4.141 +	(simpleInitApp
   4.142 +	 []
   4.143 +	 (no-exceptions
   4.144 +	  (.setTimer this (IsoTimer. 60))
   4.145 +	  ;; Create key-map.
   4.146 +	  (.setFrustumFar (.getCamera this) 300)
   4.147 +	  (initialize-inputs this (.getInputManager this) (all-keys))
   4.148 +	  ;; Don't take control of the mouse
   4.149 +	  (org.lwjgl.input.Mouse/setGrabbed false)
   4.150 +	  ;; add all objects to the world
   4.151 +	  (.attachChild (.getRootNode this) root-node)
   4.152 +	  ;; enable physics
   4.153 +	  ;; add a physics manager
   4.154 +	  (.attach (.getStateManager this) physics-manager)
   4.155 +	  (.setGravity (.getPhysicsSpace physics-manager) gravity)
   4.156 +
   4.157 +
   4.158 +	  ;; go through every object and add it to the physics manager
   4.159 +	  ;; if relavant.
   4.160 +	   (traverse (fn [geom]
   4.161 +		       (dorun
   4.162 +			(for [n (range (.getNumControls geom))]
   4.163 +			  (do
   4.164 +			    (println-repl "adding control " (.getControl geom n))
   4.165 +			    (.add (.getPhysicsSpace physics-manager)
   4.166 +				  (.getControl geom n))))))
   4.167 +		     (.getRootNode this))
   4.168 +	  ;;(.addAll (.getPhysicsSpace physics-manager) (.getRootNode this))
   4.169 +
   4.170 +	  (setup-fn this)
   4.171 +	  (.setDirection shadow-renderer
   4.172 +			 (.normalizeLocal (Vector3f. -1 -1 -1)))
   4.173 +	  (.addProcessor (.getViewPort this) shadow-renderer)
   4.174 +	  (.setShadowMode (.getRootNode this) RenderQueue$ShadowMode/Off)
   4.175 +	  ))
   4.176 +	(simpleUpdate
   4.177 +	 [tpf]
   4.178 +	 (no-exceptions
   4.179 +	  (update-fn this tpf))) 
   4.180 +	(onAction
   4.181 +	 [binding value tpf]
   4.182 +	 ;; whenever a key is pressed, call the function returned from
   4.183 +	 ;; key-map.
   4.184 +	 (no-exceptions
   4.185 +	  (if-let [react (key-map binding)]
   4.186 +	    (react this value)))))
   4.187 +    ;; don't show a menu to change options.
   4.188 +    
   4.189 +    (.setShowSettings false)
   4.190 +    (.setPauseOnLostFocus false)
   4.191 +    (.setSettings *app-settings*))))
   4.192 +
   4.193 +(defn apply-map
   4.194 +  "Like apply, but works for maps and functions that expect an implicit map
   4.195 +   and nothing else as in (fn [& {}]).
   4.196 +------- Example -------
   4.197 + (defn jjj [& {:keys [www] :or {www \"oh yeah\"} :as env}] (println www))
   4.198 + (apply-map jjj {:www \"whatever\"})
   4.199 +  -->\"whatever\""
   4.200 +  [fn m]
   4.201 +  (apply fn (reduce #(into %1 %2) [] m)))
   4.202 +
   4.203 +#+end_src 
   4.204 +
   4.205 +
   4.206 +=world= is the most important function here.  
   4.207 +***  TODO more documentation
   4.208 +
   4.209 +#+srcname: world-shapes
   4.210 +#+begin_src clojure :results silent
   4.211 +(in-ns 'cortex.world)
   4.212 +(defrecord shape-description
   4.213 +  [name
   4.214 +   color
   4.215 +   mass
   4.216 +   friction
   4.217 +   texture
   4.218 +   material
   4.219 +   position
   4.220 +   rotation
   4.221 +   shape
   4.222 +   physical?])
   4.223 +
   4.224 +(def base-shape
   4.225 +     (shape-description.
   4.226 +      "default-shape"
   4.227 +      false
   4.228 +      ;;ColorRGBA/Blue
   4.229 +      1.0 ;; mass
   4.230 +      1.0 ;; friction
   4.231 +      ;; texture
   4.232 +      "Textures/Terrain/BrickWall/BrickWall.jpg"
   4.233 +      ;; material
   4.234 +      "Common/MatDefs/Misc/Unshaded.j3md"
   4.235 +      Vector3f/ZERO
   4.236 +      Quaternion/IDENTITY
   4.237 +      (Box. Vector3f/ZERO 0.5 0.5 0.5)
   4.238 +      true))
   4.239 +
   4.240 +(defn make-shape
   4.241 +  [#^shape-description d]
   4.242 +  (let [asset-manager (if (:asset-manager d) (:asset-manager d) (asset-manager))
   4.243 +        mat (Material. asset-manager (:material d))
   4.244 +	geom (Geometry. (:name d) (:shape d))]
   4.245 +    (if (:texture d)
   4.246 +      (let [key (TextureKey. (:texture d))]
   4.247 +	(.setGenerateMips key true)
   4.248 +	(.setTexture mat "ColorMap" (.loadTexture asset-manager key))))
   4.249 +    (if (:color d) (.setColor mat "Color" (:color d)))
   4.250 +    (.setMaterial geom mat)
   4.251 +    (if-let [rotation (:rotation d)] (.rotate geom rotation))
   4.252 +    (.setLocalTranslation geom (:position d))
   4.253 +    (if (:physical? d)
   4.254 +      (let [impact-shape (doto (GImpactCollisionShape. 
   4.255 +                                (.getMesh geom)) (.setMargin 0))
   4.256 +	    physics-control (RigidBodyControl.
   4.257 +			     ;;impact-shape ;; comment to disable 
   4.258 +			     (float (:mass d)))]
   4.259 +	(.createJmeMesh impact-shape)
   4.260 +	(.addControl geom physics-control)
   4.261 +	;;(.setSleepingThresholds physics-control (float 0) (float 0))
   4.262 +	(.setFriction physics-control (:friction d))))
   4.263 +    ;;the default is to keep this node in the physics engine forever.
   4.264 +    ;;these commands must come after the control is added to the geometry.
   4.265 +    ;;
   4.266 +    geom))
   4.267 +  
   4.268 +(defn box
   4.269 +  ([l w h & {:as options}]
   4.270 +     (let [options (merge base-shape options)]
   4.271 +       (make-shape (assoc options
   4.272 +		     :shape (Box. l w h)))))
   4.273 +  ([] (box 0.5 0.5 0.5)))
   4.274 +
   4.275 +(defn sphere
   4.276 +  ([r & {:as options}]
   4.277 +     (let [options (merge base-shape options)]
   4.278 +       (make-shape (assoc options
   4.279 +		     :shape (Sphere. 32 32 (float r)))))) 
   4.280 +  ([] (sphere 0.5)))
   4.281 +
   4.282 +(defn add-element
   4.283 +  ([game element node]
   4.284 +  (.addAll
   4.285 +   (.getPhysicsSpace
   4.286 +    (.getState
   4.287 +     (.getStateManager game)
   4.288 +     BulletAppState))
   4.289 +    element)
   4.290 +  (.attachChild node element))
   4.291 +  ([game element]
   4.292 +     (add-element game element (.getRootNode game))))
   4.293 +
   4.294 +
   4.295 +(defn set-gravity*
   4.296 +  [game gravity]
   4.297 +  (traverse
   4.298 +   (fn [geom]
   4.299 +     (if-let
   4.300 +	 [control (.getControl geom RigidBodyControl)]
   4.301 +       (do
   4.302 +	 (.setGravity control gravity)
   4.303 +	 (.applyImpulse control Vector3f/ZERO Vector3f/ZERO)
   4.304 +	 )))
   4.305 +   (.getRootNode game)))
   4.306 +
   4.307 +#+end_src
   4.308 +
   4.309 +These are convienence functions for creating JME objects and
   4.310 +manipulating a world.
   4.311 +