diff org/cortex.org @ 0:92f8d83b5d0b

initial import: I've made hearing and vision, and am working on touch.
author Robert McIntyre <rlm@mit.edu>
date Sun, 16 Oct 2011 05:12:19 -0700
parents
children 50c92af2018e
line wrap: on
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/org/cortex.org	Sun Oct 16 05:12:19 2011 -0700
     1.3 @@ -0,0 +1,1738 @@
     1.4 +#+title: Simulated Senses
     1.5 +#+author: Robert McIntyre
     1.6 +#+email: rlm@mit.edu
     1.7 +#+MATHJAX: align:"left" mathml:t path:"../aurellem/src/MathJax/MathJax.js"
     1.8 +#+STYLE: <link rel="stylesheet" type="text/css" href="../aurellem/src/css/aurellem.css"/>
     1.9 +#+BABEL: :exports both :noweb yes :cache no :mkdirp yes
    1.10 +#+INCLUDE: ../aurellem/src/templates/level-0.org
    1.11 +#+description: Simulating senses for AI research using JMonkeyEngine3
    1.12 +
    1.13 +* Background
    1.14 +Artificial Intelligence has tried and failed for more than half a
    1.15 +century to produce programs as flexible, creative, and “intelligent”
    1.16 +as the human mind itself. Clearly, we are still missing some important
    1.17 +ideas concerning intelligent programs or we would have strong AI
    1.18 +already. What idea could be missing?
    1.19 +
    1.20 +When Turing first proposed his famous “Turing Test” in the
    1.21 +groundbreaking paper [[./sources/turing.pdf][/Computing Machines and Intelligence/]], he gave
    1.22 +little importance to how a computer program might interact with the
    1.23 +world:
    1.24 +
    1.25 +#+BEGIN_QUOTE
    1.26 +\ldquo{}We need not be too concerned about the legs, eyes, etc. The example of
    1.27 +Miss Helen Keller shows that education can take place provided that
    1.28 +communication in both directions between teacher and pupil can take
    1.29 +place by some means or other.\rdquo{}
    1.30 +#+END_QUOTE
    1.31 +
    1.32 +And from the example of Hellen Keller he went on to assume that the
    1.33 +only thing a fledgling AI program could need by way of communication
    1.34 +is a teletypewriter. But Hellen Keller did possess vision and hearing
    1.35 +for the first few months of her life, and her tactile sense was far
    1.36 +more rich than any text-stream could hope to achieve. She possessed a
    1.37 +body she could move freely, and had continual access to the real world
    1.38 +to learn from her actions.
    1.39 +
    1.40 +I believe that our programs are suffering from too little sensory
    1.41 +input to become really intelligent. Imagine for a moment that you
    1.42 +lived in a world completely cut off form all sensory stimulation. You
    1.43 +have no eyes to see, no ears to hear, no mouth to speak. No body, no
    1.44 +taste, no feeling whatsoever. The only sense you get at all is a
    1.45 +single point of light, flickering on and off in the void. If this was
    1.46 +your life from birth, you would never learn anything, and could never
    1.47 +become intelligent. Actual humans placed in sensory deprivation
    1.48 +chambers experience hallucinations and can begin to loose their sense
    1.49 +of reality in as little as 15 minutes[sensory-deprivation]. Most of
    1.50 +the time, the programs we write are in exactly this situation. They do
    1.51 +not interface with cameras and microphones, and they do not control a
    1.52 +real or simulated body or interact with any sort of world.
    1.53 +
    1.54 +
    1.55 +* Simulation vs. Reality
    1.56 +I want demonstrate that multiple senses are what enable
    1.57 +intelligence. There are two ways of playing around with senses and
    1.58 +computer programs:
    1.59 +
    1.60 +The first is to go entirely with simulation: virtual world, virtual
    1.61 +character, virtual senses. The advantages are that when everything is
    1.62 +a simulation, experiments in that simulation are absolutely
    1.63 +reproducible. It's also easier to change the character and world to
    1.64 +explore new situations and different sensory combinations.
    1.65 +
    1.66 +
    1.67 +** Issues with Simulation
    1.68 +
    1.69 +If the world is to be simulated on a computer, then not only do you
    1.70 +have to worry about whether the character's senses are rich enough to
    1.71 +learn from the world, but whether the world itself is rendered with
    1.72 +enough detail and realism to give enough working material to the
    1.73 +character's senses. To name just a few difficulties facing modern
    1.74 +physics simulators: destructibility of the environment, simulation of
    1.75 +water/other fluids, large areas, nonrigid bodies, lots of objects,
    1.76 +smoke. I don't know of any computer simulation that would allow a
    1.77 +character to take a rock and grind it into fine dust, then use that
    1.78 +dust to make a clay sculpture, at least not without spending years
    1.79 +calculating the interactions of every single small grain of
    1.80 +dust. Maybe a simulated world with today's limitations doesn't provide
    1.81 +enough richness for real intelligence to evolve.
    1.82 +
    1.83 +** Issues with Reality
    1.84 +
    1.85 +The other approach for playing with senses is to hook your software up
    1.86 +to real cameras, microphones, robots, etc., and let it loose in the
    1.87 +real world. This has the advantage of eliminating concerns about
    1.88 +simulating the world at the expense of increasing the complexity of
    1.89 +implementing the senses. Instead of just grabbing the current rendered
    1.90 +frame for processing, you have to use an actual camera with real
    1.91 +lenses and interact with photons to get an image. It is much harder to
    1.92 +change the character, which is now partly a physical robot of some
    1.93 +sort, since doing so involves changing things around in the real world
    1.94 +instead of modifying lines of code. While the real world is very rich
    1.95 +and definitely provides enough stimulation for intelligence to develop
    1.96 +as evidenced by our own existence, it is also uncontrollable in the
    1.97 +sense that a particular situation cannot be recreated perfectly or
    1.98 +saved for later use. It is harder to conduct science because it is
    1.99 +harder to repeat an experiment. The worst thing about using the real
   1.100 +world instead of a simulation is the matter of time. Instead of
   1.101 +simulated time you get the constant and unstoppable flow of real
   1.102 +time. This severely limits the sorts of software you can use to
   1.103 +program the AI because all sense inputs must be handled in real
   1.104 +time. Complicated ideas may have to be implemented in hardware or may
   1.105 +simply be impossible given the current speed of our
   1.106 +processors. Contrast this with a simulation, in which the flow of time
   1.107 +in the simulated world can be slowed down to accommodate the
   1.108 +limitations of the character's programming. In terms of cost, doing
   1.109 +everything in software is far cheaper than building custom real-time
   1.110 +hardware. All you need is a laptop and some patience.
   1.111 +
   1.112 +* Choose a Simulation Engine
   1.113 +
   1.114 +Mainly because of issues with controlling the flow of time, I chose to
   1.115 +simulate both the world and the character. I set out to make a minimal
   1.116 +world in which I could embed a character with multiple senses. My main
   1.117 +goal is to make an environment where I can perform further experiments
   1.118 +in simulated senses.
   1.119 +
   1.120 +As Carl Sagan once said, "If you wish to make an apple pie from
   1.121 +scratch, you must first invent the universe.” I examined many
   1.122 +different 3D environments to try and find something I would use as the
   1.123 +base for my simulation; eventually the choice came down to three
   1.124 +engines: the Quake II engine, the Source Engine, and jMonkeyEngine.
   1.125 +
   1.126 +** Quake II/Jake2
   1.127 +
   1.128 +I spent a bit more than a month working with the Quake II Engine from
   1.129 +ID software to see if I could use it for my purposes. All the source
   1.130 +code was released by ID software into the Public Domain several years
   1.131 +ago, and as a result it has been ported and modified for many
   1.132 +different reasons. This engine was famous for its advanced use of
   1.133 +realistic shading and had decent and fast physics
   1.134 +simulation. Researchers at Princeton [[http://www.nature.com/nature/journal/v461/n7266/pdf/nature08499.pdf][used this code]] to study spatial
   1.135 +information encoding in the hippocampal cells of rats. Those
   1.136 +researchers created a special Quake II level that simulated a maze,
   1.137 +and added an interface where a mouse could run around inside a ball in
   1.138 +various directions to move the character in the simulated maze. They
   1.139 +measured hippocampal activity during this exercise to try and tease
   1.140 +out the method in which spatial data was stored in that area of the
   1.141 +brain. I find this promising because if a real living rat can interact
   1.142 +with a computer simulation of a maze in the same way as it interacts
   1.143 +with a real-world maze, then maybe that simulation is close enough to
   1.144 +reality that a simulated sense of vision and motor control interacting
   1.145 +with that simulation could reveal useful information about the real
   1.146 +thing. It happens that there is a Java port of the original C source
   1.147 +code called Jake2. The port demonstrates Java's OpenGL bindings and
   1.148 +runs anywhere from 90% to 105% as fast as the C version. After
   1.149 +reviewing much of the source of Jake2, I eventually rejected it
   1.150 +because the engine is too tied to the concept of a first-person
   1.151 +shooter game. One of the problems I had was that there does not seem
   1.152 +to be any easy way to attach multiple cameras to a single
   1.153 +character. There are also several physics clipping issues that are
   1.154 +corrected in a way that only applies to the main character and does
   1.155 +not apply to arbitrary objects. While there is a large community of
   1.156 +level modders, I couldn't find a community to support using the engine
   1.157 +to make new things.
   1.158 +
   1.159 +** Source Engine
   1.160 +
   1.161 +The Source Engine evolved from the Quake II and Quake I engines and is
   1.162 +used by Valve in the Half-Life series of games. The physics simulation
   1.163 +in the Source Engine is quite accurate and probably the best out of
   1.164 +all the engines I investigated. There is also an extensive community
   1.165 +actively working with the engine. However, applications that use the
   1.166 +Source Engine must be written in C++, the code is not open, it only
   1.167 +runs on Windows, and the tools that come with the SDK to handle models
   1.168 +and textures are complicated and awkward to use.
   1.169 +
   1.170 +** jMonkeyEngine
   1.171 +
   1.172 +jMonkeyEngine is a new library for creating games in Java. It uses
   1.173 +OpenGL to render to the screen and uses screengraphs to avoid drawing
   1.174 +things that do not appear on the screen. It has an active community
   1.175 +and several games in the pipeline. The engine was not built to serve
   1.176 +any particular game but is instead meant to be used for any 3D
   1.177 +game. After experimenting with each of these three engines and a few
   1.178 +others for about 2 months I settled on jMonkeyEngine. I chose it
   1.179 +because it had the most features out of all the open projects I looked
   1.180 +at, and because I could then write my code in Clojure, an
   1.181 +implementation of LISP that runs on the JVM.
   1.182 +
   1.183 +* Setup
   1.184 +
   1.185 +First, I checked out the source to jMonkeyEngine:
   1.186 +
   1.187 +#+srcname: checkout 
   1.188 +#+begin_src sh :results verbatim
   1.189 +svn checkout http://jmonkeyengine.googlecode.com/svn/trunk/engine jme3
   1.190 +#+end_src
   1.191 +
   1.192 +#+results: checkout
   1.193 +: Checked out revision 7975.
   1.194 +
   1.195 +
   1.196 +Building jMonkeyEngine is easy enough:
   1.197 +
   1.198 +#+srcname: build
   1.199 +#+begin_src sh :results verbatim
   1.200 +cd jme3
   1.201 +ant jar | tail -n 2
   1.202 +#+end_src
   1.203 +
   1.204 +#+results: build
   1.205 +: BUILD SUCCESSFUL
   1.206 +: Total time: 15 seconds
   1.207 +
   1.208 +
   1.209 +Also build the javadoc:
   1.210 +
   1.211 +#+srcname: javadoc
   1.212 +#+begin_src sh :results verbatim
   1.213 +cd jme3
   1.214 +ant javadoc | tail -n 2
   1.215 +#+end_src
   1.216 +
   1.217 +#+results: javadoc
   1.218 +: BUILD SUCCESSFUL
   1.219 +: Total time: 12 seconds
   1.220 +
   1.221 +Now, move the jars from the compilation into the project's lib folder.
   1.222 +
   1.223 +#+srcname: move-jars
   1.224 +#+begin_src sh :results verbatim
   1.225 +mkdir -p lib 
   1.226 +mkdir -p src
   1.227 +cp jme3/dist/jMonkeyEngine3.jar lib/
   1.228 +cp jme3/dist/lib/* lib/
   1.229 +ls lib
   1.230 +#+end_src
   1.231 +
   1.232 +#+results: move-jars
   1.233 +#+begin_example
   1.234 +eventbus-1.4.jar
   1.235 +jbullet.jar
   1.236 +jheora-jst-debug-0.6.0.jar
   1.237 +jinput.jar
   1.238 +jME3-jbullet.jar
   1.239 +jME3-lwjgl-natives.jar
   1.240 +jME3-testdata.jar
   1.241 +jME3-test.jar
   1.242 +jMonkeyEngine3.jar
   1.243 +j-ogg-oggd.jar
   1.244 +j-ogg-vorbisd.jar
   1.245 +lwjgl.jar
   1.246 +nifty-1.3.jar
   1.247 +nifty-default-controls-1.3.jar
   1.248 +nifty-examples-1.3.jar
   1.249 +nifty-lwjgl-renderer-1.3.jar
   1.250 +nifty-openal-soundsystem-1.0.jar
   1.251 +nifty-style-black-1.3.jar
   1.252 +nifty-style-grey-1.0.jar
   1.253 +noise-0.0.1-SNAPSHOT.jar
   1.254 +stack-alloc.jar
   1.255 +vecmath.jar
   1.256 +xmlpull-xpp3-1.1.4c.jar
   1.257 +#+end_example
   1.258 +
   1.259 +It's good to create a =assets= directory in the style that the
   1.260 +=AssetManager= will like.
   1.261 +
   1.262 +#+srcname: create-assets
   1.263 +#+begin_src sh :results verbatim
   1.264 +mkdir -p assets
   1.265 +mkdir -p assets/Interface
   1.266 +mkdir -p assets/Materials
   1.267 +mkdir -p assets/MatDefs
   1.268 +mkdir -p assets/Models
   1.269 +mkdir -p assets/Scenes
   1.270 +mkdir -p assets/Shaders
   1.271 +mkdir -p assets/Sounds
   1.272 +mkdir -p assets/Textures
   1.273 +tree -L 1 assets
   1.274 +#+end_src
   1.275 +
   1.276 +#+results: create-assets
   1.277 +#+begin_example
   1.278 +assets
   1.279 +|-- Interface
   1.280 +|-- MatDefs
   1.281 +|-- Materials
   1.282 +|-- Models
   1.283 +|-- Scenes
   1.284 +|-- Shaders
   1.285 +|-- Sounds
   1.286 +`-- Textures
   1.287 +
   1.288 +8 directories, 0 files
   1.289 +#+end_example
   1.290 +
   1.291 +
   1.292 +The java classpath should have all the jars contained in the =lib=
   1.293 +directory as well as the src directory.
   1.294 +
   1.295 +For example, here is the file I use to run my REPL for clojure.
   1.296 +
   1.297 +#+include: "~/swank-all" src sh :exports code
   1.298 +
   1.299 +The important thing here is that =cortex/lib/*=, =cortex/src=, and
   1.300 +=cortex/assets= appear on the classpath. (=cortex= is the base
   1.301 +directory of this project.)
   1.302 +
   1.303 +#+srcname: pwd
   1.304 +#+begin_src sh 
   1.305 +pwd
   1.306 +#+end_src
   1.307 +
   1.308 +#+results: pwd
   1.309 +: /home/r/cortex
   1.310 +
   1.311 +
   1.312 +* Simulation Base
   1.313 +  
   1.314 +** Imports
   1.315 +First, I'll import jme core classes. 
   1.316 +#+srcname: import
   1.317 +#+begin_src clojure :results silent
   1.318 +(ns cortex.import
   1.319 +  (:require swank.util.class-browse))
   1.320 +
   1.321 +(defn import-jme3 []
   1.322 +  (import '[com.jme3.system AppSettings JmeSystem]) 
   1.323 +  (import '[com.jme3.app Application SimpleApplication])
   1.324 +  (import 'com.jme3.material.Material)
   1.325 +  (import '[com.jme3.math Vector3f ColorRGBA Quaternion Transform])
   1.326 +  (import '[com.jme3.scene Node  Geometry])
   1.327 +  (import '[com.jme3.scene.shape Box Sphere Sphere$TextureMode])
   1.328 +  (import 'com.jme3.font.BitmapText)
   1.329 +  (import '[com.jme3.input KeyInput InputManager])
   1.330 +  (import '[com.jme3.input.controls
   1.331 +	    ActionListener AnalogListener KeyTrigger MouseButtonTrigger])
   1.332 +  (import '[com.jme3.asset AssetManager DesktopAssetManager] )
   1.333 +  (import '[com.jme3.asset.plugins HttpZipLocator ZipLocator])
   1.334 +  (import '[com.jme3.light PointLight DirectionalLight])
   1.335 +  (import '[com.jme3.animation AnimControl Skeleton Bone])
   1.336 +  (import '[com.jme3.bullet.collision.shapes
   1.337 +	    MeshCollisionShape SphereCollisionShape BoxCollisionShape])
   1.338 +  (import 'com.jme3.renderer.queue.RenderQueue$ShadowMode)
   1.339 +  (import 'jme3test.TestChooser)
   1.340 +  (import '[com.jme3.bullet PhysicsTickListener PhysicsSpace])
   1.341 +  (import '[com.jme3.bullet.joints SixDofJoint HingeJoint
   1.342 +	    SliderJoint Point2PointJoint ConeJoint]))
   1.343 +
   1.344 +
   1.345 +(defmacro permissive-import* [class-symbol]
   1.346 +  `(try
   1.347 +       (import ~class-symbol)
   1.348 +       (catch Exception e#
   1.349 +	 (println "can't import " ~class-symbol))))
   1.350 +
   1.351 +(defn permissive-import [class-symbol]
   1.352 +  (eval (list 'cortex.import/permissive-import* class-symbol)))
   1.353 +
   1.354 +(defn selection-import [selection-fn]
   1.355 +  (dorun
   1.356 +   (map (comp permissive-import symbol)
   1.357 +	(filter selection-fn
   1.358 +		(map :name
   1.359 +		     swank.util.class-browse/available-classes)))))
   1.360 +  
   1.361 +(defn mega-import-jme3
   1.362 +  "ALL the jme classes. For REPL use."
   1.363 +  []
   1.364 +  (selection-import
   1.365 +   #(and
   1.366 +     (.startsWith % "com.jme3.")
   1.367 +     ;; Don't import the Lwjgl stuff since it can throw exceptions
   1.368 +     ;; upon being loaded.
   1.369 +     (not (re-matches #".*Lwjgl.*" %)))))
   1.370 +#+end_src  
   1.371 +
   1.372 +The =mega-import-jme3= is quite usefull for debugging purposes since
   1.373 +it allows completion for almost all of JME's classes
   1.374 +
   1.375 +** Simplification
   1.376 +*** World
   1.377 +
   1.378 +It is comvienent to wrap the JME elements that deal with creating a
   1.379 +world, creation of basic objects, and Keyboard input with a nicer
   1.380 +interface (at least for my purposes).
   1.381 +
   1.382 +#+srcname: world-inputs
   1.383 +#+begin_src clojure :results silent
   1.384 +(ns cortex.world)
   1.385 +(require 'cortex.import)
   1.386 +(use 'clojure.contrib.def)
   1.387 +(rlm.rlm-commands/help)
   1.388 +(cortex.import/mega-import-jme3)
   1.389 +
   1.390 +(defvar *app-settings*
   1.391 +  (doto (AppSettings. true)
   1.392 +    (.setFullscreen false)
   1.393 +    (.setTitle "Aurellem.")
   1.394 +    ;; disable 32 bit stuff for now
   1.395 +    ;;(.setAudioRenderer "Send")
   1.396 +    )
   1.397 +  "These settings control how the game is displayed on the screen for
   1.398 +   debugging purposes.  Use binding forms to change this if desired.
   1.399 +   Full-screen mode does not work on some computers.")
   1.400 +
   1.401 +(defn asset-manager
   1.402 +  "returns a new, configured assetManager" []
   1.403 +  (JmeSystem/newAssetManager
   1.404 +   (.getResource
   1.405 +    (.getContextClassLoader (Thread/currentThread))
   1.406 +    "com/jme3/asset/Desktop.cfg")))
   1.407 +
   1.408 +(defmacro no-exceptions
   1.409 +  "Sweet relief like I never knew."
   1.410 +  [& forms]
   1.411 +  `(try ~@forms (catch Exception e# (.printStackTrace e#))))
   1.412 +
   1.413 +(defn thread-exception-removal []
   1.414 +  (println "removing exceptions from " (Thread/currentThread))
   1.415 +  (.setUncaughtExceptionHandler
   1.416 +   (Thread/currentThread)
   1.417 +   (proxy [Thread$UncaughtExceptionHandler] []
   1.418 +     (uncaughtException
   1.419 +      [thread thrown]
   1.420 +      (println "uncaught-exception thrown in " thread)
   1.421 +      (println (.getMessage thrown))))))
   1.422 +
   1.423 +(def println-repl (bound-fn [& args] (apply println args)))
   1.424 +
   1.425 +(use '[pokemon [lpsolve :only [constant-map]]])
   1.426 +
   1.427 +(defn no-op [& _])
   1.428 +
   1.429 +(defn all-keys
   1.430 +  "Construct a map of strings representing all the manual inputs from
   1.431 +   either the keyboard or mouse." 
   1.432 +  []
   1.433 +  (let [inputs (constant-map KeyInput)]
   1.434 +    (assoc
   1.435 +	(zipmap (map (fn [field]
   1.436 +		       (.toLowerCase (re-gsub #"_" "-" field))) (vals inputs))
   1.437 +		(map (fn [val] (KeyTrigger. val)) (keys inputs)))
   1.438 +      ;;explicitly add mouse controls
   1.439 +      "mouse-left" (MouseButtonTrigger. 0)
   1.440 +      "mouse-middle" (MouseButtonTrigger. 2)
   1.441 +      "mouse-right" (MouseButtonTrigger. 1))))
   1.442 +
   1.443 +(defn initialize-inputs
   1.444 +  "more java-interop cruft to establish keybindings for a particular virtual world"
   1.445 +  [game  input-manager key-map]
   1.446 +  (doall (map (fn [[name trigger]]
   1.447 +		(.addMapping ^InputManager input-manager
   1.448 +			     name (into-array (class trigger) [trigger]))) key-map))
   1.449 +  (doall (map (fn [name] 
   1.450 +		(.addListener ^InputManager input-manager game
   1.451 +			      (into-array String  [name]))) (keys key-map))))
   1.452 +
   1.453 +#+end_src
   1.454 +
   1.455 +These functions are all for debug controlling of the world through
   1.456 +keyboard and mouse. 
   1.457 +
   1.458 +We reuse  =constant-map= from =pokemon.lpsolve= to get the numerical
   1.459 +values for all the keys defined in the =KeyInput= class. The
   1.460 +documentation for =constant-map= is:
   1.461 +
   1.462 +#+begin_src clojure :results output
   1.463 +(doc pokemon.lpsolve/constant-map)
   1.464 +#+end_src
   1.465 +
   1.466 +#+results:
   1.467 +: -------------------------
   1.468 +: pokemon.lpsolve/constant-map
   1.469 +: ([class])
   1.470 +:   Takes a class and creates a map of the static constant integer
   1.471 +:   fields with their names.  This helps with C wrappers where they have
   1.472 +:   just defined a bunch of integer constants instead of enums
   1.473 +
   1.474 +
   1.475 +Then, =all-keys= converts the constant names like =KEY_J= to the more
   1.476 +clojure-like =key-j=, and returns a map from these keys to
   1.477 +jMonkeyEngine KeyTrigger objects, the use of which will soon become
   1.478 +apparent. =all-keys= also adds the three mouse button controls to the
   1.479 +map.
   1.480 +
   1.481 +#+srcname: world
   1.482 +#+begin_src clojure :results silent
   1.483 +(in-ns 'cortex.world)
   1.484 +
   1.485 +(defn traverse
   1.486 +  "apply f to every non-node, deeply"
   1.487 +  [f node]
   1.488 +  (if (isa? (class node) Node)
   1.489 +    (dorun (map (partial traverse f) (.getChildren node)))
   1.490 +    (f node)))
   1.491 +
   1.492 +(def gravity (Vector3f. 0 -9.81 0))
   1.493 +
   1.494 +(defn world
   1.495 +  [root-node key-map setup-fn update-fn]
   1.496 +  (let [physics-manager (BulletAppState.)
   1.497 +	shadow-renderer (BasicShadowRenderer. (asset-manager) (int 256))
   1.498 +	;;maybe use a better shadow renderer someday!
   1.499 +	;;shadow-renderer (PssmShadowRenderer. (asset-manager) 256 1)
   1.500 +	]   	
   1.501 +  (doto
   1.502 +      (proxy [SimpleApplication ActionListener] []
   1.503 +	(simpleInitApp
   1.504 +	 []
   1.505 +	 (no-exceptions
   1.506 +	  (.setTimer this (IsoTimer. 60))
   1.507 +	  ;; Create key-map.
   1.508 +	  (.setFrustumFar (.getCamera this) 300)
   1.509 +	  (initialize-inputs this (.getInputManager this) (all-keys))
   1.510 +	  ;; Don't take control of the mouse
   1.511 +	  (org.lwjgl.input.Mouse/setGrabbed false)
   1.512 +	  ;; add all objects to the world
   1.513 +	  (.attachChild (.getRootNode this) root-node)
   1.514 +	  ;; enable physics
   1.515 +	  ;; add a physics manager
   1.516 +	  (.attach (.getStateManager this) physics-manager)
   1.517 +	  (.setGravity (.getPhysicsSpace physics-manager) gravity)
   1.518 +
   1.519 +
   1.520 +	  ;; go through every object and add it to the physics manager
   1.521 +	  ;; if relavant.
   1.522 +	   (traverse (fn [geom]
   1.523 +		       (dorun
   1.524 +			(for [n (range (.getNumControls geom))]
   1.525 +			  (do
   1.526 +			    (println-repl "adding control " (.getControl geom n))
   1.527 +			    (.add (.getPhysicsSpace physics-manager)
   1.528 +				  (.getControl geom n))))))
   1.529 +		     (.getRootNode this))
   1.530 +	  ;;(.addAll (.getPhysicsSpace physics-manager) (.getRootNode this))
   1.531 +
   1.532 +	  (setup-fn this)
   1.533 +	  (.setDirection shadow-renderer
   1.534 +			 (.normalizeLocal (Vector3f. -1 -1 -1)))
   1.535 +	  (.addProcessor (.getViewPort this) shadow-renderer)
   1.536 +	  (.setShadowMode (.getRootNode this) RenderQueue$ShadowMode/Off)
   1.537 +	  ))
   1.538 +	(simpleUpdate
   1.539 +	 [tpf]
   1.540 +	 (no-exceptions
   1.541 +	  (update-fn this tpf))) 
   1.542 +	(onAction
   1.543 +	 [binding value tpf]
   1.544 +	 ;; whenever a key is pressed, call the function returned from
   1.545 +	 ;; key-map.
   1.546 +	 (no-exceptions
   1.547 +	  (if-let [react (key-map binding)]
   1.548 +	    (react this value)))))
   1.549 +    ;; don't show a menu to change options.
   1.550 +    
   1.551 +    (.setShowSettings false)
   1.552 +    (.setPauseOnLostFocus false)
   1.553 +    (.setSettings *app-settings*))))
   1.554 +
   1.555 +(defn apply-map
   1.556 +  "Like apply, but works for maps and functions that expect an implicit map
   1.557 +   and nothing else as in (fn [& {}]).
   1.558 +------- Example -------
   1.559 + (defn jjj [& {:keys [www] :or {www \"oph yeah\"} :as env}] (println www))
   1.560 + (apply-map jjj {:www \"whatever\"})
   1.561 +  -->\"whatever\""
   1.562 +  [fn m]
   1.563 +  (apply fn (reduce #(into %1 %2) [] m)))
   1.564 +
   1.565 +#+end_src 
   1.566 +
   1.567 +
   1.568 +=world= is the most important function here.  
   1.569 +***  TODO more documentation
   1.570 +
   1.571 +#+srcname: world-shapes
   1.572 +#+begin_src clojure :results silent
   1.573 +(in-ns 'cortex.world)
   1.574 +(defrecord shape-description
   1.575 +  [name
   1.576 +   color
   1.577 +   mass
   1.578 +   friction
   1.579 +   texture
   1.580 +   material
   1.581 +   position
   1.582 +   rotation
   1.583 +   shape
   1.584 +   physical?])
   1.585 +
   1.586 +(def base-shape
   1.587 +     (shape-description.
   1.588 +      "default-shape"
   1.589 +      false
   1.590 +      ;;ColorRGBA/Blue
   1.591 +      1.0 ;; mass
   1.592 +      1.0 ;; friction
   1.593 +      ;; texture
   1.594 +      "Textures/Terrain/BrickWall/BrickWall.jpg"
   1.595 +      ;; material
   1.596 +      "Common/MatDefs/Misc/Unshaded.j3md"
   1.597 +      Vector3f/ZERO
   1.598 +      Quaternion/IDENTITY
   1.599 +      (Box. Vector3f/ZERO 0.5 0.5 0.5)
   1.600 +      true))
   1.601 +
   1.602 +(defn make-shape
   1.603 +  [#^shape-description d]
   1.604 +  (let [mat (Material. (asset-manager) (:material d))
   1.605 +	geom (Geometry. (:name d) (:shape d))]
   1.606 +    (if (:texture d)
   1.607 +      (let [key (TextureKey. (:texture d))]
   1.608 +	(.setGenerateMips key true)
   1.609 +	(.setTexture mat "ColorMap" (.loadTexture (asset-manager) key))))
   1.610 +    (if (:color d) (.setColor mat "Color" (:color d)))
   1.611 +    (.setMaterial geom mat)
   1.612 +    (if-let [rotation (:rotation d)] (.rotate geom rotation))
   1.613 +    (.setLocalTranslation geom (:position d))
   1.614 +    (if (:physical? d)
   1.615 +      (let [impact-shape (doto (GImpactCollisionShape. (.getMesh geom)) (.setMargin 0))
   1.616 +	    physics-control (RigidBodyControl.
   1.617 +			     impact-shape 
   1.618 +			     (float (:mass d)))]
   1.619 +	(.createJmeMesh impact-shape)
   1.620 +	(.addControl geom physics-control)
   1.621 +	;;(.setSleepingThresholds physics-control (float 0) (float 0))
   1.622 +	(.setFriction physics-control (:friction d))))
   1.623 +    ;;the default is to keep this node in the physics engine forever.
   1.624 +    ;;these commands must come after the control is added to the geometry.
   1.625 +    ;;
   1.626 +    geom))
   1.627 +  
   1.628 +(defn box
   1.629 +  ([l w h & {:as options}]
   1.630 +     (let [options (merge base-shape options)]
   1.631 +       (make-shape (assoc options
   1.632 +		     :shape (Box. l w h)))))
   1.633 +  ([] (box 0.5 0.5 0.5)))
   1.634 +
   1.635 +(defn sphere
   1.636 +  ([r & {:as options}]
   1.637 +     (let [options (merge base-shape options)]
   1.638 +       (make-shape (assoc options
   1.639 +		     :shape (Sphere. 32 32 (float r)))))) 
   1.640 +  ([] (sphere 0.5)))
   1.641 +
   1.642 +(defn add-element [game node]
   1.643 +  (.addAll
   1.644 +   (.getPhysicsSpace
   1.645 +    (.getState
   1.646 +     (.getStateManager game)
   1.647 +     BulletAppState))
   1.648 +    node)
   1.649 +  (.attachChild (.getRootNode game) node))
   1.650 +
   1.651 +(defn set-gravity*
   1.652 +  [game gravity]
   1.653 +  (traverse
   1.654 +   (fn [geom]
   1.655 +     (if-let
   1.656 +	 [control (.getControl geom RigidBodyControl)]
   1.657 +       (do
   1.658 +	 (.setGravity control gravity)
   1.659 +	 (.applyImpulse control Vector3f/ZERO Vector3f/ZERO)
   1.660 +	 )))
   1.661 +   (.getRootNode game)))
   1.662 +
   1.663 +#+end_src
   1.664 +
   1.665 +These are convienence functions for creating JME objects and
   1.666 +manipulating a world.
   1.667 +
   1.668 +#+srcname: world-view
   1.669 +#+begin_src clojure :results silent
   1.670 +(in-ns 'cortex.world)
   1.671 +
   1.672 +(defprotocol Viewable
   1.673 +  (view [something]))
   1.674 +
   1.675 +(extend-type com.jme3.scene.Geometry
   1.676 +  Viewable
   1.677 +  (view [geo]
   1.678 +	(view (doto (Node.)(.attachChild geo)))))
   1.679 +
   1.680 +(extend-type com.jme3.scene.Node
   1.681 +  Viewable
   1.682 +  (view [node]
   1.683 +	(.start
   1.684 +	 (world node
   1.685 +		{}
   1.686 +		(fn [world]
   1.687 +		  (.enableDebug 
   1.688 +		   (.getPhysicsSpace
   1.689 +		    (.getState
   1.690 +		     (.getStateManager world)
   1.691 +		     BulletAppState))
   1.692 +		   (asset-manager))
   1.693 +		  (set-gravity* world Vector3f/ZERO)
   1.694 +;;		  (set-gravity* world (Vector3f. 0 (float -0.4) 0))
   1.695 +		  (let [sun (doto (DirectionalLight.)
   1.696 +			      (.setDirection (.normalizeLocal (Vector3f. 1 0 -2)))
   1.697 +			      (.setColor ColorRGBA/White))]
   1.698 +		    (.addLight (.getRootNode world) sun)))
   1.699 +		no-op))))
   1.700 +
   1.701 +(defn position-camera [game]
   1.702 +  (doto (.getCamera game)
   1.703 +    (.setLocation (Vector3f. 0 6 6))
   1.704 +    (.lookAt Vector3f/ZERO (Vector3f. 0 1 0))))
   1.705 +
   1.706 +#+end_src
   1.707 +
   1.708 +Here I make the =Viewable= protocol and extend it to JME's types.  Now
   1.709 +hello-world can be written as easily as:
   1.710 +
   1.711 +#+begin_src clojure :results silent
   1.712 +(cortex.world/view (cortex.world/box))
   1.713 +#+end_src
   1.714 +
   1.715 +** Hello
   1.716 +Here are the jmonkeyengine "Hello" programs translated to clojure.
   1.717 +*** Hello Simple App
   1.718 +Here is the hello world example for jme3 in clojure.
   1.719 +It's a more or less direct translation from the java source
   1.720 +from
   1.721 +http://jmonkeyengine.org/wiki/doku.php/jme3:beginner:hello_simpleapplication.
   1.722 +
   1.723 +Of note is the fact that since we don't have access to the
   1.724 +=AssetManager= via extendig =SimpleApplication=, we have to build one
   1.725 +ourselves. 
   1.726 +
   1.727 +#+srcname: hello-simple-app
   1.728 +#+begin_src clojure :results silent
   1.729 +(ns hello.hello-simple-app)
   1.730 +(require 'cortex.import)
   1.731 +(use 'clojure.contrib.def)
   1.732 +(rlm.rlm-commands/help)
   1.733 +(cortex.import/import-jme3)
   1.734 +(use 'cortex.world)
   1.735 +
   1.736 +
   1.737 +(def cube (Box. Vector3f/ZERO 1 1 1))
   1.738 +
   1.739 +(def geom (Geometry. "Box" cube))
   1.740 +
   1.741 +(def mat (Material. (asset-manager) "Common/MatDefs/Misc/Unshaded.j3md"))
   1.742 +
   1.743 +(.setColor mat "Color" ColorRGBA/Blue)
   1.744 +
   1.745 +(.setMaterial geom mat)
   1.746 +
   1.747 +(defn simple-app []
   1.748 +  (doto
   1.749 +      (proxy [SimpleApplication] []
   1.750 +	(simpleInitApp
   1.751 +	 []
   1.752 +	 ;; Don't take control of the mouse
   1.753 +	 (org.lwjgl.input.Mouse/setGrabbed false)
   1.754 +	 (.attachChild (.getRootNode this) geom)))
   1.755 +    ;; don't show a menu to change options.
   1.756 +    (.setShowSettings false)
   1.757 +    (.setPauseOnLostFocus false)
   1.758 +    (.setSettings *app-settings*)))
   1.759 +#+end_src
   1.760 +
   1.761 +Running this program will begin a new jMonkeyEngine game which
   1.762 +displays a single blue cube.
   1.763 +
   1.764 +#+begin_src clojure :exports code :results silent
   1.765 +(.start (hello.hello-simple-app/simple-app))
   1.766 +#+end_src
   1.767 +
   1.768 +#+caption: the simplest JME game.
   1.769 +[[./images/simple-app.jpg]]
   1.770 +
   1.771 +
   1.772 +
   1.773 +*** Hello Physics
   1.774 +From http://jmonkeyengine.org/wiki/doku.php/jme3:beginner:hello_physics
   1.775 +
   1.776 +#+srcname: brick-wall-header
   1.777 +#+begin_src clojure :results silent
   1.778 +(ns hello.brick-wall)
   1.779 +(require 'cortex.import)
   1.780 +(use 'clojure.contrib.def)
   1.781 +(rlm.rlm-commands/help)
   1.782 +(cortex.import/mega-import-jme3)
   1.783 +(use '[pokemon [lpsolve :only [constant-map]]])
   1.784 +(use 'cortex.world)
   1.785 +#+end_src
   1.786 +
   1.787 +#+srcname: brick-wall-body
   1.788 +#+begin_src clojure :results silent
   1.789 +(in-ns 'hello.brick-wall)
   1.790 +
   1.791 +(defn floor
   1.792 +  "make a sturdy, unmovable physical floor"
   1.793 +  []
   1.794 +  (box 20 1 20 :mass 0 :color false :position (Vector3f. 0 -2 0)))
   1.795 +
   1.796 +(def brick-length 0.48)
   1.797 +(def brick-width 0.24)
   1.798 +(def brick-height 0.12)
   1.799 +
   1.800 +
   1.801 +(defn brick* [position]
   1.802 +  (doto (box brick-length brick-height brick-width
   1.803 +	     :position position :name "brick"
   1.804 +	     :material "Common/MatDefs/Misc/Unshaded.j3md"
   1.805 +	     :texture "Textures/Terrain/BrickWall/BrickWall.jpg"
   1.806 +	     :mass 36)
   1.807 +    (->
   1.808 +     (.getMesh)
   1.809 +     (.scaleTextureCoordinates (Vector2f. 1 0.5)))
   1.810 +    ;;(.setShadowMode RenderQueue$ShadowMode/CastAndReceive)
   1.811 +    )
   1.812 +  )
   1.813 +
   1.814 +(defn inception-brick-wall
   1.815 +  "construct a physical brick wall"
   1.816 +  []
   1.817 +  (let [node (Node. "brick-wall")]
   1.818 +    (dorun
   1.819 +     (map (comp #(.attachChild node %) brick*)
   1.820 +	  (for
   1.821 +	      [x (range 15)
   1.822 +	       y (range 10)
   1.823 +	       z (range 1)]
   1.824 +	    (Vector3f.
   1.825 +	     (* brick-length x 1.03)
   1.826 +	     (* brick-width y y 10)
   1.827 +	     (* brick-height z)))))
   1.828 +    node))
   1.829 +
   1.830 +(defn gravity-toggle
   1.831 +  [new-value]
   1.832 +  (fn [game value]
   1.833 +    (println-repl "set gravity to " new-value)
   1.834 +    (if value
   1.835 +      (set-gravity* game new-value)
   1.836 +      (set-gravity* game gravity))))
   1.837 +
   1.838 +(defn fire-cannon-ball []
   1.839 +  (fn [game value]
   1.840 +    (if (not value)
   1.841 +      (let [camera (.getCamera game)
   1.842 +	    cannon-ball
   1.843 +	    (sphere  0.7 
   1.844 +		     :material "Common/MatDefs/Misc/Unshaded.j3md"
   1.845 +		     :texture "Textures/PokeCopper.jpg"
   1.846 +		     :position
   1.847 +		     (.add (.getLocation camera)
   1.848 +			   (.mult (.getDirection camera) (float 1)))
   1.849 +		     :mass 3)] ;200 0.05
   1.850 +	(.setShadowMode cannon-ball RenderQueue$ShadowMode/CastAndReceive)
   1.851 +	(.setLinearVelocity
   1.852 +	 (.getControl cannon-ball RigidBodyControl)
   1.853 +	 (.mult (.getDirection camera) (float 50))) ;50
   1.854 +	(add-element game cannon-ball)))))
   1.855 +
   1.856 +(defn floor* []
   1.857 +  (doto (box 10 0.1 5 :name "floor" ;10 0.1 5 ; 240 0.1 240
   1.858 +	     :material "Common/MatDefs/Misc/Unshaded.j3md"
   1.859 +	     :texture "Textures/Terrain/Pond/Pond.png"
   1.860 +	     :position (Vector3f. 0 -0.1 0 )
   1.861 +	     :mass 0)
   1.862 +    (->
   1.863 +     (.getMesh)
   1.864 +     (.scaleTextureCoordinates (Vector2f. 3 6)));64 64
   1.865 +    (->
   1.866 +     (.getMaterial)
   1.867 +     (.getTextureParam "ColorMap")
   1.868 +     (.getTextureValue)
   1.869 +     (.setWrap Texture$WrapMode/Repeat))
   1.870 +    (.setShadowMode RenderQueue$ShadowMode/Receive)
   1.871 +  ))
   1.872 +    
   1.873 +(defn brick-wall* []
   1.874 +  (let [node (Node. "brick-wall")]
   1.875 +    (dorun
   1.876 +     (map
   1.877 +      (comp  #(.attachChild node %) brick*)
   1.878 +       (for [y (range 15)
   1.879 +	     x (range 4)
   1.880 +	     z (range 1)]
   1.881 +       	    (Vector3f.
   1.882 +       	     (+ (* 2 x brick-length)
   1.883 +		(if (even? (+ y z)) 
   1.884 +		  (/ brick-length 4) (/ brick-length -4)))
   1.885 +       	     (+ (* brick-height (inc (* 2 y))))
   1.886 +	     (* 2 z brick-width) ))))
   1.887 +    (.setShadowMode node RenderQueue$ShadowMode/CastAndReceive)
   1.888 +    node))
   1.889 +
   1.890 +(defn brick-wall-game-run []
   1.891 +  (doto
   1.892 +      (world
   1.893 +       (doto (Node.) (.attachChild (floor*))
   1.894 +	     (.attachChild (brick-wall*))
   1.895 +	     )
   1.896 +       {"key-i" (gravity-toggle (Vector3f. 0 0 -9.81))
   1.897 +	"key-m" (gravity-toggle (Vector3f. 0 0 9.81))
   1.898 +	"key-l" (gravity-toggle (Vector3f. 9.81 0 0))
   1.899 +	"key-j" (gravity-toggle (Vector3f. -9.81 0 0))
   1.900 +	"key-k" (gravity-toggle Vector3f/ZERO)
   1.901 +	"key-u" (gravity-toggle (Vector3f. 0 9.81 0))
   1.902 +	"key-o" (gravity-toggle (Vector3f. 0 -9.81 0))
   1.903 +	"key-f" (fn[game value] 
   1.904 +		  (if (not value) (add-element game (brick-wall*))))
   1.905 +	"key-return" (fire-cannon-ball)}
   1.906 +       position-camera
   1.907 +       (fn [& _]))     
   1.908 +    (.start)))
   1.909 +#+end_src
   1.910 +
   1.911 +#+begin_src clojure :results silent
   1.912 +(hello.brick-wall/brick-wall-game-run)
   1.913 +#+end_src
   1.914 +
   1.915 +#+caption: the brick wall standing
   1.916 +[[./images/brick-wall-standing.jpg]]
   1.917 +
   1.918 +#+caption: the brick wall after it has been knocked over by a "pok\eacute{}ball"
   1.919 +[[./images/brick-wall-knocked-down.jpg]]
   1.920 +
   1.921 +*** Other Brick Games
   1.922 +#+srcname: other-games
   1.923 +#+begin_src clojure :results silent
   1.924 +(ns cortex.other-games
   1.925 +   {:author "Dylan Holmes"})
   1.926 +(use 'cortex.world)
   1.927 +(use 'hello.brick-wall)
   1.928 +(use 'cortex.import)
   1.929 +(cortex.import/mega-import-jme3)
   1.930 +
   1.931 +(defn scad [position]
   1.932 +  (doto (box 0.1 0.1 0.1
   1.933 +	     :position position :name "brick"
   1.934 +	     :material "Common/MatDefs/Misc/Unshaded.j3md"
   1.935 +	     :texture "Textures/Terrain/BrickWall/BrickWall.jpg"
   1.936 +	     :mass 20)
   1.937 +    (->
   1.938 +     (.getMesh)
   1.939 +     (.scaleTextureCoordinates (Vector2f. 1 0.5))
   1.940 +     )
   1.941 +        (->	(.getControl RigidBodyControl)
   1.942 +	(.setLinearVelocity (Vector3f. 0 100 0))
   1.943 +	)
   1.944 +	
   1.945 +    ;;(.setShadowMode RenderQueue$ShadowMode/Cast)
   1.946 +    ))
   1.947 +
   1.948 +
   1.949 +(defn shrapnel []
   1.950 +  (let [node (Node. "explosion-day")]
   1.951 +    (dorun
   1.952 +     (map
   1.953 +      (comp  #(.attachChild node %) scad)
   1.954 +       (for [y (range 15)
   1.955 +	     x (range 4)
   1.956 +	     z (range 1)]
   1.957 +       	    (Vector3f.
   1.958 +       	     (+ (* 2 x brick-height)
   1.959 +		(if (even? (+ y z)) (/ brick-height 4) (/ brick-height -4)))
   1.960 +       	     (+ (* brick-height (inc (* 2 y))))
   1.961 +	     (* 2 z brick-height) ))))
   1.962 +    node))
   1.963 +
   1.964 +
   1.965 +(def domino-height 0.48)
   1.966 +(def domino-thickness 0.12)
   1.967 +(def domino-width 0.24)
   1.968 +
   1.969 +(def domino-thickness 0.05)
   1.970 +(def domino-width 0.5)
   1.971 +(def domino-height 1)
   1.972 +
   1.973 +(defn domino
   1.974 +  ([position]
   1.975 +     (domino position (Quaternion/IDENTITY)))
   1.976 +  ([position rotation]
   1.977 +     (doto (box domino-width domino-height domino-thickness
   1.978 +	     :position position :name "domino"
   1.979 +	     :material "Common/MatDefs/Misc/Unshaded.j3md"
   1.980 +	     :texture "Textures/Terrain/BrickWall/BrickWall.jpg"
   1.981 +	     :mass 1
   1.982 +	     :rotation rotation)
   1.983 +       (.setShadowMode RenderQueue$ShadowMode/CastAndReceive)
   1.984 +       )))
   1.985 +
   1.986 +
   1.987 +(defn domino-row []
   1.988 +  (let [node (Node. "domino-row")]
   1.989 +    (dorun
   1.990 +     (map
   1.991 +      (comp  #(.attachChild node %) domino)
   1.992 +       (for [
   1.993 +	     z (range 10)
   1.994 +	     x (range 5)
   1.995 +	     ]
   1.996 +       	    (Vector3f.
   1.997 +	     (+ (* z domino-width) (* x 5 domino-width))
   1.998 +	     (/ domino-height 1)
   1.999 +	     (* -5.5 domino-thickness z) ))))
  1.1000 +
  1.1001 +    node))
  1.1002 +
  1.1003 +(defn domino-cycle []
  1.1004 +  (let [node (Node. "domino-cycle")]
  1.1005 +    (dorun
  1.1006 +     (map
  1.1007 +      (comp  #(.attachChild node %) (partial apply domino) ) 
  1.1008 +      (for [n (range 720)]
  1.1009 +	(let [space (* domino-height 5.5)
  1.1010 +	      r (fn[n] (* (+ n 3) domino-width 0.5)) 
  1.1011 +	      t (fn[n] (reduce
  1.1012 +			+
  1.1013 +			(map
  1.1014 +			 (fn dt[n] (/ space (* 2 (Math/PI) (r n))))
  1.1015 +			 (range n))))
  1.1016 +	      t (t n)
  1.1017 +	      r (r n)
  1.1018 +	      ct (Math/cos t)
  1.1019 +	      st (Math/sin t)
  1.1020 +	      ]
  1.1021 +	(list
  1.1022 +       	    (Vector3f.
  1.1023 +	     (* -1 r st)
  1.1024 +	     (/ domino-height 1)
  1.1025 +	     (* r ct))
  1.1026 +	    (.fromAngleAxis (Quaternion.)
  1.1027 +			    (- (/ 3.1415926 2) t) (Vector3f. 0 1 0))
  1.1028 +	    )))
  1.1029 +	))
  1.1030 +    node))
  1.1031 +
  1.1032 +
  1.1033 +(defn domino-game-run []
  1.1034 +  (doto
  1.1035 +      (world
  1.1036 +       (doto (Node.) (.attachChild (floor*))
  1.1037 +	     )
  1.1038 +       {"key-i" (gravity-toggle (Vector3f. 0 0 -9.81))
  1.1039 +	"key-m" (gravity-toggle (Vector3f. 0 0 9.81))
  1.1040 +	"key-l" (gravity-toggle (Vector3f. 9.81 0 0))
  1.1041 +	"key-j" (gravity-toggle (Vector3f. -9.81 0 0))
  1.1042 +	"key-k" (gravity-toggle (Vector3f. 0 9.81 0) )
  1.1043 +	"key-u" (fn[g v] ((gravity-toggle (Vector3f. 0 -0 0)) g true))
  1.1044 +	"key-o" (gravity-toggle (Vector3f. 0 -9.81 0))
  1.1045 +
  1.1046 +	"key-space"
  1.1047 +	(fn[game value]
  1.1048 +	  
  1.1049 +	  (if (not value)
  1.1050 +	    (let [d (domino (Vector3f. 0 (/ domino-height 0.25) 0)
  1.1051 +			    (.fromAngleAxis (Quaternion.)
  1.1052 +					    (/ Math/PI 2) (Vector3f. 0 1 0)))]
  1.1053 +	      (add-element game d))))
  1.1054 +	"key-f"
  1.1055 +	(fn[game value](if (not value) (add-element game (domino-cycle))))
  1.1056 +	"key-return" (fire-cannon-ball)}
  1.1057 +       position-camera
  1.1058 +       (fn [& _]))     
  1.1059 +    (.start)))       
  1.1060 +#+end_src
  1.1061 +
  1.1062 +#+begin_src clojure :results silent
  1.1063 +(cortex.other-games/domino-game-run)
  1.1064 +#+end_src
  1.1065 +
  1.1066 +#+caption: floating dominos
  1.1067 +[[./images/dominos.jpg]]
  1.1068 +
  1.1069 +*** Hello Loop
  1.1070 +#+srcname: hello-loop
  1.1071 +#+begin_src clojure :results silent
  1.1072 +(ns hello.loop)
  1.1073 +(use 'cortex.world)
  1.1074 +(use 'cortex.import)
  1.1075 +(cortex.import/mega-import-jme3)
  1.1076 +(rlm.rlm-commands/help)
  1.1077 +
  1.1078 +(defn blue-cube []
  1.1079 +  (box 1 1 1
  1.1080 +       :color ColorRGBA/Blue
  1.1081 +       :texture false
  1.1082 +       :material "Common/MatDefs/Misc/Unshaded.j3md"
  1.1083 +       :name "blue-cube"
  1.1084 +       :physical? false))
  1.1085 +
  1.1086 +(defn blue-cube-game []
  1.1087 +  (let [cube (blue-cube)
  1.1088 +	root (doto (Node.) (.attachChild cube))]
  1.1089 +  (world root
  1.1090 +	 {}
  1.1091 +	 no-op
  1.1092 +	 (fn [game tpf]
  1.1093 +	   (.rotate cube 0.0 (* 2 tpf) 0.0)))))	 	 		  
  1.1094 +#+end_src
  1.1095 +
  1.1096 +*** Hello Collision
  1.1097 +
  1.1098 +#+srcname: hello-collision
  1.1099 +#+begin_src clojure :results silent
  1.1100 +(ns hello.collision)
  1.1101 +(use 'cortex.world)
  1.1102 +(use 'cortex.import)
  1.1103 +(use 'clojure.contrib.def)
  1.1104 +
  1.1105 +
  1.1106 +(cortex.import/mega-import-jme3)
  1.1107 +(rlm.rlm-commands/help)
  1.1108 +(use '[hello [brick-wall :only [fire-cannon-ball brick-wall*]]])
  1.1109 +
  1.1110 +
  1.1111 +(defn environment []
  1.1112 +     (let
  1.1113 +	 [scene-model
  1.1114 +	  (doto
  1.1115 +	      (.loadModel
  1.1116 +	       (doto (asset-manager)
  1.1117 +		 (.registerLocator
  1.1118 +		  "/home/r/cortex/assets/zips/town.zip" ZipLocator))
  1.1119 +	       "main.scene")
  1.1120 +	    (.setLocalScale (float 2.0)))
  1.1121 +	  collision-shape
  1.1122 +	  (CollisionShapeFactory/createMeshShape #^Node scene-model)
  1.1123 +	  landscape (RigidBodyControl. collision-shape 0)]
  1.1124 +       (.setShadowMode scene-model RenderQueue$ShadowMode/CastAndReceive)
  1.1125 +       (.addControl scene-model landscape)
  1.1126 +       scene-model))
  1.1127 +	  
  1.1128 +(defn player-fn []
  1.1129 +  (doto
  1.1130 +      (CharacterControl.
  1.1131 +       (CapsuleCollisionShape. (float 1.5) (float 6)(float 1))
  1.1132 +       (float 0.05))
  1.1133 +    (.setJumpSpeed 20)
  1.1134 +    (.setFallSpeed 30)
  1.1135 +    (.setGravity 30) ;30
  1.1136 +    (.setPhysicsLocation (Vector3f. 0 10 0))))
  1.1137 +
  1.1138 +(defn lights []
  1.1139 +  [(doto (AmbientLight.) (.setColor (.mult (ColorRGBA. 1 1 1 1) (float 1))))
  1.1140 +   (doto (AmbientLight.) (.setColor (.mult (ColorRGBA. 1 0.7 0 1) (float 1))))
  1.1141 +   (doto (DirectionalLight.)
  1.1142 +     (.setColor (.mult ColorRGBA/White (float 0.9) ))
  1.1143 +     (.setDirection (.normalizeLocal (Vector3f. 2.8 -28 2.8))))])
  1.1144 +
  1.1145 +(defn night-lights []
  1.1146 +  [(doto (AmbientLight.) (.setColor (.mult (ColorRGBA. 0.275 0.467 0.784 1) (float 0.3))))
  1.1147 +   (doto (DirectionalLight.)
  1.1148 +     (.setColor (.mult ColorRGBA/White (float 0.2) ))
  1.1149 +     (.setDirection (.normalizeLocal (Vector3f. 2.8 -28 2.8))))])
  1.1150 +
  1.1151 +(def player (atom (player-fn)))
  1.1152 +
  1.1153 +(defn setup-fn [game]
  1.1154 +  (dorun (map #(.addLight (.getRootNode game) %) (lights)))
  1.1155 +  ;; set the color of the sky
  1.1156 +  (.setBackgroundColor (.getViewPort game) (ColorRGBA. 0.3 0.4 0.9 1))
  1.1157 +  ;(.setBackgroundColor (.getViewPort game) (ColorRGBA. 0 0 0 1)
  1.1158 +  (doto (.getFlyByCamera game)
  1.1159 +    (.setMoveSpeed (float 100))
  1.1160 +    (.setRotationSpeed 3))
  1.1161 +  (.add
  1.1162 +   (.getPhysicsSpace
  1.1163 +    (.getState (.getStateManager game) BulletAppState))
  1.1164 +   @player)
  1.1165 +
  1.1166 +   (doto (Node.) (.attachChild (.getRootNode game))
  1.1167 +	     (.attachChild (brick-wall*))
  1.1168 +   )
  1.1169 +
  1.1170 +)
  1.1171 +
  1.1172 +
  1.1173 +(def walking-up? (atom false))
  1.1174 +(def walking-down? (atom false))
  1.1175 +(def walking-left? (atom false))
  1.1176 +(def walking-right? (atom false))
  1.1177 +
  1.1178 +(defn set-walk [walk-atom game value]
  1.1179 +  ;;(println-repl "setting  stuff to " value)
  1.1180 +  (reset! walk-atom value))
  1.1181 +
  1.1182 +(defn responses []
  1.1183 +     {"key-w" (partial set-walk walking-up?)
  1.1184 +      "key-d" (partial set-walk walking-right?)
  1.1185 +      "key-s" (partial set-walk walking-down?)
  1.1186 +      "key-a" (partial set-walk walking-left?)
  1.1187 +      "key-return" (fire-cannon-ball)
  1.1188 +      "key-space" (fn [game value] (.jump @player))
  1.1189 +      })
  1.1190 +     
  1.1191 +(defn update-fn
  1.1192 +  [game tpf]
  1.1193 +  (let [camera (.getCamera game)         
  1.1194 +	cam-dir (.multLocal
  1.1195 +		 (.clone
  1.1196 +		  (.getDirection camera)) (float 0.6))
  1.1197 +	cam-left (.multLocal
  1.1198 +		  (.clone
  1.1199 +		   (.getLeft camera)) (float 0.4))
  1.1200 +	walk-direction (Vector3f. 0 0 0)]
  1.1201 +    
  1.1202 +    (cond
  1.1203 +     @walking-up?     (.addLocal walk-direction cam-dir)
  1.1204 +     @walking-right?  (.addLocal walk-direction (.negate cam-left))
  1.1205 +     @walking-down?   (.addLocal walk-direction (.negate cam-dir))
  1.1206 +     @walking-left?   (.addLocal walk-direction cam-left))
  1.1207 +    (.setWalkDirection @player walk-direction)
  1.1208 +    (.setLocation camera (.getPhysicsLocation @player))))
  1.1209 +    
  1.1210 +(defn run-game []
  1.1211 +  (.start
  1.1212 +   (world (environment)
  1.1213 +	  (responses)
  1.1214 +	  setup-fn
  1.1215 +	  update-fn)))
  1.1216 +#+end_src
  1.1217 +
  1.1218 +*** Hello Terrain
  1.1219 +#+srcname: hello-terrain
  1.1220 +#+begin_src clojure :results silent
  1.1221 +(ns hello.terrain)
  1.1222 +(use 'cortex.world)
  1.1223 +(use 'cortex.import)
  1.1224 +(use 'clojure.contrib.def)
  1.1225 +(import jme3tools.converters.ImageToAwt)
  1.1226 +
  1.1227 +
  1.1228 +(cortex.import/mega-import-jme3)
  1.1229 +(rlm.rlm-commands/help)
  1.1230 +(use '[hello [brick-wall :only [fire-cannon-ball brick-wall*]]])
  1.1231 +
  1.1232 +
  1.1233 +(defn setup-fn [type game]
  1.1234 +  (.setMoveSpeed (.getFlyByCamera game) 50)
  1.1235 +  (.setFrustumFar (.getCamera game) 10000)
  1.1236 +  (let [env (environment type)
  1.1237 +	cameras [(.getCamera game)]
  1.1238 +	control (TerrainLodControl. env cameras)]
  1.1239 +    ;;(.addControl env control)
  1.1240 +    (.attachChild (.getRootNode game) env)))                
  1.1241 +
  1.1242 +(defn environment [type]
  1.1243 +  (let
  1.1244 +      [mat_terrain
  1.1245 +       (Material. (asset-manager) "Common/MatDefs/Terrain/Terrain.j3md")
  1.1246 +       grass (.loadTexture (asset-manager) "Textures/Terrain/splat/grass.jpg")
  1.1247 +       dirt (.loadTexture (asset-manager) "Textures/Terrain/splat/dirt.jpg")
  1.1248 +       rock (.loadTexture (asset-manager) "Textures/Terrain/splat/road.jpg")
  1.1249 +       heightmap-image (.loadTexture (asset-manager)
  1.1250 +				     ({:mountain "Textures/Terrain/splat/mountains512.png"
  1.1251 +				       :fortress "Textures/Terrain/splat/fortress512.png"
  1.1252 +				       }type))
  1.1253 +       heightmap (ImageBasedHeightMap.
  1.1254 +		  (ImageToAwt/convert (.getImage heightmap-image) false true 0))
  1.1255 +       terrain (do (.load heightmap)
  1.1256 +		   (TerrainQuad. "my terrain" 65 513 (.getHeightMap heightmap)))
  1.1257 +       ]
  1.1258 +    
  1.1259 +    (dorun (map #(.setWrap % Texture$WrapMode/Repeat)
  1.1260 +		[grass dirt rock]))
  1.1261 +
  1.1262 +    (doto mat_terrain
  1.1263 +      (.setTexture "Tex1" grass)
  1.1264 +      (.setFloat "Tex1Scale" (float 64))
  1.1265 +
  1.1266 +      (.setTexture "Tex2" dirt)
  1.1267 +      (.setFloat "Tex2Scale" (float 32))
  1.1268 +
  1.1269 +      (.setTexture "Tex3" rock)
  1.1270 +      (.setFloat "Tex3Scale" (float 128))
  1.1271 +
  1.1272 +      (.setTexture "Alpha"
  1.1273 +		   (.loadTexture
  1.1274 +		    (asset-manager) 
  1.1275 +		    ({:mountain "Textures/Terrain/splat/alphamap.png"
  1.1276 +		      :fortress "Textures/Terrain/splat/alphamap2.png"} type))))
  1.1277 +
  1.1278 +    (doto terrain
  1.1279 +      (.setMaterial mat_terrain)
  1.1280 +      (.setLocalTranslation 0 -100 0)
  1.1281 +      (.setLocalScale 2 1 2))))
  1.1282 +      
  1.1283 +
  1.1284 +
  1.1285 +(defn run-terrain-game [type]
  1.1286 +  (.start
  1.1287 +   (world
  1.1288 +    (Node.)
  1.1289 +    {}
  1.1290 +    (partial setup-fn type)
  1.1291 +    no-op)))
  1.1292 +#+end_src
  1.1293 +
  1.1294 +
  1.1295 +
  1.1296 +#+srcname: hello-animation
  1.1297 +#+begin_src clojure :results silent
  1.1298 +(ns hello.animation)
  1.1299 +(use 'cortex.world)
  1.1300 +(use 'cortex.import)
  1.1301 +(use 'clojure.contrib.def)
  1.1302 +(cortex.import/mega-import-jme3)
  1.1303 +(rlm.rlm-commands/help)
  1.1304 +(use '[hello [collision :only [lights]]])
  1.1305 +
  1.1306 +(defn stand
  1.1307 +  [channel]
  1.1308 +  (doto channel
  1.1309 +    (.setAnim "stand" (float 0.5))
  1.1310 +    (.setLoopMode LoopMode/DontLoop)
  1.1311 +    (.setSpeed (float 1))))
  1.1312 +
  1.1313 +(defn anim-listener []
  1.1314 +     (proxy [AnimEventListener] []
  1.1315 +       (onAnimChange
  1.1316 +	[control channel animation-name]
  1.1317 +	(println-repl "RLM --- onAnimChange"))
  1.1318 +       (onAnimCycleDone
  1.1319 +	[control channel animation-name]
  1.1320 +	(if (= animation-name "Walk")
  1.1321 +	  (stand channel)
  1.1322 +	  ))))
  1.1323 +       
  1.1324 +(defn setup-fn [channel game]
  1.1325 +  (dorun (map #(.addLight (.getRootNode game) %) (lights)))
  1.1326 +  ;; set the color of the sky
  1.1327 +  (.setBackgroundColor (.getViewPort game) (ColorRGBA. 0.3 0.4 0.9 1))
  1.1328 +  ;(.setBackgroundColor (.getViewPort game) (ColorRGBA. 0 0 0 1)
  1.1329 +  (.setAnim channel "stand")
  1.1330 +  (doto (.getFlyByCamera game)
  1.1331 +    (.setMoveSpeed (float 10))
  1.1332 +    (.setRotationSpeed 1)))
  1.1333 +
  1.1334 +(defn walk [channel]
  1.1335 +  (println-repl "zzz")
  1.1336 +  (doto channel
  1.1337 +    (.setAnim "Walk" (float 0.5))
  1.1338 +    (.setLoopMode LoopMode/Loop)))
  1.1339 +    
  1.1340 +
  1.1341 +(defn key-map [channel]
  1.1342 +  {"key-space" (fn [game value]
  1.1343 +		 (if (not value)
  1.1344 +		   (walk channel)))})
  1.1345 +
  1.1346 +(defn player []
  1.1347 +  (let [model (.loadModel (asset-manager) "Models/Oto/Oto.mesh.xml")
  1.1348 +	control (.getControl model AnimControl)]
  1.1349 +    (.setLocalScale model (float 0.5))
  1.1350 +    (.clearListeners control)
  1.1351 +    (.addListener control (anim-control))
  1.1352 +    model))
  1.1353 +    
  1.1354 +
  1.1355 +
  1.1356 +(defn run-anim-game []
  1.1357 +  (let [ninja (player)
  1.1358 +	control  (.getControl ninja AnimControl)
  1.1359 +	channel (.createChannel control)]
  1.1360 +    (.start
  1.1361 +     (world
  1.1362 +      ninja
  1.1363 +      (key-map channel)
  1.1364 +      (partial setup-fn channel)
  1.1365 +      no-op))))
  1.1366 +#+end_src
  1.1367 +
  1.1368 +*** Hello Materials
  1.1369 +#+srcname: material
  1.1370 +#+begin_src clojure :results silent
  1.1371 +(ns hello.material)
  1.1372 +(use 'cortex.world)
  1.1373 +(use 'cortex.import)
  1.1374 +(use 'clojure.contrib.def)
  1.1375 +(cortex.import/mega-import-jme3)
  1.1376 +(rlm.rlm-commands/help)
  1.1377 +
  1.1378 +(defn simple-cube []
  1.1379 +  (box 1 1 1
  1.1380 +       :position (Vector3f. -3 1.1 0)
  1.1381 +       :material "Common/MatDefs/Misc/Unshaded.j3md"
  1.1382 +       :texture "Interface/Logo/Monkey.jpg"
  1.1383 +       :physical? false))
  1.1384 +
  1.1385 +(defn leaky-box []
  1.1386 +  (box 1 1 1
  1.1387 +       :position (Vector3f. 3 -1 0)
  1.1388 +       :material "Common/MatDefs/Misc/ColoredTextured.j3md"
  1.1389 +       :texture  "Textures/ColoredTex/Monkey.png"
  1.1390 +       :color    (ColorRGBA. 1 0 1 1)
  1.1391 +       :physical? false))
  1.1392 +
  1.1393 +(defn transparent-box []
  1.1394 +  (doto
  1.1395 +      (box 1 1 0.1
  1.1396 +	   :position Vector3f/ZERO
  1.1397 +	   :name "window frame"
  1.1398 +	   :material "Common/MatDefs/Misc/Unshaded.j3md"
  1.1399 +	   :texture "Textures/ColoredTex/Monkey.png"
  1.1400 +	   :physical? false)
  1.1401 +    (-> (.getMaterial)
  1.1402 +	(.getAdditionalRenderState)
  1.1403 +	(.setBlendMode RenderState$BlendMode/Alpha))
  1.1404 +    (.setQueueBucket RenderQueue$Bucket/Transparent)))
  1.1405 +                  
  1.1406 +(defn bumpy-sphere []
  1.1407 +  (doto 
  1.1408 +      (sphere 2
  1.1409 +	      :position (Vector3f. 0 2 -2)
  1.1410 +	      :name "Shiny rock"
  1.1411 +	      :material "Common/MatDefs/Light/Lighting.j3md"
  1.1412 +	      :texture false
  1.1413 +	      :physical? false)
  1.1414 +    (-> (.getMesh)
  1.1415 +	(doto 
  1.1416 +	  (.setTextureMode Sphere$TextureMode/Projected)
  1.1417 +	  (TangentBinormalGenerator/generate)))
  1.1418 +    (-> (.getMaterial)
  1.1419 +	(doto
  1.1420 +	  (.setTexture "DiffuseMap" (.loadTexture (asset-manager)
  1.1421 +						  "Textures/Terrain/Pond/Pond.png"))
  1.1422 +	  (.setTexture "NormalMap"  (.loadTexture (asset-manager)
  1.1423 +						  "Textures/Terrain/Pond/Pond_normal.png"))
  1.1424 +	  (.setFloat   "Shininess"  (float 5))))
  1.1425 +    (.rotate (float 1.6) 0 0)))
  1.1426 +
  1.1427 +
  1.1428 +(defn start-game []
  1.1429 +  (.start
  1.1430 +   (world
  1.1431 +    (let [root  (Node.)]
  1.1432 +      (dorun (map #(.attachChild root %)
  1.1433 +		  [(simple-cube) (leaky-box) (transparent-box) (bumpy-sphere)]))
  1.1434 +      root)
  1.1435 +    {}
  1.1436 +    (fn [world]
  1.1437 +      (let [sun (doto (DirectionalLight.)
  1.1438 +		  (.setDirection (.normalizeLocal (Vector3f. 1 0 -2)))
  1.1439 +		  (.setColor ColorRGBA/White))]
  1.1440 +	(.addLight (.getRootNode world) sun)))
  1.1441 +    no-op 
  1.1442 +    )))
  1.1443 +#+end_src
  1.1444 +
  1.1445 +
  1.1446 +    
  1.1447 +* The Body
  1.1448 +** Eyes
  1.1449 +
  1.1450 +Ultimately I want to make creatures with eyes. Each eye can be
  1.1451 +independely moved and should see its own version of the world
  1.1452 +depending on where it is.
  1.1453 +#+srcname: eyes
  1.1454 +#+begin_src clojure 
  1.1455 +(ns body.eye)
  1.1456 +(use 'cortex.world)
  1.1457 +(use 'cortex.import)
  1.1458 +(use 'clojure.contrib.def)
  1.1459 +(cortex.import/mega-import-jme3)
  1.1460 +(rlm.rlm-commands/help)
  1.1461 +(import java.nio.ByteBuffer)
  1.1462 +(import java.awt.image.BufferedImage)
  1.1463 +(import java.awt.Color)
  1.1464 +(import java.awt.Dimension)
  1.1465 +(import java.awt.Graphics)
  1.1466 +(import java.awt.Graphics2D)
  1.1467 +(import java.awt.event.WindowAdapter)
  1.1468 +(import java.awt.event.WindowEvent)
  1.1469 +(import java.awt.image.BufferedImage)
  1.1470 +(import java.nio.ByteBuffer)
  1.1471 +(import javax.swing.JFrame)
  1.1472 +(import javax.swing.JPanel)
  1.1473 +(import javax.swing.SwingUtilities)
  1.1474 +(import javax.swing.ImageIcon)
  1.1475 +(import javax.swing.JOptionPane)
  1.1476 +(import java.awt.image.ImageObserver)
  1.1477 +
  1.1478 +
  1.1479 +
  1.1480 +(defn scene-processor
  1.1481 +  "deals with converting FrameBuffers to BufferedImages so
  1.1482 +   that the continuation function can be defined only in terms
  1.1483 +   of what it does with BufferedImages"
  1.1484 +  [continuation]
  1.1485 +  (let [byte-buffer (atom nil)
  1.1486 +	renderer (atom nil)
  1.1487 +	image (atom nil)]
  1.1488 +  (proxy [SceneProcessor] []
  1.1489 +    (initialize
  1.1490 +     [renderManager viewPort]
  1.1491 +     (let [cam (.getCamera viewPort)
  1.1492 +	   width (.getWidth cam)
  1.1493 +	   height (.getHeight cam)]
  1.1494 +       (reset! renderer (.getRenderer renderManager))
  1.1495 +       (reset! byte-buffer
  1.1496 +	     (BufferUtils/createByteBuffer
  1.1497 +	      (* width height 4)))
  1.1498 +       (reset! image (BufferedImage. width height
  1.1499 +				     BufferedImage/TYPE_4BYTE_ABGR))))
  1.1500 +    (isInitialized [] (not (nil? @byte-buffer)))
  1.1501 +    (reshape [_ _ _])
  1.1502 +    (preFrame [_])
  1.1503 +    (postQueue [_])
  1.1504 +    (postFrame
  1.1505 +     [#^FrameBuffer fb]
  1.1506 +     (.clear @byte-buffer)
  1.1507 +     (.readFrameBuffer @renderer fb @byte-buffer)
  1.1508 +     (Screenshots/convertScreenShot @byte-buffer @image)
  1.1509 +     (continuation @image))
  1.1510 +    (cleanup []))))
  1.1511 +    
  1.1512 +(defn add-eye
  1.1513 +  "Add an eye to the world, and call continuation on
  1.1514 +   every frame produced"
  1.1515 +  [world camera continuation]
  1.1516 +  (let [width (.getWidth camera)
  1.1517 +	height (.getHeight camera)
  1.1518 +	render-manager (.getRenderManager world)
  1.1519 +	viewport (.createMainView render-manager "eye-view" camera)]
  1.1520 +    (doto viewport
  1.1521 +      (.setBackgroundColor ColorRGBA/Black)
  1.1522 +      (.setClearFlags true true true)
  1.1523 +      (.addProcessor (scene-processor continuation))
  1.1524 +      (.attachScene (.getRootNode world)))))
  1.1525 +
  1.1526 +(defn make-display-frame [display width height]
  1.1527 +  (SwingUtilities/invokeLater
  1.1528 +   (fn []
  1.1529 +     (.setPreferredSize display (Dimension. width height))
  1.1530 +     (doto (JFrame. "Eye Camera!")
  1.1531 +       (-> (.getContentPane) (.add display))
  1.1532 +       (.setDefaultCloseOperation JFrame/DISPOSE_ON_CLOSE)
  1.1533 +       (.pack)
  1.1534 +       (.setLocationRelativeTo nil)
  1.1535 +       (.setResizable false)
  1.1536 +       (.setVisible true)))))
  1.1537 +	   
  1.1538 +(defn image-monitor [#^BufferedImage image]
  1.1539 +  (proxy [JPanel] []
  1.1540 +    (paintComponent 
  1.1541 +     [g]
  1.1542 +     (proxy-super paintComponent g)
  1.1543 +     (locking image
  1.1544 +       (.drawImage g image 0 0
  1.1545 +		   (proxy [ImageObserver]
  1.1546 +		       []
  1.1547 +		     (imageUpdate
  1.1548 +		      []
  1.1549 +		      (proxy-super imageUpdate))))))))
  1.1550 +
  1.1551 +(defn movie-image []
  1.1552 +  (let [setup
  1.1553 +	(runonce
  1.1554 +	 (fn [#^BufferedImage image]
  1.1555 +	   (let [width (.getWidth image)
  1.1556 +		 height (.getHeight image)
  1.1557 +		 display (image-monitor image)
  1.1558 +		 frame (make-display-frame display width height)]
  1.1559 +	     display)))]
  1.1560 +    (fn [#^BufferedImage image]
  1.1561 +      (.repaint (setup image)))))
  1.1562 +	     
  1.1563 +
  1.1564 +(defn observer
  1.1565 +  "place thy eye!"
  1.1566 +  [world camera]
  1.1567 +  (let [eye camera
  1.1568 +	width (.getWidth eye)
  1.1569 +	height (.getHeight eye)]
  1.1570 +    (no-exceptions
  1.1571 +     (add-eye
  1.1572 +      world
  1.1573 +      eye
  1.1574 +      (movie-image)))))
  1.1575 +#+end_src
  1.1576 +
  1.1577 +#+srcname: test-vision
  1.1578 +#+begin_src clojure
  1.1579 +
  1.1580 +(ns test.vision)
  1.1581 +(use 'cortex.world)
  1.1582 +(use 'cortex.import)
  1.1583 +(use 'clojure.contrib.def)
  1.1584 +(use 'body.eye)
  1.1585 +(cortex.import/mega-import-jme3)
  1.1586 +(rlm.rlm-commands/help)
  1.1587 +(import java.nio.ByteBuffer)
  1.1588 +(import java.awt.image.BufferedImage)
  1.1589 +(import java.awt.Color)
  1.1590 +(import java.awt.Dimension)
  1.1591 +(import java.awt.Graphics)
  1.1592 +(import java.awt.Graphics2D)
  1.1593 +(import java.awt.event.WindowAdapter)
  1.1594 +(import java.awt.event.WindowEvent)
  1.1595 +(import java.awt.image.BufferedImage)
  1.1596 +(import java.nio.ByteBuffer)
  1.1597 +(import javax.swing.JFrame)
  1.1598 +(import javax.swing.JPanel)
  1.1599 +(import javax.swing.SwingUtilities)
  1.1600 +(import javax.swing.ImageIcon)
  1.1601 +(import javax.swing.JOptionPane)
  1.1602 +(import java.awt.image.ImageObserver)
  1.1603 +
  1.1604 +
  1.1605 +(def width 200)
  1.1606 +(def height 200)
  1.1607 +
  1.1608 +(defn camera []
  1.1609 +  (doto (Camera. width height)
  1.1610 +    (.setFrustumPerspective 45 1 1 1000)
  1.1611 +    (.setLocation (Vector3f. -3 0 -5))
  1.1612 +    (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)))
  1.1613 +
  1.1614 +(defn camera2 []
  1.1615 +  (doto (Camera. width height)
  1.1616 +    (.setFrustumPerspective 45 1 1 1000)
  1.1617 +    (.setLocation (Vector3f. 3 0 -5))
  1.1618 +    (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)))
  1.1619 +
  1.1620 +(defn setup-fn [world]
  1.1621 +  (let [eye (camera)
  1.1622 +	width (.getWidth eye)
  1.1623 +	height (.getHeight eye)]
  1.1624 +    (no-exceptions
  1.1625 +     (add-eye
  1.1626 +      world
  1.1627 +      eye
  1.1628 +      (runonce visual))
  1.1629 +     (add-eye
  1.1630 +      world
  1.1631 +      (camera2)
  1.1632 +      (runonce visual)))))
  1.1633 +
  1.1634 +(defn spider-eye [position]
  1.1635 +  (doto (Camera. 200 200 )
  1.1636 +    (.setFrustumPerspective 45 1 1 1000)
  1.1637 +    (.setLocation position)
  1.1638 +    (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)))
  1.1639 +  
  1.1640 +(defn setup-fn* [world]
  1.1641 +  (let [eye (camera)
  1.1642 +	width (.getWidth eye)
  1.1643 +	height (.getHeight eye)]
  1.1644 +    ;;(.setClearFlags (.getViewPort world) true true true)
  1.1645 +    (observer world (.getCamera world))
  1.1646 +    (observer world (spider-eye (Vector3f.  3  0 -5)))
  1.1647 +    ;;(observer world (spider-eye (Vector3f.  0  0 -5)))
  1.1648 +    ;; (observer world (spider-eye (Vector3f. -3  0 -5)))
  1.1649 +    ;; (observer world (spider-eye (Vector3f.  0  3 -5)))
  1.1650 +    ;; (observer world (spider-eye (Vector3f.  0 -3 -5)))
  1.1651 +    ;; (observer world (spider-eye (Vector3f.  3  3 -5)))
  1.1652 +    ;; (observer world (spider-eye (Vector3f. -3  3 -5)))
  1.1653 +    ;; (observer world (spider-eye (Vector3f.  3 -3 -5)))
  1.1654 +    ;; (observer world (spider-eye (Vector3f. -3 -3 -5)))
  1.1655 +
  1.1656 +    )
  1.1657 +  world)
  1.1658 +
  1.1659 +(defn test-world []
  1.1660 +  (let [thing (box 1 1 1 :physical? false)]
  1.1661 +    (world
  1.1662 +     (doto (Node.)
  1.1663 +       (.attachChild thing))
  1.1664 +     {}
  1.1665 +     setup-fn
  1.1666 +     (fn [world tpf]
  1.1667 +       (.rotate thing (* tpf 0.2) 0 0)
  1.1668 +       ))))
  1.1669 +
  1.1670 +
  1.1671 +#+end_src
  1.1672 +
  1.1673 +
  1.1674 +#+results: eyes
  1.1675 +: #'body.eye/test-world
  1.1676 +
  1.1677 +Note the use of continuation passing style for connecting the eye to a
  1.1678 +function to process the output. The example code will create two
  1.1679 +videos of the same rotating cube from different angles, sutiable for
  1.1680 +stereoscopic vision.
  1.1681 +
  1.1682 +
  1.1683 +
  1.1684 +
  1.1685 +
  1.1686 +
  1.1687 +* COMMENT code generation
  1.1688 +#+begin_src clojure :tangle ../src/cortex/import.clj
  1.1689 +<<import>>
  1.1690 +#+end_src
  1.1691 +
  1.1692 +#+begin_src clojure :tangle ../src/hello/brick_wall.clj
  1.1693 +<<brick-wall-header>>
  1.1694 +<<brick-wall-body>>
  1.1695 +#+end_src
  1.1696 +
  1.1697 +#+begin_src clojure :tangle ../src/hello/hello_simple_app.clj
  1.1698 +<<hello-simple-app>>
  1.1699 +#+end_src
  1.1700 +  
  1.1701 +#+begin_src clojure :tangle ../src/cortex/world.clj
  1.1702 +<<world-inputs>>
  1.1703 +<<world>>
  1.1704 +<<world-shapes>>
  1.1705 +<<world-view>>
  1.1706 +#+end_src
  1.1707 +
  1.1708 +#+begin_src clojure :tangle ../src/cortex/other_games.clj
  1.1709 +<<other-games>>
  1.1710 +#+end_src
  1.1711 +
  1.1712 +#+begin_src clojure :tangle ../src/hello/loop.clj
  1.1713 +<<hello-loop>>
  1.1714 +#+end_src
  1.1715 +
  1.1716 +#+begin_src clojure :tangle ../src/hello/collision.clj
  1.1717 +<<hello-collision>>
  1.1718 +#+end_src
  1.1719 +
  1.1720 +#+begin_src clojure :tangle ../src/hello/terrain.clj
  1.1721 +<<hello-terrain>>
  1.1722 +#+end_src
  1.1723 +
  1.1724 +#+begin_src clojure :tangle ../src/hello/animation.clj
  1.1725 +<<hello-animation>>
  1.1726 +#+end_src
  1.1727 +
  1.1728 +#+begin_src clojure :tangle ../src/hello/material.clj
  1.1729 +<<material>>
  1.1730 +#+end_src
  1.1731 +
  1.1732 +#+begin_src clojure :tangle ../src/body/eye.clj
  1.1733 +<<eyes>>
  1.1734 +#+end_src
  1.1735 +
  1.1736 +#+begin_src clojure :tangle ../src/test/vision.clj
  1.1737 +<<test-vision>>
  1.1738 +#+end_src
  1.1739 +
  1.1740 +  
  1.1741 +