Mercurial > cortex
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 +