Mercurial > cortex
diff org/world.org @ 23:cab2da252494
split off the rest of cortex.org
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sun, 23 Oct 2011 23:54:26 -0700 |
parents | |
children | e965675ec4d0 |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/org/world.org Sun Oct 23 23:54:26 2011 -0700 1.3 @@ -0,0 +1,308 @@ 1.4 +#+title: A world for the creatures to live 1.5 +#+author: Robert McIntyre 1.6 +#+email: rlm@mit.edu 1.7 +#+description: Simulating senses for AI research using JMonkeyEngine3 1.8 +#+SETUPFILE: ../../aurellem/org/setup.org 1.9 +#+INCLUDE: ../../aurellem/org/level-0.org 1.10 +#+babel: :mkdirp yes :noweb yes :exports both 1.11 + 1.12 + 1.13 + 1.14 +*** World 1.15 + 1.16 +It is comvienent to wrap the JME elements that deal with creating a 1.17 +world, creation of basic objects, and Keyboard input with a nicer 1.18 +interface (at least for my purposes). 1.19 + 1.20 +#+srcname: world-inputs 1.21 +#+begin_src clojure :results silent 1.22 +(ns cortex.world) 1.23 +(require 'cortex.import) 1.24 +(use 'clojure.contrib.def) 1.25 +(rlm.rlm-commands/help) 1.26 +(cortex.import/mega-import-jme3) 1.27 + 1.28 +(defvar *app-settings* 1.29 + (doto (AppSettings. true) 1.30 + (.setFullscreen false) 1.31 + (.setTitle "Aurellem.") 1.32 + ;; disable 32 bit stuff for now 1.33 + ;;(.setAudioRenderer "Send") 1.34 + ) 1.35 + "These settings control how the game is displayed on the screen for 1.36 + debugging purposes. Use binding forms to change this if desired. 1.37 + Full-screen mode does not work on some computers.") 1.38 + 1.39 +(defn asset-manager 1.40 + "returns a new, configured assetManager" [] 1.41 + (JmeSystem/newAssetManager 1.42 + (.getResource 1.43 + (.getContextClassLoader (Thread/currentThread)) 1.44 + "com/jme3/asset/Desktop.cfg"))) 1.45 + 1.46 +(defmacro no-exceptions 1.47 + "Sweet relief like I never knew." 1.48 + [& forms] 1.49 + `(try ~@forms (catch Exception e# (.printStackTrace e#)))) 1.50 + 1.51 +(defn thread-exception-removal [] 1.52 + (println "removing exceptions from " (Thread/currentThread)) 1.53 + (.setUncaughtExceptionHandler 1.54 + (Thread/currentThread) 1.55 + (proxy [Thread$UncaughtExceptionHandler] [] 1.56 + (uncaughtException 1.57 + [thread thrown] 1.58 + (println "uncaught-exception thrown in " thread) 1.59 + (println (.getMessage thrown)))))) 1.60 + 1.61 +(def println-repl (bound-fn [& args] (apply println args))) 1.62 + 1.63 +(use '[pokemon [lpsolve :only [constant-map]]]) 1.64 + 1.65 +(defn no-op [& _]) 1.66 + 1.67 +(defn all-keys 1.68 + "Construct a map of strings representing all the manual inputs from 1.69 + either the keyboard or mouse." 1.70 + [] 1.71 + (let [inputs (constant-map KeyInput)] 1.72 + (assoc 1.73 + (zipmap (map (fn [field] 1.74 + (.toLowerCase (re-gsub #"_" "-" field))) (vals inputs)) 1.75 + (map (fn [val] (KeyTrigger. val)) (keys inputs))) 1.76 + ;;explicitly add mouse controls 1.77 + "mouse-left" (MouseButtonTrigger. 0) 1.78 + "mouse-middle" (MouseButtonTrigger. 2) 1.79 + "mouse-right" (MouseButtonTrigger. 1)))) 1.80 + 1.81 +(defn initialize-inputs 1.82 + "more java-interop cruft to establish keybindings for a particular virtual world" 1.83 + [game input-manager key-map] 1.84 + (doall (map (fn [[name trigger]] 1.85 + (.addMapping ^InputManager input-manager 1.86 + name (into-array (class trigger) [trigger]))) key-map)) 1.87 + (doall (map (fn [name] 1.88 + (.addListener ^InputManager input-manager game 1.89 + (into-array String [name]))) (keys key-map)))) 1.90 + 1.91 +#+end_src 1.92 + 1.93 +These functions are all for debug controlling of the world through 1.94 +keyboard and mouse. 1.95 + 1.96 +We reuse =constant-map= from =pokemon.lpsolve= to get the numerical 1.97 +values for all the keys defined in the =KeyInput= class. The 1.98 +documentation for =constant-map= is: 1.99 + 1.100 +#+begin_src clojure :results output 1.101 +(doc pokemon.lpsolve/constant-map) 1.102 +#+end_src 1.103 + 1.104 +#+results: 1.105 +: ------------------------- 1.106 +: pokemon.lpsolve/constant-map 1.107 +: ([class]) 1.108 +: Takes a class and creates a map of the static constant integer 1.109 +: fields with their names. This helps with C wrappers where they have 1.110 +: just defined a bunch of integer constants instead of enums 1.111 + 1.112 + 1.113 +Then, =all-keys= converts the constant names like =KEY_J= to the more 1.114 +clojure-like =key-j=, and returns a map from these keys to 1.115 +jMonkeyEngine KeyTrigger objects, the use of which will soon become 1.116 +apparent. =all-keys= also adds the three mouse button controls to the 1.117 +map. 1.118 + 1.119 +#+srcname: world 1.120 +#+begin_src clojure :results silent 1.121 +(in-ns 'cortex.world) 1.122 + 1.123 +(defn traverse 1.124 + "apply f to every non-node, deeply" 1.125 + [f node] 1.126 + (if (isa? (class node) Node) 1.127 + (dorun (map (partial traverse f) (.getChildren node))) 1.128 + (f node))) 1.129 + 1.130 +(def gravity (Vector3f. 0 -9.81 0)) 1.131 + 1.132 +(defn world 1.133 + [root-node key-map setup-fn update-fn] 1.134 + (let [physics-manager (BulletAppState.) 1.135 + shadow-renderer (BasicShadowRenderer. (asset-manager) (int 256)) 1.136 + ;;maybe use a better shadow renderer someday! 1.137 + ;;shadow-renderer (PssmShadowRenderer. (asset-manager) 256 1) 1.138 + ] 1.139 + (doto 1.140 + (proxy [SimpleApplication ActionListener] [] 1.141 + (simpleInitApp 1.142 + [] 1.143 + (no-exceptions 1.144 + (.setTimer this (IsoTimer. 60)) 1.145 + ;; Create key-map. 1.146 + (.setFrustumFar (.getCamera this) 300) 1.147 + (initialize-inputs this (.getInputManager this) (all-keys)) 1.148 + ;; Don't take control of the mouse 1.149 + (org.lwjgl.input.Mouse/setGrabbed false) 1.150 + ;; add all objects to the world 1.151 + (.attachChild (.getRootNode this) root-node) 1.152 + ;; enable physics 1.153 + ;; add a physics manager 1.154 + (.attach (.getStateManager this) physics-manager) 1.155 + (.setGravity (.getPhysicsSpace physics-manager) gravity) 1.156 + 1.157 + 1.158 + ;; go through every object and add it to the physics manager 1.159 + ;; if relavant. 1.160 + (traverse (fn [geom] 1.161 + (dorun 1.162 + (for [n (range (.getNumControls geom))] 1.163 + (do 1.164 + (println-repl "adding control " (.getControl geom n)) 1.165 + (.add (.getPhysicsSpace physics-manager) 1.166 + (.getControl geom n)))))) 1.167 + (.getRootNode this)) 1.168 + ;;(.addAll (.getPhysicsSpace physics-manager) (.getRootNode this)) 1.169 + 1.170 + (setup-fn this) 1.171 + (.setDirection shadow-renderer 1.172 + (.normalizeLocal (Vector3f. -1 -1 -1))) 1.173 + (.addProcessor (.getViewPort this) shadow-renderer) 1.174 + (.setShadowMode (.getRootNode this) RenderQueue$ShadowMode/Off) 1.175 + )) 1.176 + (simpleUpdate 1.177 + [tpf] 1.178 + (no-exceptions 1.179 + (update-fn this tpf))) 1.180 + (onAction 1.181 + [binding value tpf] 1.182 + ;; whenever a key is pressed, call the function returned from 1.183 + ;; key-map. 1.184 + (no-exceptions 1.185 + (if-let [react (key-map binding)] 1.186 + (react this value))))) 1.187 + ;; don't show a menu to change options. 1.188 + 1.189 + (.setShowSettings false) 1.190 + (.setPauseOnLostFocus false) 1.191 + (.setSettings *app-settings*)))) 1.192 + 1.193 +(defn apply-map 1.194 + "Like apply, but works for maps and functions that expect an implicit map 1.195 + and nothing else as in (fn [& {}]). 1.196 +------- Example ------- 1.197 + (defn jjj [& {:keys [www] :or {www \"oh yeah\"} :as env}] (println www)) 1.198 + (apply-map jjj {:www \"whatever\"}) 1.199 + -->\"whatever\"" 1.200 + [fn m] 1.201 + (apply fn (reduce #(into %1 %2) [] m))) 1.202 + 1.203 +#+end_src 1.204 + 1.205 + 1.206 +=world= is the most important function here. 1.207 +*** TODO more documentation 1.208 + 1.209 +#+srcname: world-shapes 1.210 +#+begin_src clojure :results silent 1.211 +(in-ns 'cortex.world) 1.212 +(defrecord shape-description 1.213 + [name 1.214 + color 1.215 + mass 1.216 + friction 1.217 + texture 1.218 + material 1.219 + position 1.220 + rotation 1.221 + shape 1.222 + physical?]) 1.223 + 1.224 +(def base-shape 1.225 + (shape-description. 1.226 + "default-shape" 1.227 + false 1.228 + ;;ColorRGBA/Blue 1.229 + 1.0 ;; mass 1.230 + 1.0 ;; friction 1.231 + ;; texture 1.232 + "Textures/Terrain/BrickWall/BrickWall.jpg" 1.233 + ;; material 1.234 + "Common/MatDefs/Misc/Unshaded.j3md" 1.235 + Vector3f/ZERO 1.236 + Quaternion/IDENTITY 1.237 + (Box. Vector3f/ZERO 0.5 0.5 0.5) 1.238 + true)) 1.239 + 1.240 +(defn make-shape 1.241 + [#^shape-description d] 1.242 + (let [asset-manager (if (:asset-manager d) (:asset-manager d) (asset-manager)) 1.243 + mat (Material. asset-manager (:material d)) 1.244 + geom (Geometry. (:name d) (:shape d))] 1.245 + (if (:texture d) 1.246 + (let [key (TextureKey. (:texture d))] 1.247 + (.setGenerateMips key true) 1.248 + (.setTexture mat "ColorMap" (.loadTexture asset-manager key)))) 1.249 + (if (:color d) (.setColor mat "Color" (:color d))) 1.250 + (.setMaterial geom mat) 1.251 + (if-let [rotation (:rotation d)] (.rotate geom rotation)) 1.252 + (.setLocalTranslation geom (:position d)) 1.253 + (if (:physical? d) 1.254 + (let [impact-shape (doto (GImpactCollisionShape. 1.255 + (.getMesh geom)) (.setMargin 0)) 1.256 + physics-control (RigidBodyControl. 1.257 + ;;impact-shape ;; comment to disable 1.258 + (float (:mass d)))] 1.259 + (.createJmeMesh impact-shape) 1.260 + (.addControl geom physics-control) 1.261 + ;;(.setSleepingThresholds physics-control (float 0) (float 0)) 1.262 + (.setFriction physics-control (:friction d)))) 1.263 + ;;the default is to keep this node in the physics engine forever. 1.264 + ;;these commands must come after the control is added to the geometry. 1.265 + ;; 1.266 + geom)) 1.267 + 1.268 +(defn box 1.269 + ([l w h & {:as options}] 1.270 + (let [options (merge base-shape options)] 1.271 + (make-shape (assoc options 1.272 + :shape (Box. l w h))))) 1.273 + ([] (box 0.5 0.5 0.5))) 1.274 + 1.275 +(defn sphere 1.276 + ([r & {:as options}] 1.277 + (let [options (merge base-shape options)] 1.278 + (make-shape (assoc options 1.279 + :shape (Sphere. 32 32 (float r)))))) 1.280 + ([] (sphere 0.5))) 1.281 + 1.282 +(defn add-element 1.283 + ([game element node] 1.284 + (.addAll 1.285 + (.getPhysicsSpace 1.286 + (.getState 1.287 + (.getStateManager game) 1.288 + BulletAppState)) 1.289 + element) 1.290 + (.attachChild node element)) 1.291 + ([game element] 1.292 + (add-element game element (.getRootNode game)))) 1.293 + 1.294 + 1.295 +(defn set-gravity* 1.296 + [game gravity] 1.297 + (traverse 1.298 + (fn [geom] 1.299 + (if-let 1.300 + [control (.getControl geom RigidBodyControl)] 1.301 + (do 1.302 + (.setGravity control gravity) 1.303 + (.applyImpulse control Vector3f/ZERO Vector3f/ZERO) 1.304 + ))) 1.305 + (.getRootNode game))) 1.306 + 1.307 +#+end_src 1.308 + 1.309 +These are convienence functions for creating JME objects and 1.310 +manipulating a world. 1.311 +