diff org/world.org @ 25:775d97247dd0

cleaning up world.org
author Robert McIntyre <rlm@mit.edu>
date Mon, 24 Oct 2011 05:25:01 -0700
parents e965675ec4d0
children bbffa41a12a9
line wrap: on
line diff
     1.1 --- a/org/world.org	Mon Oct 24 01:19:15 2011 -0700
     1.2 +++ b/org/world.org	Mon Oct 24 05:25:01 2011 -0700
     1.3 @@ -1,26 +1,83 @@
     1.4 -#+title: A world for the creatures to live
     1.5 +#+title: A World for the Creatures
     1.6  #+author: Robert McIntyre
     1.7  #+email: rlm@mit.edu
     1.8 -#+description: Simulating senses for AI research using JMonkeyEngine3
     1.9 +#+description: Creating a Virtual World for AI constructs using clojure and JME3
    1.10 +#+keywords: JME3, clojure, virtual world, exception handling
    1.11  #+SETUPFILE: ../../aurellem/org/setup.org
    1.12  #+INCLUDE: ../../aurellem/org/level-0.org
    1.13  #+babel: :mkdirp yes :noweb yes :exports both
    1.14  
    1.15 +* The  World
    1.16  
    1.17 +There's no point in having senses if there's nothing to experience. In
    1.18 +this section I make some tools with which to build virtual worlds for
    1.19 +my characters to inhabit. If you look at the tutorials at [[http://www.jmonkeyengine.org/wiki/doku.php/jme3:beginner][the jme3
    1.20 +website]], you will see a pattern in how virtual worlds are normally
    1.21 +built. I call this "the Java way" of making worlds.
    1.22  
    1.23 -*** World
    1.24 + - The Java way:  
    1.25 +   - Create a class that extends =SimpleApplication= or =Application=
    1.26 +   - Implement setup functions that create all the scene objects using
    1.27 +     the inherited =assetManager= and call them by overriding the
    1.28 +     =simpleInitApp= method.
    1.29 +   - Create =ActionListeners= and add them to the =inputManager=
    1.30 +     inherited from =Application= to handle key-bindings.
    1.31 +   - Override =simpleUpdate= to implement game logic.
    1.32 +   - Running/Testing an Application involves creating a new JVM,
    1.33 +     running the App, and then closing everything down.
    1.34  
    1.35 -It is comvienent to wrap the JME elements that deal with creating a
    1.36 -world, creation of basic objects, and Keyboard input with a nicer
    1.37 -interface (at least for my purposes).
    1.38  
    1.39 -#+srcname: world-inputs
    1.40 -#+begin_src clojure :results silent
    1.41 -(ns cortex.world)
    1.42 -(require 'cortex.import)
    1.43 -(use 'clojure.contrib.def)
    1.44 -(rlm.rlm-commands/help)
    1.45 -(cortex.import/mega-import-jme3)
    1.46 + - A more Clojureish way:
    1.47 +   - Use a map from keys->functions to specify key-bindings. 
    1.48 +   - Use functions to create objects separately from any particular
    1.49 +     application.
    1.50 +   - Use an REPL -- this means that there's only ever one JVM, and
    1.51 +     Applications come and go.
    1.52 +
    1.53 +Since most development work using jMonkeyEngine is done in Java, jme3
    1.54 +supports "the Java way" quite well out of the box. To work "the
    1.55 +clojure way", it necessary to wrap the jme3 elements that deal with
    1.56 +the Application life-cycle with a REPL driven interface.
    1.57 +
    1.58 +The most important modifications are:
    1.59 +
    1.60 + - Separation of Object life-cycles with the Application life-cycle.
    1.61 + - Functional interface to the underlying =Application= and
    1.62 +   =SimpleApplication= classes.
    1.63 +
    1.64 +** Header
    1.65 +#+srcname: header
    1.66 +#+begin_src clojure :results silent    
    1.67 +(ns cortex.world
    1.68 +  "World Creation, abstracion over jme3's input system, and REPL
    1.69 +  driven exception handling"
    1.70 +  {:author "Robert McIntyre"}
    1.71 +  
    1.72 +  (:use (clojure.contrib (def :only (defvar))))
    1.73 +  (:use [pokemon [lpsolve :only [constant-map]]])
    1.74 +  (:use [clojure.contrib [str-utils :only [re-gsub]]])
    1.75 +
    1.76 +  (:import com.jme3.math.Vector3f)
    1.77 +  (:import com.jme3.scene.Node)
    1.78 +  (:import com.jme3.system.AppSettings)
    1.79 +  (:import com.jme3.system.JmeSystem)
    1.80 +  (:import com.jme3.system.IsoTimer)
    1.81 +  (:import com.jme3.input.KeyInput)
    1.82 +  (:import com.jme3.input.controls.KeyTrigger)
    1.83 +  (:import com.jme3.input.controls.MouseButtonTrigger)
    1.84 +  (:import com.jme3.input.InputManager)
    1.85 +  (:import com.jme3.bullet.BulletAppState)
    1.86 +  (:import com.jme3.shadow.BasicShadowRenderer)
    1.87 +  (:import com.jme3.app.SimpleApplication)
    1.88 +  (:import com.jme3.input.controls.ActionListener)
    1.89 +  (:import com.jme3.renderer.queue.RenderQueue$ShadowMode)
    1.90 +  (:import org.lwjgl.input.Mouse))
    1.91 +#+end_src    
    1.92 +    
    1.93 +** General Settings    
    1.94 +#+srcname: settings    
    1.95 +#+begin_src clojure 
    1.96 +(in-ns 'cortex.world)
    1.97  
    1.98  (defvar *app-settings*
    1.99    (doto (AppSettings. true)
   1.100 @@ -31,7 +88,7 @@
   1.101      )
   1.102    "These settings control how the game is displayed on the screen for
   1.103     debugging purposes.  Use binding forms to change this if desired.
   1.104 -   Full-screen mode does not work on some computers.")
   1.105 +   Full-screen mode does not work on some computers.")    
   1.106  
   1.107  (defn asset-manager
   1.108    "returns a new, configured assetManager" []
   1.109 @@ -39,33 +96,58 @@
   1.110     (.getResource
   1.111      (.getContextClassLoader (Thread/currentThread))
   1.112      "com/jme3/asset/Desktop.cfg")))
   1.113 +#+end_src
   1.114 +
   1.115 +Normally, people just use the =AssetManager= inherited from
   1.116 +=Application= whenever they extend that class. However,
   1.117 +=AssetManagers= are useful on their own to create objects/ materials,
   1.118 +independent from any particular application. =(asset-manager)= makes
   1.119 +object creation less tightly bound to Application initialization.
   1.120 +
   1.121 +
   1.122 +** Exception Protection
   1.123 +#+srcname: exceptions
   1.124 +#+begin_src clojure
   1.125 +(in-ns 'cortex.world)
   1.126  
   1.127  (defmacro no-exceptions
   1.128    "Sweet relief like I never knew."
   1.129    [& forms]
   1.130    `(try ~@forms (catch Exception e# (.printStackTrace e#))))
   1.131  
   1.132 -(defn thread-exception-removal []
   1.133 -  (println "removing exceptions from " (Thread/currentThread))
   1.134 +(defn thread-exception-removal
   1.135 +  "Exceptions thrown in the graphics rendering thread generally cause
   1.136 +  the entire REPL to crash! It is good to suppress them while trying
   1.137 +  things out to shorten the debug loop."
   1.138 +  []
   1.139    (.setUncaughtExceptionHandler
   1.140     (Thread/currentThread)
   1.141     (proxy [Thread$UncaughtExceptionHandler] []
   1.142       (uncaughtException
   1.143 -      [thread thrown]
   1.144 -      (println "uncaught-exception thrown in " thread)
   1.145 -      (println (.getMessage thrown))))))
   1.146 +       [thread thrown]
   1.147 +       (println "uncaught-exception thrown in " thread)
   1.148 +       (println (.getMessage thrown))))))
   1.149  
   1.150 -(def println-repl (bound-fn [& args] (apply println args)))
   1.151 +#+end_src
   1.152  
   1.153 -(use '[pokemon [lpsolve :only [constant-map]]])
   1.154 +Exceptions thrown in the LWJGL render thread, if not caught, will
   1.155 +destroy the entire JVM process including the REPL and slow development
   1.156 +to a crawl.  It is better to try to continue on in the face of
   1.157 +exceptions and keep the REPL alive as long as possible.  Normally it
   1.158 +is possible to just exit the faulty Application, fix the bug,
   1.159 +reevaluate the appropriate forms, and be on your way, without
   1.160 +restarting the JVM.
   1.161  
   1.162 -(defn no-op [& _])
   1.163 +** Input
   1.164 +#+srcname: input
   1.165 +#+begin_src clojure
   1.166 +(in-ns 'cortex.world)
   1.167  
   1.168  (defn all-keys
   1.169 -  "Construct a map of strings representing all the manual inputs from
   1.170 -   either the keyboard or mouse." 
   1.171 +  "Uses reflection to generate a map of string names to jme3 trigger
   1.172 +  objects, which govern input from the keyboard and mouse"
   1.173    []
   1.174 -  (let [inputs (constant-map KeyInput)]
   1.175 +  (let [inputs (pokemon.lpsolve/constant-map KeyInput)]
   1.176      (assoc
   1.177  	(zipmap (map (fn [field]
   1.178  		       (.toLowerCase (re-gsub #"_" "-" field))) (vals inputs))
   1.179 @@ -76,21 +158,26 @@
   1.180        "mouse-right" (MouseButtonTrigger. 1))))
   1.181  
   1.182  (defn initialize-inputs
   1.183 -  "more java-interop cruft to establish keybindings for a particular virtual world"
   1.184 +  "Establish key-bindings for a particular virtual world."
   1.185    [game  input-manager key-map]
   1.186 -  (doall (map (fn [[name trigger]]
   1.187 -		(.addMapping ^InputManager input-manager
   1.188 -			     name (into-array (class trigger) [trigger]))) key-map))
   1.189 -  (doall (map (fn [name] 
   1.190 -		(.addListener ^InputManager input-manager game
   1.191 -			      (into-array String  [name]))) (keys key-map))))
   1.192 +  (doall
   1.193 +   (map (fn [[name trigger]]
   1.194 +          (.addMapping
   1.195 +           ^InputManager input-manager
   1.196 +           name (into-array (class trigger)
   1.197 +                            [trigger]))) key-map))
   1.198 +  (doall
   1.199 +   (map (fn [name] 
   1.200 +          (.addListener
   1.201 +           ^InputManager input-manager game
   1.202 +           (into-array String  [name]))) (keys key-map))))
   1.203  
   1.204  #+end_src
   1.205  
   1.206 -These functions are all for debug controlling of the world through
   1.207 -keyboard and mouse. 
   1.208 +These functions are for controlling the world through the keyboard and
   1.209 +mouse.
   1.210  
   1.211 -We reuse  =constant-map= from =pokemon.lpsolve= to get the numerical
   1.212 +I reuse =constant-map= from [[../../pokemon-types/html/lpsolve.html#sec-3-3-4][=pokemon.lpsolve=]] to get the numerical
   1.213  values for all the keys defined in the =KeyInput= class. The
   1.214  documentation for =constant-map= is:
   1.215  
   1.216 @@ -106,17 +193,63 @@
   1.217  :   fields with their names.  This helps with C wrappers where they have
   1.218  :   just defined a bunch of integer constants instead of enums
   1.219  
   1.220 +=(all-keys)= converts the constant names like =KEY_J= to the more
   1.221 +clojure-like =key-j=, and returns a map from these keys to
   1.222 +jMonkeyEngine =KeyTrigger= objects, which jMonkeyEngine3 uses as it's
   1.223 +abstraction over the physical keys. =all-keys= also adds the three
   1.224 +mouse button controls to the map.
   1.225  
   1.226 -Then, =all-keys= converts the constant names like =KEY_J= to the more
   1.227 -clojure-like =key-j=, and returns a map from these keys to
   1.228 -jMonkeyEngine KeyTrigger objects, the use of which will soon become
   1.229 -apparent. =all-keys= also adds the three mouse button controls to the
   1.230 -map.
   1.231  
   1.232 +#+begin_src clojure :exports both :results output
   1.233 +(require 'clojure.contrib.pprint)
   1.234 +(clojure.contrib.pprint/pprint
   1.235 + (take 10 (keys (cortex.world/all-keys))))
   1.236 +#+end_src
   1.237 +
   1.238 +#+results:
   1.239 +#+begin_example
   1.240 +("key-n"
   1.241 + "key-apps"
   1.242 + "key-pgup"
   1.243 + "key-f8"
   1.244 + "key-o"
   1.245 + "key-at"
   1.246 + "key-f9"
   1.247 + "key-0"
   1.248 + "key-p"
   1.249 + "key-subtract")
   1.250 +#+end_example
   1.251 +
   1.252 +#+begin_src clojure :exports both :results output
   1.253 +(clojure.contrib.pprint/pprint
   1.254 + (take 10 (vals (cortex.world/all-keys))))
   1.255 +#+end_src
   1.256 +
   1.257 +#+results:
   1.258 +#+begin_example
   1.259 +(#<KeyTrigger com.jme3.input.controls.KeyTrigger@6ec21e52>
   1.260 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@a54d24d>
   1.261 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@1ba5e91b>
   1.262 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@296af9cb>
   1.263 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@2e3593ab>
   1.264 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@3f71d740>
   1.265 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@4aeacb4a>
   1.266 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@7cc88db2>
   1.267 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@52cee11e>
   1.268 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@c1da30b>)
   1.269 +#+end_example
   1.270 +
   1.271 +
   1.272 +
   1.273 +** World Creation
   1.274  #+srcname: world
   1.275  #+begin_src clojure :results silent
   1.276  (in-ns 'cortex.world)
   1.277  
   1.278 +(defn no-op
   1.279 +  "Takes any number of arguments and does nothing."
   1.280 +  [& _])
   1.281 +
   1.282  (defn traverse
   1.283    "apply f to every non-node, deeply"
   1.284    [f node]
   1.285 @@ -124,196 +257,129 @@
   1.286      (dorun (map (partial traverse f) (.getChildren node)))
   1.287      (f node)))
   1.288  
   1.289 -(def gravity (Vector3f. 0 -9.81 0))
   1.290 +(defn world
   1.291 +  "the =world= function takes care of the details of initializing a
   1.292 +  SimpleApplication.
   1.293  
   1.294 -(defn world
   1.295 +   ***** Arguments:
   1.296 +
   1.297 +   - root-node : a com.jme3.scene.Node object which contains all of
   1.298 +       the objects that should be in the simulation.
   1.299 +
   1.300 +   - key-map : a map from strings describing keys to functions that
   1.301 +       should be executed whenever that key is pressed.
   1.302 +       the functions should take a SimpleApplication object and a
   1.303 +       boolean value.  The SimpleApplication is the current simulation
   1.304 +       that is running, and the boolean is true if the key is being
   1.305 +       pressed, and false if it is being released. As an example,
   1.306 +
   1.307 +       {\"key-j\" (fn [game value] (if value (println \"key j pressed\")))}
   1.308 +
   1.309 +       is a valid key-map which will cause the simulation to print a
   1.310 +       message whenever the 'j' key on the keyboard is pressed.
   1.311 +
   1.312 +   - setup-fn : a function that takes a SimpleApplication object. It
   1.313 +       is called once when initializing the simulation. Use it to
   1.314 +       create things like lights, change the gravity, initialize debug
   1.315 +       nodes, etc.
   1.316 +
   1.317 +   - update-fn : this function takes a SimpleApplication object and a
   1.318 +       float and is called every frame of the simulation.  The float
   1.319 +       tells how many seconds is has been since the last frame was
   1.320 +       rendered, according to whatever clock jme is currently
   1.321 +       using. The default is to use IsoTimer which will result in this
   1.322 +       value always being the same.
   1.323 +  "
   1.324    [root-node key-map setup-fn update-fn]
   1.325    (let [physics-manager (BulletAppState.)
   1.326 -	shadow-renderer (BasicShadowRenderer. (asset-manager) (int 256))
   1.327 -	;;maybe use a better shadow renderer someday!
   1.328 -	;;shadow-renderer (PssmShadowRenderer. (asset-manager) 256 1)
   1.329 -	]   	
   1.330 -  (doto
   1.331 -      (proxy [SimpleApplication ActionListener] []
   1.332 -	(simpleInitApp
   1.333 -	 []
   1.334 -	 (no-exceptions
   1.335 -	  (.setTimer this (IsoTimer. 60))
   1.336 -	  ;; Create key-map.
   1.337 -	  (.setFrustumFar (.getCamera this) 300)
   1.338 -	  (initialize-inputs this (.getInputManager this) (all-keys))
   1.339 -	  ;; Don't take control of the mouse
   1.340 -	  (org.lwjgl.input.Mouse/setGrabbed false)
   1.341 -	  ;; add all objects to the world
   1.342 -	  (.attachChild (.getRootNode this) root-node)
   1.343 -	  ;; enable physics
   1.344 -	  ;; add a physics manager
   1.345 -	  (.attach (.getStateManager this) physics-manager)
   1.346 -	  (.setGravity (.getPhysicsSpace physics-manager) gravity)
   1.347 -
   1.348 -
   1.349 -	  ;; go through every object and add it to the physics manager
   1.350 -	  ;; if relavant.
   1.351 -	   (traverse (fn [geom]
   1.352 -		       (dorun
   1.353 -			(for [n (range (.getNumControls geom))]
   1.354 -			  (do
   1.355 -			    (println-repl "adding control " (.getControl geom n))
   1.356 -			    (.add (.getPhysicsSpace physics-manager)
   1.357 -				  (.getControl geom n))))))
   1.358 -		     (.getRootNode this))
   1.359 -	  ;;(.addAll (.getPhysicsSpace physics-manager) (.getRootNode this))
   1.360 -
   1.361 -	  (setup-fn this)
   1.362 -	  (.setDirection shadow-renderer
   1.363 -			 (.normalizeLocal (Vector3f. -1 -1 -1)))
   1.364 -	  (.addProcessor (.getViewPort this) shadow-renderer)
   1.365 -	  (.setShadowMode (.getRootNode this) RenderQueue$ShadowMode/Off)
   1.366 -	  ))
   1.367 -	(simpleUpdate
   1.368 -	 [tpf]
   1.369 -	 (no-exceptions
   1.370 -	  (update-fn this tpf))) 
   1.371 -	(onAction
   1.372 -	 [binding value tpf]
   1.373 -	 ;; whenever a key is pressed, call the function returned from
   1.374 -	 ;; key-map.
   1.375 -	 (no-exceptions
   1.376 -	  (if-let [react (key-map binding)]
   1.377 -	    (react this value)))))
   1.378 -    ;; don't show a menu to change options.
   1.379 -    
   1.380 -    (.setShowSettings false)
   1.381 -    (.setPauseOnLostFocus false)
   1.382 -    (.setSettings *app-settings*))))
   1.383 +	shadow-renderer (BasicShadowRenderer.
   1.384 +                         (asset-manager) (int 256))]   	
   1.385 +    (doto
   1.386 +        (proxy [SimpleApplication ActionListener] []
   1.387 +          (simpleInitApp
   1.388 +            []
   1.389 +            (no-exceptions
   1.390 +             ;; allow AI entities as much time as they need to think.
   1.391 +             (.setTimer this (IsoTimer. 60))
   1.392 +             (.setFrustumFar (.getCamera this) 300)
   1.393 +             ;; Create default key-map.
   1.394 +             (initialize-inputs this (.getInputManager this) (all-keys))
   1.395 +             ;; Don't take control of the mouse
   1.396 +             (org.lwjgl.input.Mouse/setGrabbed false)
   1.397 +             ;; add all objects to the world
   1.398 +             (.attachChild (.getRootNode this) root-node)
   1.399 +             ;; enable physics
   1.400 +             ;; add a physics manager
   1.401 +             (.attach (.getStateManager this) physics-manager)
   1.402 +             (.setGravity (.getPhysicsSpace physics-manager) 
   1.403 +                          (Vector3f. 0 -9.81 0))
   1.404 +             ;; go through every object and add it to the physics
   1.405 +             ;; manager if relevant.
   1.406 +             (traverse (fn [geom]
   1.407 +                         (dorun
   1.408 +                          (for [n (range (.getNumControls geom))]
   1.409 +                            (do
   1.410 +                              (.add (.getPhysicsSpace physics-manager)
   1.411 +                                    (.getControl geom n))))))
   1.412 +                       (.getRootNode this))
   1.413 +             ;;(.addAll (.getPhysicsSpace physics-manager) (.getRootNode this))
   1.414 +             
   1.415 +             ;; set some basic defaults for the shadow renderer.
   1.416 +             ;; these can be undone in the setup function
   1.417 +             (.setDirection shadow-renderer
   1.418 +                            (.normalizeLocal (Vector3f. -1 -1 -1)))
   1.419 +             (.addProcessor (.getViewPort this) shadow-renderer)
   1.420 +             (.setShadowMode (.getRootNode this)
   1.421 +                             RenderQueue$ShadowMode/Off)
   1.422 +             ;; call the supplied setup-fn
   1.423 +             (if setup-fn
   1.424 +               (setup-fn this))))
   1.425 +          (simpleUpdate
   1.426 +            [tpf]
   1.427 +            (no-exceptions
   1.428 +             (update-fn this tpf))) 
   1.429 +          (onAction
   1.430 +            [binding value tpf]
   1.431 +            ;; whenever a key is pressed, call the function returned
   1.432 +            ;; from key-map.
   1.433 +            (no-exceptions
   1.434 +             (if-let [react (key-map binding)]
   1.435 +               (react this value)))))
   1.436 +      ;; don't show a menu to change options.      
   1.437 +      (.setShowSettings false)
   1.438 +      ;; continue running simulation even if the window has lost
   1.439 +      ;; focus.
   1.440 +      (.setPauseOnLostFocus false)
   1.441 +      (.setSettings *app-settings*))))
   1.442  
   1.443  (defn apply-map
   1.444 -  "Like apply, but works for maps and functions that expect an implicit map
   1.445 -   and nothing else as in (fn [& {}]).
   1.446 -------- Example -------
   1.447 - (defn jjj [& {:keys [www] :or {www \"oh yeah\"} :as env}] (println www))
   1.448 - (apply-map jjj {:www \"whatever\"})
   1.449 -  -->\"whatever\""
   1.450 +  "Like apply, but works for maps and functions that expect an
   1.451 +   implicit map and nothing else as in (fn [& {}]).
   1.452 +   ------- Example -------
   1.453 +   (defn demo [& {:keys [www] :or {www \"oh yeah\"} :as env}] 
   1.454 +     (println www))
   1.455 +   (apply-map demo {:www \"hello!\"})
   1.456 +   -->\"hello\""
   1.457    [fn m]
   1.458    (apply fn (reduce #(into %1 %2) [] m)))
   1.459  
   1.460  #+end_src 
   1.461  
   1.462  
   1.463 -=world= is the most important function here.  
   1.464 -***  TODO more documentation
   1.465 -
   1.466 -#+srcname: world-shapes
   1.467 -#+begin_src clojure :results silent
   1.468 -(in-ns 'cortex.world)
   1.469 -(defrecord shape-description
   1.470 -  [name
   1.471 -   color
   1.472 -   mass
   1.473 -   friction
   1.474 -   texture
   1.475 -   material
   1.476 -   position
   1.477 -   rotation
   1.478 -   shape
   1.479 -   physical?])
   1.480 -
   1.481 -(def base-shape
   1.482 -     (shape-description.
   1.483 -      "default-shape"
   1.484 -      false
   1.485 -      ;;ColorRGBA/Blue
   1.486 -      1.0 ;; mass
   1.487 -      1.0 ;; friction
   1.488 -      ;; texture
   1.489 -      "Textures/Terrain/BrickWall/BrickWall.jpg"
   1.490 -      ;; material
   1.491 -      "Common/MatDefs/Misc/Unshaded.j3md"
   1.492 -      Vector3f/ZERO
   1.493 -      Quaternion/IDENTITY
   1.494 -      (Box. Vector3f/ZERO 0.5 0.5 0.5)
   1.495 -      true))
   1.496 -
   1.497 -(defn make-shape
   1.498 -  [#^shape-description d]
   1.499 -  (let [asset-manager (if (:asset-manager d) (:asset-manager d) (asset-manager))
   1.500 -        mat (Material. asset-manager (:material d))
   1.501 -	geom (Geometry. (:name d) (:shape d))]
   1.502 -    (if (:texture d)
   1.503 -      (let [key (TextureKey. (:texture d))]
   1.504 -	(.setGenerateMips key true)
   1.505 -	(.setTexture mat "ColorMap" (.loadTexture asset-manager key))))
   1.506 -    (if (:color d) (.setColor mat "Color" (:color d)))
   1.507 -    (.setMaterial geom mat)
   1.508 -    (if-let [rotation (:rotation d)] (.rotate geom rotation))
   1.509 -    (.setLocalTranslation geom (:position d))
   1.510 -    (if (:physical? d)
   1.511 -      (let [impact-shape (doto (GImpactCollisionShape. 
   1.512 -                                (.getMesh geom)) (.setMargin 0))
   1.513 -	    physics-control (RigidBodyControl.
   1.514 -			     ;;impact-shape ;; comment to disable 
   1.515 -			     (float (:mass d)))]
   1.516 -	(.createJmeMesh impact-shape)
   1.517 -	(.addControl geom physics-control)
   1.518 -	;;(.setSleepingThresholds physics-control (float 0) (float 0))
   1.519 -	(.setFriction physics-control (:friction d))))
   1.520 -    ;;the default is to keep this node in the physics engine forever.
   1.521 -    ;;these commands must come after the control is added to the geometry.
   1.522 -    ;;
   1.523 -    geom))
   1.524 -  
   1.525 -(defn box
   1.526 -  ([l w h & {:as options}]
   1.527 -     (let [options (merge base-shape options)]
   1.528 -       (make-shape (assoc options
   1.529 -		     :shape (Box. l w h)))))
   1.530 -  ([] (box 0.5 0.5 0.5)))
   1.531 -
   1.532 -(defn sphere
   1.533 -  ([r & {:as options}]
   1.534 -     (let [options (merge base-shape options)]
   1.535 -       (make-shape (assoc options
   1.536 -		     :shape (Sphere. 32 32 (float r)))))) 
   1.537 -  ([] (sphere 0.5)))
   1.538 -
   1.539 -(defn add-element
   1.540 -  ([game element node]
   1.541 -  (.addAll
   1.542 -   (.getPhysicsSpace
   1.543 -    (.getState
   1.544 -     (.getStateManager game)
   1.545 -     BulletAppState))
   1.546 -    element)
   1.547 -  (.attachChild node element))
   1.548 -  ([game element]
   1.549 -     (add-element game element (.getRootNode game))))
   1.550 -
   1.551 -
   1.552 -(defn set-gravity*
   1.553 -  [game gravity]
   1.554 -  (traverse
   1.555 -   (fn [geom]
   1.556 -     (if-let
   1.557 -	 [control (.getControl geom RigidBodyControl)]
   1.558 -       (do
   1.559 -	 (.setGravity control gravity)
   1.560 -	 (.applyImpulse control Vector3f/ZERO Vector3f/ZERO)
   1.561 -	 )))
   1.562 -   (.getRootNode game)))
   1.563 -
   1.564 -#+end_src
   1.565 -
   1.566 -These are convienence functions for creating JME objects and
   1.567 -manipulating a world.
   1.568 -
   1.569 -
   1.570 +=(world)= is the most important function here. It presents a more
   1.571 +functional interface to the Application life-cycle, and all it's
   1.572 +objects except =root-node= are plain clojure data structures. It's now
   1.573 +possible to extend functionally by composing multiple functions
   1.574 +together, and to add more keyboard-driven actions by combining clojure
   1.575 +maps.
   1.576  
   1.577  
   1.578  
   1.579  * COMMENT code generation
   1.580 -
   1.581  #+begin_src clojure :tangle ../src/cortex/world.clj
   1.582 -<<world-inputs>>
   1.583 +<<header>>
   1.584 +<<settings>>
   1.585 +<<exceptions>>
   1.586 +<<input>>
   1.587  <<world>>
   1.588 -<<world-shapes>>
   1.589  #+end_src