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