changeset 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
files org/cortex.org org/eyes.org org/util.org org/world.org
diffstat 4 files changed, 410 insertions(+), 483 deletions(-) [+]
line wrap: on
line diff
     1.1 --- a/org/cortex.org	Mon Oct 24 01:19:15 2011 -0700
     1.2 +++ b/org/cortex.org	Mon Oct 24 05:25:01 2011 -0700
     1.3 @@ -78,6 +78,7 @@
     1.4  (cortex.import/mega-import-jme3)
     1.5  (use '[pokemon [lpsolve :only [constant-map]]])
     1.6  (use 'cortex.world)
     1.7 +(use 'cortex.util)
     1.8  #+end_src
     1.9  
    1.10  #+srcname: brick-wall-body
    1.11 @@ -744,250 +745,7 @@
    1.12  
    1.13  
    1.14      
    1.15 -* The Body
    1.16 -** Eyes
    1.17 -
    1.18 -Ultimately I want to make creatures with eyes. Each eye can be
    1.19 -independely moved and should see its own version of the world
    1.20 -depending on where it is.
    1.21 -#+srcname: eyes
    1.22 -#+begin_src clojure 
    1.23 -(ns body.eye)
    1.24 -(use 'cortex.world)
    1.25 -(use 'cortex.import)
    1.26 -(use 'clojure.contrib.def)
    1.27 -(cortex.import/mega-import-jme3)
    1.28 -(rlm.rlm-commands/help)
    1.29 -(import java.nio.ByteBuffer)
    1.30 -(import java.awt.image.BufferedImage)
    1.31 -(import java.awt.Color)
    1.32 -(import java.awt.Dimension)
    1.33 -(import java.awt.Graphics)
    1.34 -(import java.awt.Graphics2D)
    1.35 -(import java.awt.event.WindowAdapter)
    1.36 -(import java.awt.event.WindowEvent)
    1.37 -(import java.awt.image.BufferedImage)
    1.38 -(import java.nio.ByteBuffer)
    1.39 -(import javax.swing.JFrame)
    1.40 -(import javax.swing.JPanel)
    1.41 -(import javax.swing.SwingUtilities)
    1.42 -(import javax.swing.ImageIcon)
    1.43 -(import javax.swing.JOptionPane)
    1.44 -(import java.awt.image.ImageObserver)
    1.45 -
    1.46 -
    1.47 -
    1.48 -(defn scene-processor
    1.49 -  "deals with converting FrameBuffers to BufferedImages so
    1.50 -   that the continuation function can be defined only in terms
    1.51 -   of what it does with BufferedImages"
    1.52 -  [continuation]
    1.53 -  (let [byte-buffer (atom nil)
    1.54 -	renderer (atom nil)
    1.55 -	image (atom nil)]
    1.56 -  (proxy [SceneProcessor] []
    1.57 -    (initialize
    1.58 -     [renderManager viewPort]
    1.59 -     (let [cam (.getCamera viewPort)
    1.60 -	   width (.getWidth cam)
    1.61 -	   height (.getHeight cam)]
    1.62 -       (reset! renderer (.getRenderer renderManager))
    1.63 -       (reset! byte-buffer
    1.64 -	     (BufferUtils/createByteBuffer
    1.65 -	      (* width height 4)))
    1.66 -       (reset! image (BufferedImage. width height
    1.67 -				     BufferedImage/TYPE_4BYTE_ABGR))))
    1.68 -    (isInitialized [] (not (nil? @byte-buffer)))
    1.69 -    (reshape [_ _ _])
    1.70 -    (preFrame [_])
    1.71 -    (postQueue [_])
    1.72 -    (postFrame
    1.73 -     [#^FrameBuffer fb]
    1.74 -     (.clear @byte-buffer)
    1.75 -     (.readFrameBuffer @renderer fb @byte-buffer)
    1.76 -     (Screenshots/convertScreenShot @byte-buffer @image)
    1.77 -     (continuation @image))
    1.78 -    (cleanup []))))
    1.79 -    
    1.80 -(defn add-eye
    1.81 -  "Add an eye to the world, and call continuation on
    1.82 -   every frame produced"
    1.83 -  [world camera continuation]
    1.84 -  (let [width (.getWidth camera)
    1.85 -	height (.getHeight camera)
    1.86 -	render-manager (.getRenderManager world)
    1.87 -	viewport (.createMainView render-manager "eye-view" camera)]
    1.88 -    (doto viewport
    1.89 -      (.setBackgroundColor ColorRGBA/Black)
    1.90 -      (.setClearFlags true true true)
    1.91 -      (.addProcessor (scene-processor continuation))
    1.92 -      (.attachScene (.getRootNode world)))))
    1.93 -
    1.94 -(defn make-display-frame [display width height]
    1.95 -  (SwingUtilities/invokeLater
    1.96 -   (fn []
    1.97 -     (.setPreferredSize display (Dimension. width height))
    1.98 -     (doto (JFrame. "Eye Camera!")
    1.99 -       (-> (.getContentPane) (.add display))
   1.100 -       (.setDefaultCloseOperation JFrame/DISPOSE_ON_CLOSE)
   1.101 -       (.pack)
   1.102 -       (.setLocationRelativeTo nil)
   1.103 -       (.setResizable false)
   1.104 -       (.setVisible true)))))
   1.105 -	   
   1.106 -(defn image-monitor [#^BufferedImage image]
   1.107 -  (proxy [JPanel] []
   1.108 -    (paintComponent 
   1.109 -     [g]
   1.110 -     (proxy-super paintComponent g)
   1.111 -     (locking image
   1.112 -       (.drawImage g image 0 0
   1.113 -		   (proxy [ImageObserver]
   1.114 -		       []
   1.115 -		     (imageUpdate
   1.116 -		      []
   1.117 -		      (proxy-super imageUpdate))))))))
   1.118 -
   1.119 -(defn movie-image []
   1.120 -  (let [setup
   1.121 -	(runonce
   1.122 -	 (fn [#^BufferedImage image]
   1.123 -	   (let [width (.getWidth image)
   1.124 -		 height (.getHeight image)
   1.125 -		 display (image-monitor image)
   1.126 -		 frame (make-display-frame display width height)]
   1.127 -	     display)))]
   1.128 -    (fn [#^BufferedImage image]
   1.129 -      (.repaint (setup image)))))
   1.130 -	     
   1.131 -
   1.132 -(defn observer
   1.133 -  "place thy eye!"
   1.134 -  [world camera]
   1.135 -  (let [eye camera
   1.136 -	width (.getWidth eye)
   1.137 -	height (.getHeight eye)]
   1.138 -    (no-exceptions
   1.139 -     (add-eye
   1.140 -      world
   1.141 -      eye
   1.142 -      (movie-image)))))
   1.143 -#+end_src
   1.144 -
   1.145 -#+srcname: test-vision
   1.146 -#+begin_src clojure
   1.147 -
   1.148 -(ns test.vision)
   1.149 -(use 'cortex.world)
   1.150 -(use 'cortex.import)
   1.151 -(use 'clojure.contrib.def)
   1.152 -(use 'body.eye)
   1.153 -(cortex.import/mega-import-jme3)
   1.154 -(rlm.rlm-commands/help)
   1.155 -(import java.nio.ByteBuffer)
   1.156 -(import java.awt.image.BufferedImage)
   1.157 -(import java.awt.Color)
   1.158 -(import java.awt.Dimension)
   1.159 -(import java.awt.Graphics)
   1.160 -(import java.awt.Graphics2D)
   1.161 -(import java.awt.event.WindowAdapter)
   1.162 -(import java.awt.event.WindowEvent)
   1.163 -(import java.awt.image.BufferedImage)
   1.164 -(import java.nio.ByteBuffer)
   1.165 -(import javax.swing.JFrame)
   1.166 -(import javax.swing.JPanel)
   1.167 -(import javax.swing.SwingUtilities)
   1.168 -(import javax.swing.ImageIcon)
   1.169 -(import javax.swing.JOptionPane)
   1.170 -(import java.awt.image.ImageObserver)
   1.171 -
   1.172 -
   1.173 -(def width 200)
   1.174 -(def height 200)
   1.175 -
   1.176 -(defn camera []
   1.177 -  (doto (Camera. width height)
   1.178 -    (.setFrustumPerspective 45 1 1 1000)
   1.179 -    (.setLocation (Vector3f. -3 0 -5))
   1.180 -    (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)))
   1.181 -
   1.182 -(defn camera2 []
   1.183 -  (doto (Camera. width height)
   1.184 -    (.setFrustumPerspective 45 1 1 1000)
   1.185 -    (.setLocation (Vector3f. 3 0 -5))
   1.186 -    (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)))
   1.187 -
   1.188 -(defn setup-fn [world]
   1.189 -  (let [eye (camera)
   1.190 -	width (.getWidth eye)
   1.191 -	height (.getHeight eye)]
   1.192 -    (no-exceptions
   1.193 -     (add-eye
   1.194 -      world
   1.195 -      eye
   1.196 -      (runonce visual))
   1.197 -     (add-eye
   1.198 -      world
   1.199 -      (camera2)
   1.200 -      (runonce visual)))))
   1.201 -
   1.202 -(defn spider-eye [position]
   1.203 -  (doto (Camera. 200 200 )
   1.204 -    (.setFrustumPerspective 45 1 1 1000)
   1.205 -    (.setLocation position)
   1.206 -    (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)))
   1.207 -  
   1.208 -(defn setup-fn* [world]
   1.209 -  (let [eye (camera)
   1.210 -	width (.getWidth eye)
   1.211 -	height (.getHeight eye)]
   1.212 -    ;;(.setClearFlags (.getViewPort world) true true true)
   1.213 -    (observer world (.getCamera world))
   1.214 -    (observer world (spider-eye (Vector3f.  3  0 -5)))
   1.215 -    ;;(observer world (spider-eye (Vector3f.  0  0 -5)))
   1.216 -    ;; (observer world (spider-eye (Vector3f. -3  0 -5)))
   1.217 -    ;; (observer world (spider-eye (Vector3f.  0  3 -5)))
   1.218 -    ;; (observer world (spider-eye (Vector3f.  0 -3 -5)))
   1.219 -    ;; (observer world (spider-eye (Vector3f.  3  3 -5)))
   1.220 -    ;; (observer world (spider-eye (Vector3f. -3  3 -5)))
   1.221 -    ;; (observer world (spider-eye (Vector3f.  3 -3 -5)))
   1.222 -    ;; (observer world (spider-eye (Vector3f. -3 -3 -5)))
   1.223 -
   1.224 -    )
   1.225 -  world)
   1.226 -
   1.227 -(defn test-world []
   1.228 -  (let [thing (box 1 1 1 :physical? false)]
   1.229 -    (world
   1.230 -     (doto (Node.)
   1.231 -       (.attachChild thing))
   1.232 -     {}
   1.233 -     setup-fn
   1.234 -     (fn [world tpf]
   1.235 -       (.rotate thing (* tpf 0.2) 0 0)
   1.236 -       ))))
   1.237 -
   1.238 -
   1.239 -#+end_src
   1.240 -
   1.241 -
   1.242 -#+results: eyes
   1.243 -: #'body.eye/test-world
   1.244 -
   1.245 -Note the use of continuation passing style for connecting the eye to a
   1.246 -function to process the output. The example code will create two
   1.247 -videos of the same rotating cube from different angles, sutiable for
   1.248 -stereoscopic vision.
   1.249 -
   1.250 -
   1.251 -
   1.252 -
   1.253 -
   1.254 -
   1.255  * COMMENT code generation
   1.256 -#+begin_src clojure :tangle ../src/cortex/import.clj
   1.257 -<<import>>
   1.258 -#+end_src
   1.259  
   1.260  #+begin_src clojure :tangle ../src/hello/brick_wall.clj
   1.261  <<brick-wall-header>>
   1.262 @@ -997,13 +755,6 @@
   1.263  #+begin_src clojure :tangle ../src/hello/hello_simple_app.clj
   1.264  <<hello-simple-app>>
   1.265  #+end_src
   1.266 -  
   1.267 -#+begin_src clojure :tangle ../src/cortex/world.clj
   1.268 -<<world-inputs>>
   1.269 -<<world>>
   1.270 -<<world-shapes>>
   1.271 -<<world-view>>
   1.272 -#+end_src
   1.273  
   1.274  #+begin_src clojure :tangle ../src/cortex/other_games.clj
   1.275  <<other-games>>
   1.276 @@ -1029,13 +780,6 @@
   1.277  <<material>>
   1.278  #+end_src
   1.279  
   1.280 -#+begin_src clojure :tangle ../src/body/eye.clj
   1.281 -<<eyes>>
   1.282 -#+end_src
   1.283 -
   1.284 -#+begin_src clojure :tangle ../src/test/vision.clj
   1.285 -<<test-vision>>
   1.286 -#+end_src
   1.287  
   1.288    
   1.289  
     2.1 --- a/org/eyes.org	Mon Oct 24 01:19:15 2011 -0700
     2.2 +++ b/org/eyes.org	Mon Oct 24 05:25:01 2011 -0700
     2.3 @@ -38,8 +38,6 @@
     2.4  (import javax.swing.JOptionPane)
     2.5  (import java.awt.image.ImageObserver)
     2.6  
     2.7 -
     2.8 -
     2.9  (defn scene-processor
    2.10    "deals with converting FrameBuffers to BufferedImages so
    2.11     that the continuation function can be defined only in terms
     3.1 --- a/org/util.org	Mon Oct 24 01:19:15 2011 -0700
     3.2 +++ b/org/util.org	Mon Oct 24 05:25:01 2011 -0700
     3.3 @@ -61,11 +61,13 @@
     3.4  #+results:
     3.5  : 955
     3.6  
     3.7 -** Simplification
     3.8 +
     3.9  #+srcname: world-view
    3.10  #+begin_src clojure :results silent
    3.11 -(in-ns 'cortex.debug)
    3.12 -
    3.13 +(ns cortex.util)
    3.14 +(require 'cortex.import)
    3.15 +(cortex.import/mega-import-jme3)
    3.16 +(use 'cortex.world)
    3.17  (defprotocol Viewable
    3.18    (view [something]))
    3.19  
    3.20 @@ -94,12 +96,6 @@
    3.21  			      (.setColor ColorRGBA/White))]
    3.22  		    (.addLight (.getRootNode world) sun)))
    3.23  		no-op))))
    3.24 -
    3.25 -(defn position-camera [game]
    3.26 -  (doto (.getCamera game)
    3.27 -    (.setLocation (Vector3f. 0 6 6))
    3.28 -    (.lookAt Vector3f/ZERO (Vector3f. 0 1 0))))
    3.29 -
    3.30  #+end_src
    3.31  
    3.32  Here I make the =Viewable= protocol and extend it to JME's types.  Now
    3.33 @@ -110,8 +106,131 @@
    3.34  #+end_src
    3.35  
    3.36  
    3.37 +#+srcname: util
    3.38 +#+begin_src clojure 
    3.39 +(in-ns 'cortex.util)
    3.40 +
    3.41 +(def println-repl (bound-fn [& args] (apply println args)))
    3.42 +
    3.43 +(defn position-camera [game]
    3.44 +  (doto (.getCamera game)
    3.45 +    (.setLocation (Vector3f. 0 6 6))
    3.46 +    (.lookAt Vector3f/ZERO (Vector3f. 0 1 0))))
    3.47 +
    3.48 +(defn set-gravity*
    3.49 +  [game gravity]
    3.50 +  (traverse
    3.51 +   (fn [geom]
    3.52 +     (if-let
    3.53 +	 [control (.getControl geom RigidBodyControl)]
    3.54 +       (do
    3.55 +	 (.setGravity control gravity)
    3.56 +	 (.applyImpulse control Vector3f/ZERO Vector3f/ZERO)
    3.57 +	 )))
    3.58 +   (.getRootNode game)))
    3.59 +#+end_src
    3.60 +
    3.61 +
    3.62 +#+srcname: shapes
    3.63 +#+begin_src clojure :results silent
    3.64 +(in-ns 'cortex.util)
    3.65 +(defrecord shape-description
    3.66 +  [name
    3.67 +   color
    3.68 +   mass
    3.69 +   friction
    3.70 +   texture
    3.71 +   material
    3.72 +   position
    3.73 +   rotation
    3.74 +   shape
    3.75 +   physical?])
    3.76 +
    3.77 +(def base-shape
    3.78 +     (shape-description.
    3.79 +      "default-shape"
    3.80 +      false
    3.81 +      ;;ColorRGBA/Blue
    3.82 +      1.0 ;; mass
    3.83 +      1.0 ;; friction
    3.84 +      ;; texture
    3.85 +      "Textures/Terrain/BrickWall/BrickWall.jpg"
    3.86 +      ;; material
    3.87 +      "Common/MatDefs/Misc/Unshaded.j3md"
    3.88 +      Vector3f/ZERO
    3.89 +      Quaternion/IDENTITY
    3.90 +      (Box. Vector3f/ZERO 0.5 0.5 0.5)
    3.91 +      true))
    3.92 +
    3.93 +(defn make-shape
    3.94 +  [#^shape-description d]
    3.95 +  (let [asset-manager (if (:asset-manager d) (:asset-manager d) (asset-manager))
    3.96 +        mat (Material. asset-manager (:material d))
    3.97 +	geom (Geometry. (:name d) (:shape d))]
    3.98 +    (if (:texture d)
    3.99 +      (let [key (TextureKey. (:texture d))]
   3.100 +	(.setGenerateMips key true)
   3.101 +	(.setTexture mat "ColorMap" (.loadTexture asset-manager key))))
   3.102 +    (if (:color d) (.setColor mat "Color" (:color d)))
   3.103 +    (.setMaterial geom mat)
   3.104 +    (if-let [rotation (:rotation d)] (.rotate geom rotation))
   3.105 +    (.setLocalTranslation geom (:position d))
   3.106 +    (if (:physical? d)
   3.107 +      (let [impact-shape (doto (GImpactCollisionShape. 
   3.108 +                                (.getMesh geom)) (.setMargin 0))
   3.109 +	    physics-control (RigidBodyControl.
   3.110 +			     ;;impact-shape ;; comment to disable 
   3.111 +			     (float (:mass d)))]
   3.112 +	(.createJmeMesh impact-shape)
   3.113 +	(.addControl geom physics-control)
   3.114 +	;;(.setSleepingThresholds physics-control (float 0) (float 0))
   3.115 +	(.setFriction physics-control (:friction d))))
   3.116 +    ;;the default is to keep this node in the physics engine forever.
   3.117 +    ;;these commands must come after the control is added to the geometry.
   3.118 +    ;;
   3.119 +    geom))
   3.120 +  
   3.121 +(defn box
   3.122 +  ([l w h & {:as options}]
   3.123 +     (let [options (merge base-shape options)]
   3.124 +       (make-shape (assoc options
   3.125 +		     :shape (Box. l w h)))))
   3.126 +  ([] (box 0.5 0.5 0.5)))
   3.127 +
   3.128 +(defn sphere
   3.129 +  ([r & {:as options}]
   3.130 +     (let [options (merge base-shape options)]
   3.131 +       (make-shape (assoc options
   3.132 +		     :shape (Sphere. 32 32 (float r)))))) 
   3.133 +  ([] (sphere 0.5)))
   3.134 +
   3.135 +(defn add-element
   3.136 +  ([game element node]
   3.137 +  (.addAll
   3.138 +   (.getPhysicsSpace
   3.139 +    (.getState
   3.140 +     (.getStateManager game)
   3.141 +     BulletAppState))
   3.142 +    element)
   3.143 +  (.attachChild node element))
   3.144 +  ([game element]
   3.145 +     (add-element game element (.getRootNode game))))
   3.146 +
   3.147 +
   3.148 +
   3.149 +#+end_src
   3.150 +
   3.151 +
   3.152  
   3.153  * COMMENT code generation
   3.154  #+begin_src clojure :tangle ../src/cortex/import.clj
   3.155  <<import>>
   3.156  #+end_src
   3.157 +
   3.158 +
   3.159 +#+begin_src clojure :tangle ../src/cortex/util.clj
   3.160 +<<world-view>>
   3.161 +<<util>>
   3.162 +<<shapes>>
   3.163 +#+end_src
   3.164 +
     4.1 --- a/org/world.org	Mon Oct 24 01:19:15 2011 -0700
     4.2 +++ b/org/world.org	Mon Oct 24 05:25:01 2011 -0700
     4.3 @@ -1,26 +1,83 @@
     4.4 -#+title: A world for the creatures to live
     4.5 +#+title: A World for the Creatures
     4.6  #+author: Robert McIntyre
     4.7  #+email: rlm@mit.edu
     4.8 -#+description: Simulating senses for AI research using JMonkeyEngine3
     4.9 +#+description: Creating a Virtual World for AI constructs using clojure and JME3
    4.10 +#+keywords: JME3, clojure, virtual world, exception handling
    4.11  #+SETUPFILE: ../../aurellem/org/setup.org
    4.12  #+INCLUDE: ../../aurellem/org/level-0.org
    4.13  #+babel: :mkdirp yes :noweb yes :exports both
    4.14  
    4.15 +* The  World
    4.16  
    4.17 +There's no point in having senses if there's nothing to experience. In
    4.18 +this section I make some tools with which to build virtual worlds for
    4.19 +my characters to inhabit. If you look at the tutorials at [[http://www.jmonkeyengine.org/wiki/doku.php/jme3:beginner][the jme3
    4.20 +website]], you will see a pattern in how virtual worlds are normally
    4.21 +built. I call this "the Java way" of making worlds.
    4.22  
    4.23 -*** World
    4.24 + - The Java way:  
    4.25 +   - Create a class that extends =SimpleApplication= or =Application=
    4.26 +   - Implement setup functions that create all the scene objects using
    4.27 +     the inherited =assetManager= and call them by overriding the
    4.28 +     =simpleInitApp= method.
    4.29 +   - Create =ActionListeners= and add them to the =inputManager=
    4.30 +     inherited from =Application= to handle key-bindings.
    4.31 +   - Override =simpleUpdate= to implement game logic.
    4.32 +   - Running/Testing an Application involves creating a new JVM,
    4.33 +     running the App, and then closing everything down.
    4.34  
    4.35 -It is comvienent to wrap the JME elements that deal with creating a
    4.36 -world, creation of basic objects, and Keyboard input with a nicer
    4.37 -interface (at least for my purposes).
    4.38  
    4.39 -#+srcname: world-inputs
    4.40 -#+begin_src clojure :results silent
    4.41 -(ns cortex.world)
    4.42 -(require 'cortex.import)
    4.43 -(use 'clojure.contrib.def)
    4.44 -(rlm.rlm-commands/help)
    4.45 -(cortex.import/mega-import-jme3)
    4.46 + - A more Clojureish way:
    4.47 +   - Use a map from keys->functions to specify key-bindings. 
    4.48 +   - Use functions to create objects separately from any particular
    4.49 +     application.
    4.50 +   - Use an REPL -- this means that there's only ever one JVM, and
    4.51 +     Applications come and go.
    4.52 +
    4.53 +Since most development work using jMonkeyEngine is done in Java, jme3
    4.54 +supports "the Java way" quite well out of the box. To work "the
    4.55 +clojure way", it necessary to wrap the jme3 elements that deal with
    4.56 +the Application life-cycle with a REPL driven interface.
    4.57 +
    4.58 +The most important modifications are:
    4.59 +
    4.60 + - Separation of Object life-cycles with the Application life-cycle.
    4.61 + - Functional interface to the underlying =Application= and
    4.62 +   =SimpleApplication= classes.
    4.63 +
    4.64 +** Header
    4.65 +#+srcname: header
    4.66 +#+begin_src clojure :results silent    
    4.67 +(ns cortex.world
    4.68 +  "World Creation, abstracion over jme3's input system, and REPL
    4.69 +  driven exception handling"
    4.70 +  {:author "Robert McIntyre"}
    4.71 +  
    4.72 +  (:use (clojure.contrib (def :only (defvar))))
    4.73 +  (:use [pokemon [lpsolve :only [constant-map]]])
    4.74 +  (:use [clojure.contrib [str-utils :only [re-gsub]]])
    4.75 +
    4.76 +  (:import com.jme3.math.Vector3f)
    4.77 +  (:import com.jme3.scene.Node)
    4.78 +  (:import com.jme3.system.AppSettings)
    4.79 +  (:import com.jme3.system.JmeSystem)
    4.80 +  (:import com.jme3.system.IsoTimer)
    4.81 +  (:import com.jme3.input.KeyInput)
    4.82 +  (:import com.jme3.input.controls.KeyTrigger)
    4.83 +  (:import com.jme3.input.controls.MouseButtonTrigger)
    4.84 +  (:import com.jme3.input.InputManager)
    4.85 +  (:import com.jme3.bullet.BulletAppState)
    4.86 +  (:import com.jme3.shadow.BasicShadowRenderer)
    4.87 +  (:import com.jme3.app.SimpleApplication)
    4.88 +  (:import com.jme3.input.controls.ActionListener)
    4.89 +  (:import com.jme3.renderer.queue.RenderQueue$ShadowMode)
    4.90 +  (:import org.lwjgl.input.Mouse))
    4.91 +#+end_src    
    4.92 +    
    4.93 +** General Settings    
    4.94 +#+srcname: settings    
    4.95 +#+begin_src clojure 
    4.96 +(in-ns 'cortex.world)
    4.97  
    4.98  (defvar *app-settings*
    4.99    (doto (AppSettings. true)
   4.100 @@ -31,7 +88,7 @@
   4.101      )
   4.102    "These settings control how the game is displayed on the screen for
   4.103     debugging purposes.  Use binding forms to change this if desired.
   4.104 -   Full-screen mode does not work on some computers.")
   4.105 +   Full-screen mode does not work on some computers.")    
   4.106  
   4.107  (defn asset-manager
   4.108    "returns a new, configured assetManager" []
   4.109 @@ -39,33 +96,58 @@
   4.110     (.getResource
   4.111      (.getContextClassLoader (Thread/currentThread))
   4.112      "com/jme3/asset/Desktop.cfg")))
   4.113 +#+end_src
   4.114 +
   4.115 +Normally, people just use the =AssetManager= inherited from
   4.116 +=Application= whenever they extend that class. However,
   4.117 +=AssetManagers= are useful on their own to create objects/ materials,
   4.118 +independent from any particular application. =(asset-manager)= makes
   4.119 +object creation less tightly bound to Application initialization.
   4.120 +
   4.121 +
   4.122 +** Exception Protection
   4.123 +#+srcname: exceptions
   4.124 +#+begin_src clojure
   4.125 +(in-ns 'cortex.world)
   4.126  
   4.127  (defmacro no-exceptions
   4.128    "Sweet relief like I never knew."
   4.129    [& forms]
   4.130    `(try ~@forms (catch Exception e# (.printStackTrace e#))))
   4.131  
   4.132 -(defn thread-exception-removal []
   4.133 -  (println "removing exceptions from " (Thread/currentThread))
   4.134 +(defn thread-exception-removal
   4.135 +  "Exceptions thrown in the graphics rendering thread generally cause
   4.136 +  the entire REPL to crash! It is good to suppress them while trying
   4.137 +  things out to shorten the debug loop."
   4.138 +  []
   4.139    (.setUncaughtExceptionHandler
   4.140     (Thread/currentThread)
   4.141     (proxy [Thread$UncaughtExceptionHandler] []
   4.142       (uncaughtException
   4.143 -      [thread thrown]
   4.144 -      (println "uncaught-exception thrown in " thread)
   4.145 -      (println (.getMessage thrown))))))
   4.146 +       [thread thrown]
   4.147 +       (println "uncaught-exception thrown in " thread)
   4.148 +       (println (.getMessage thrown))))))
   4.149  
   4.150 -(def println-repl (bound-fn [& args] (apply println args)))
   4.151 +#+end_src
   4.152  
   4.153 -(use '[pokemon [lpsolve :only [constant-map]]])
   4.154 +Exceptions thrown in the LWJGL render thread, if not caught, will
   4.155 +destroy the entire JVM process including the REPL and slow development
   4.156 +to a crawl.  It is better to try to continue on in the face of
   4.157 +exceptions and keep the REPL alive as long as possible.  Normally it
   4.158 +is possible to just exit the faulty Application, fix the bug,
   4.159 +reevaluate the appropriate forms, and be on your way, without
   4.160 +restarting the JVM.
   4.161  
   4.162 -(defn no-op [& _])
   4.163 +** Input
   4.164 +#+srcname: input
   4.165 +#+begin_src clojure
   4.166 +(in-ns 'cortex.world)
   4.167  
   4.168  (defn all-keys
   4.169 -  "Construct a map of strings representing all the manual inputs from
   4.170 -   either the keyboard or mouse." 
   4.171 +  "Uses reflection to generate a map of string names to jme3 trigger
   4.172 +  objects, which govern input from the keyboard and mouse"
   4.173    []
   4.174 -  (let [inputs (constant-map KeyInput)]
   4.175 +  (let [inputs (pokemon.lpsolve/constant-map KeyInput)]
   4.176      (assoc
   4.177  	(zipmap (map (fn [field]
   4.178  		       (.toLowerCase (re-gsub #"_" "-" field))) (vals inputs))
   4.179 @@ -76,21 +158,26 @@
   4.180        "mouse-right" (MouseButtonTrigger. 1))))
   4.181  
   4.182  (defn initialize-inputs
   4.183 -  "more java-interop cruft to establish keybindings for a particular virtual world"
   4.184 +  "Establish key-bindings for a particular virtual world."
   4.185    [game  input-manager key-map]
   4.186 -  (doall (map (fn [[name trigger]]
   4.187 -		(.addMapping ^InputManager input-manager
   4.188 -			     name (into-array (class trigger) [trigger]))) key-map))
   4.189 -  (doall (map (fn [name] 
   4.190 -		(.addListener ^InputManager input-manager game
   4.191 -			      (into-array String  [name]))) (keys key-map))))
   4.192 +  (doall
   4.193 +   (map (fn [[name trigger]]
   4.194 +          (.addMapping
   4.195 +           ^InputManager input-manager
   4.196 +           name (into-array (class trigger)
   4.197 +                            [trigger]))) key-map))
   4.198 +  (doall
   4.199 +   (map (fn [name] 
   4.200 +          (.addListener
   4.201 +           ^InputManager input-manager game
   4.202 +           (into-array String  [name]))) (keys key-map))))
   4.203  
   4.204  #+end_src
   4.205  
   4.206 -These functions are all for debug controlling of the world through
   4.207 -keyboard and mouse. 
   4.208 +These functions are for controlling the world through the keyboard and
   4.209 +mouse.
   4.210  
   4.211 -We reuse  =constant-map= from =pokemon.lpsolve= to get the numerical
   4.212 +I reuse =constant-map= from [[../../pokemon-types/html/lpsolve.html#sec-3-3-4][=pokemon.lpsolve=]] to get the numerical
   4.213  values for all the keys defined in the =KeyInput= class. The
   4.214  documentation for =constant-map= is:
   4.215  
   4.216 @@ -106,17 +193,63 @@
   4.217  :   fields with their names.  This helps with C wrappers where they have
   4.218  :   just defined a bunch of integer constants instead of enums
   4.219  
   4.220 +=(all-keys)= converts the constant names like =KEY_J= to the more
   4.221 +clojure-like =key-j=, and returns a map from these keys to
   4.222 +jMonkeyEngine =KeyTrigger= objects, which jMonkeyEngine3 uses as it's
   4.223 +abstraction over the physical keys. =all-keys= also adds the three
   4.224 +mouse button controls to the map.
   4.225  
   4.226 -Then, =all-keys= converts the constant names like =KEY_J= to the more
   4.227 -clojure-like =key-j=, and returns a map from these keys to
   4.228 -jMonkeyEngine KeyTrigger objects, the use of which will soon become
   4.229 -apparent. =all-keys= also adds the three mouse button controls to the
   4.230 -map.
   4.231  
   4.232 +#+begin_src clojure :exports both :results output
   4.233 +(require 'clojure.contrib.pprint)
   4.234 +(clojure.contrib.pprint/pprint
   4.235 + (take 10 (keys (cortex.world/all-keys))))
   4.236 +#+end_src
   4.237 +
   4.238 +#+results:
   4.239 +#+begin_example
   4.240 +("key-n"
   4.241 + "key-apps"
   4.242 + "key-pgup"
   4.243 + "key-f8"
   4.244 + "key-o"
   4.245 + "key-at"
   4.246 + "key-f9"
   4.247 + "key-0"
   4.248 + "key-p"
   4.249 + "key-subtract")
   4.250 +#+end_example
   4.251 +
   4.252 +#+begin_src clojure :exports both :results output
   4.253 +(clojure.contrib.pprint/pprint
   4.254 + (take 10 (vals (cortex.world/all-keys))))
   4.255 +#+end_src
   4.256 +
   4.257 +#+results:
   4.258 +#+begin_example
   4.259 +(#<KeyTrigger com.jme3.input.controls.KeyTrigger@6ec21e52>
   4.260 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@a54d24d>
   4.261 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@1ba5e91b>
   4.262 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@296af9cb>
   4.263 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@2e3593ab>
   4.264 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@3f71d740>
   4.265 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@4aeacb4a>
   4.266 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@7cc88db2>
   4.267 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@52cee11e>
   4.268 + #<KeyTrigger com.jme3.input.controls.KeyTrigger@c1da30b>)
   4.269 +#+end_example
   4.270 +
   4.271 +
   4.272 +
   4.273 +** World Creation
   4.274  #+srcname: world
   4.275  #+begin_src clojure :results silent
   4.276  (in-ns 'cortex.world)
   4.277  
   4.278 +(defn no-op
   4.279 +  "Takes any number of arguments and does nothing."
   4.280 +  [& _])
   4.281 +
   4.282  (defn traverse
   4.283    "apply f to every non-node, deeply"
   4.284    [f node]
   4.285 @@ -124,196 +257,129 @@
   4.286      (dorun (map (partial traverse f) (.getChildren node)))
   4.287      (f node)))
   4.288  
   4.289 -(def gravity (Vector3f. 0 -9.81 0))
   4.290 +(defn world
   4.291 +  "the =world= function takes care of the details of initializing a
   4.292 +  SimpleApplication.
   4.293  
   4.294 -(defn world
   4.295 +   ***** Arguments:
   4.296 +
   4.297 +   - root-node : a com.jme3.scene.Node object which contains all of
   4.298 +       the objects that should be in the simulation.
   4.299 +
   4.300 +   - key-map : a map from strings describing keys to functions that
   4.301 +       should be executed whenever that key is pressed.
   4.302 +       the functions should take a SimpleApplication object and a
   4.303 +       boolean value.  The SimpleApplication is the current simulation
   4.304 +       that is running, and the boolean is true if the key is being
   4.305 +       pressed, and false if it is being released. As an example,
   4.306 +
   4.307 +       {\"key-j\" (fn [game value] (if value (println \"key j pressed\")))}
   4.308 +
   4.309 +       is a valid key-map which will cause the simulation to print a
   4.310 +       message whenever the 'j' key on the keyboard is pressed.
   4.311 +
   4.312 +   - setup-fn : a function that takes a SimpleApplication object. It
   4.313 +       is called once when initializing the simulation. Use it to
   4.314 +       create things like lights, change the gravity, initialize debug
   4.315 +       nodes, etc.
   4.316 +
   4.317 +   - update-fn : this function takes a SimpleApplication object and a
   4.318 +       float and is called every frame of the simulation.  The float
   4.319 +       tells how many seconds is has been since the last frame was
   4.320 +       rendered, according to whatever clock jme is currently
   4.321 +       using. The default is to use IsoTimer which will result in this
   4.322 +       value always being the same.
   4.323 +  "
   4.324    [root-node key-map setup-fn update-fn]
   4.325    (let [physics-manager (BulletAppState.)
   4.326 -	shadow-renderer (BasicShadowRenderer. (asset-manager) (int 256))
   4.327 -	;;maybe use a better shadow renderer someday!
   4.328 -	;;shadow-renderer (PssmShadowRenderer. (asset-manager) 256 1)
   4.329 -	]   	
   4.330 -  (doto
   4.331 -      (proxy [SimpleApplication ActionListener] []
   4.332 -	(simpleInitApp
   4.333 -	 []
   4.334 -	 (no-exceptions
   4.335 -	  (.setTimer this (IsoTimer. 60))
   4.336 -	  ;; Create key-map.
   4.337 -	  (.setFrustumFar (.getCamera this) 300)
   4.338 -	  (initialize-inputs this (.getInputManager this) (all-keys))
   4.339 -	  ;; Don't take control of the mouse
   4.340 -	  (org.lwjgl.input.Mouse/setGrabbed false)
   4.341 -	  ;; add all objects to the world
   4.342 -	  (.attachChild (.getRootNode this) root-node)
   4.343 -	  ;; enable physics
   4.344 -	  ;; add a physics manager
   4.345 -	  (.attach (.getStateManager this) physics-manager)
   4.346 -	  (.setGravity (.getPhysicsSpace physics-manager) gravity)
   4.347 -
   4.348 -
   4.349 -	  ;; go through every object and add it to the physics manager
   4.350 -	  ;; if relavant.
   4.351 -	   (traverse (fn [geom]
   4.352 -		       (dorun
   4.353 -			(for [n (range (.getNumControls geom))]
   4.354 -			  (do
   4.355 -			    (println-repl "adding control " (.getControl geom n))
   4.356 -			    (.add (.getPhysicsSpace physics-manager)
   4.357 -				  (.getControl geom n))))))
   4.358 -		     (.getRootNode this))
   4.359 -	  ;;(.addAll (.getPhysicsSpace physics-manager) (.getRootNode this))
   4.360 -
   4.361 -	  (setup-fn this)
   4.362 -	  (.setDirection shadow-renderer
   4.363 -			 (.normalizeLocal (Vector3f. -1 -1 -1)))
   4.364 -	  (.addProcessor (.getViewPort this) shadow-renderer)
   4.365 -	  (.setShadowMode (.getRootNode this) RenderQueue$ShadowMode/Off)
   4.366 -	  ))
   4.367 -	(simpleUpdate
   4.368 -	 [tpf]
   4.369 -	 (no-exceptions
   4.370 -	  (update-fn this tpf))) 
   4.371 -	(onAction
   4.372 -	 [binding value tpf]
   4.373 -	 ;; whenever a key is pressed, call the function returned from
   4.374 -	 ;; key-map.
   4.375 -	 (no-exceptions
   4.376 -	  (if-let [react (key-map binding)]
   4.377 -	    (react this value)))))
   4.378 -    ;; don't show a menu to change options.
   4.379 -    
   4.380 -    (.setShowSettings false)
   4.381 -    (.setPauseOnLostFocus false)
   4.382 -    (.setSettings *app-settings*))))
   4.383 +	shadow-renderer (BasicShadowRenderer.
   4.384 +                         (asset-manager) (int 256))]   	
   4.385 +    (doto
   4.386 +        (proxy [SimpleApplication ActionListener] []
   4.387 +          (simpleInitApp
   4.388 +            []
   4.389 +            (no-exceptions
   4.390 +             ;; allow AI entities as much time as they need to think.
   4.391 +             (.setTimer this (IsoTimer. 60))
   4.392 +             (.setFrustumFar (.getCamera this) 300)
   4.393 +             ;; Create default key-map.
   4.394 +             (initialize-inputs this (.getInputManager this) (all-keys))
   4.395 +             ;; Don't take control of the mouse
   4.396 +             (org.lwjgl.input.Mouse/setGrabbed false)
   4.397 +             ;; add all objects to the world
   4.398 +             (.attachChild (.getRootNode this) root-node)
   4.399 +             ;; enable physics
   4.400 +             ;; add a physics manager
   4.401 +             (.attach (.getStateManager this) physics-manager)
   4.402 +             (.setGravity (.getPhysicsSpace physics-manager) 
   4.403 +                          (Vector3f. 0 -9.81 0))
   4.404 +             ;; go through every object and add it to the physics
   4.405 +             ;; manager if relevant.
   4.406 +             (traverse (fn [geom]
   4.407 +                         (dorun
   4.408 +                          (for [n (range (.getNumControls geom))]
   4.409 +                            (do
   4.410 +                              (.add (.getPhysicsSpace physics-manager)
   4.411 +                                    (.getControl geom n))))))
   4.412 +                       (.getRootNode this))
   4.413 +             ;;(.addAll (.getPhysicsSpace physics-manager) (.getRootNode this))
   4.414 +             
   4.415 +             ;; set some basic defaults for the shadow renderer.
   4.416 +             ;; these can be undone in the setup function
   4.417 +             (.setDirection shadow-renderer
   4.418 +                            (.normalizeLocal (Vector3f. -1 -1 -1)))
   4.419 +             (.addProcessor (.getViewPort this) shadow-renderer)
   4.420 +             (.setShadowMode (.getRootNode this)
   4.421 +                             RenderQueue$ShadowMode/Off)
   4.422 +             ;; call the supplied setup-fn
   4.423 +             (if setup-fn
   4.424 +               (setup-fn this))))
   4.425 +          (simpleUpdate
   4.426 +            [tpf]
   4.427 +            (no-exceptions
   4.428 +             (update-fn this tpf))) 
   4.429 +          (onAction
   4.430 +            [binding value tpf]
   4.431 +            ;; whenever a key is pressed, call the function returned
   4.432 +            ;; from key-map.
   4.433 +            (no-exceptions
   4.434 +             (if-let [react (key-map binding)]
   4.435 +               (react this value)))))
   4.436 +      ;; don't show a menu to change options.      
   4.437 +      (.setShowSettings false)
   4.438 +      ;; continue running simulation even if the window has lost
   4.439 +      ;; focus.
   4.440 +      (.setPauseOnLostFocus false)
   4.441 +      (.setSettings *app-settings*))))
   4.442  
   4.443  (defn apply-map
   4.444 -  "Like apply, but works for maps and functions that expect an implicit map
   4.445 -   and nothing else as in (fn [& {}]).
   4.446 -------- Example -------
   4.447 - (defn jjj [& {:keys [www] :or {www \"oh yeah\"} :as env}] (println www))
   4.448 - (apply-map jjj {:www \"whatever\"})
   4.449 -  -->\"whatever\""
   4.450 +  "Like apply, but works for maps and functions that expect an
   4.451 +   implicit map and nothing else as in (fn [& {}]).
   4.452 +   ------- Example -------
   4.453 +   (defn demo [& {:keys [www] :or {www \"oh yeah\"} :as env}] 
   4.454 +     (println www))
   4.455 +   (apply-map demo {:www \"hello!\"})
   4.456 +   -->\"hello\""
   4.457    [fn m]
   4.458    (apply fn (reduce #(into %1 %2) [] m)))
   4.459  
   4.460  #+end_src 
   4.461  
   4.462  
   4.463 -=world= is the most important function here.  
   4.464 -***  TODO more documentation
   4.465 -
   4.466 -#+srcname: world-shapes
   4.467 -#+begin_src clojure :results silent
   4.468 -(in-ns 'cortex.world)
   4.469 -(defrecord shape-description
   4.470 -  [name
   4.471 -   color
   4.472 -   mass
   4.473 -   friction
   4.474 -   texture
   4.475 -   material
   4.476 -   position
   4.477 -   rotation
   4.478 -   shape
   4.479 -   physical?])
   4.480 -
   4.481 -(def base-shape
   4.482 -     (shape-description.
   4.483 -      "default-shape"
   4.484 -      false
   4.485 -      ;;ColorRGBA/Blue
   4.486 -      1.0 ;; mass
   4.487 -      1.0 ;; friction
   4.488 -      ;; texture
   4.489 -      "Textures/Terrain/BrickWall/BrickWall.jpg"
   4.490 -      ;; material
   4.491 -      "Common/MatDefs/Misc/Unshaded.j3md"
   4.492 -      Vector3f/ZERO
   4.493 -      Quaternion/IDENTITY
   4.494 -      (Box. Vector3f/ZERO 0.5 0.5 0.5)
   4.495 -      true))
   4.496 -
   4.497 -(defn make-shape
   4.498 -  [#^shape-description d]
   4.499 -  (let [asset-manager (if (:asset-manager d) (:asset-manager d) (asset-manager))
   4.500 -        mat (Material. asset-manager (:material d))
   4.501 -	geom (Geometry. (:name d) (:shape d))]
   4.502 -    (if (:texture d)
   4.503 -      (let [key (TextureKey. (:texture d))]
   4.504 -	(.setGenerateMips key true)
   4.505 -	(.setTexture mat "ColorMap" (.loadTexture asset-manager key))))
   4.506 -    (if (:color d) (.setColor mat "Color" (:color d)))
   4.507 -    (.setMaterial geom mat)
   4.508 -    (if-let [rotation (:rotation d)] (.rotate geom rotation))
   4.509 -    (.setLocalTranslation geom (:position d))
   4.510 -    (if (:physical? d)
   4.511 -      (let [impact-shape (doto (GImpactCollisionShape. 
   4.512 -                                (.getMesh geom)) (.setMargin 0))
   4.513 -	    physics-control (RigidBodyControl.
   4.514 -			     ;;impact-shape ;; comment to disable 
   4.515 -			     (float (:mass d)))]
   4.516 -	(.createJmeMesh impact-shape)
   4.517 -	(.addControl geom physics-control)
   4.518 -	;;(.setSleepingThresholds physics-control (float 0) (float 0))
   4.519 -	(.setFriction physics-control (:friction d))))
   4.520 -    ;;the default is to keep this node in the physics engine forever.
   4.521 -    ;;these commands must come after the control is added to the geometry.
   4.522 -    ;;
   4.523 -    geom))
   4.524 -  
   4.525 -(defn box
   4.526 -  ([l w h & {:as options}]
   4.527 -     (let [options (merge base-shape options)]
   4.528 -       (make-shape (assoc options
   4.529 -		     :shape (Box. l w h)))))
   4.530 -  ([] (box 0.5 0.5 0.5)))
   4.531 -
   4.532 -(defn sphere
   4.533 -  ([r & {:as options}]
   4.534 -     (let [options (merge base-shape options)]
   4.535 -       (make-shape (assoc options
   4.536 -		     :shape (Sphere. 32 32 (float r)))))) 
   4.537 -  ([] (sphere 0.5)))
   4.538 -
   4.539 -(defn add-element
   4.540 -  ([game element node]
   4.541 -  (.addAll
   4.542 -   (.getPhysicsSpace
   4.543 -    (.getState
   4.544 -     (.getStateManager game)
   4.545 -     BulletAppState))
   4.546 -    element)
   4.547 -  (.attachChild node element))
   4.548 -  ([game element]
   4.549 -     (add-element game element (.getRootNode game))))
   4.550 -
   4.551 -
   4.552 -(defn set-gravity*
   4.553 -  [game gravity]
   4.554 -  (traverse
   4.555 -   (fn [geom]
   4.556 -     (if-let
   4.557 -	 [control (.getControl geom RigidBodyControl)]
   4.558 -       (do
   4.559 -	 (.setGravity control gravity)
   4.560 -	 (.applyImpulse control Vector3f/ZERO Vector3f/ZERO)
   4.561 -	 )))
   4.562 -   (.getRootNode game)))
   4.563 -
   4.564 -#+end_src
   4.565 -
   4.566 -These are convienence functions for creating JME objects and
   4.567 -manipulating a world.
   4.568 -
   4.569 -
   4.570 +=(world)= is the most important function here. It presents a more
   4.571 +functional interface to the Application life-cycle, and all it's
   4.572 +objects except =root-node= are plain clojure data structures. It's now
   4.573 +possible to extend functionally by composing multiple functions
   4.574 +together, and to add more keyboard-driven actions by combining clojure
   4.575 +maps.
   4.576  
   4.577  
   4.578  
   4.579  * COMMENT code generation
   4.580 -
   4.581  #+begin_src clojure :tangle ../src/cortex/world.clj
   4.582 -<<world-inputs>>
   4.583 +<<header>>
   4.584 +<<settings>>
   4.585 +<<exceptions>>
   4.586 +<<input>>
   4.587  <<world>>
   4.588 -<<world-shapes>>
   4.589  #+end_src