Mercurial > cortex
comparison 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 |
comparison
equal
deleted
inserted
replaced
22:157b416152ea | 23:cab2da252494 |
---|---|
1 #+title: A world for the creatures to live | |
2 #+author: Robert McIntyre | |
3 #+email: rlm@mit.edu | |
4 #+description: Simulating senses for AI research using JMonkeyEngine3 | |
5 #+SETUPFILE: ../../aurellem/org/setup.org | |
6 #+INCLUDE: ../../aurellem/org/level-0.org | |
7 #+babel: :mkdirp yes :noweb yes :exports both | |
8 | |
9 | |
10 | |
11 *** World | |
12 | |
13 It is comvienent to wrap the JME elements that deal with creating a | |
14 world, creation of basic objects, and Keyboard input with a nicer | |
15 interface (at least for my purposes). | |
16 | |
17 #+srcname: world-inputs | |
18 #+begin_src clojure :results silent | |
19 (ns cortex.world) | |
20 (require 'cortex.import) | |
21 (use 'clojure.contrib.def) | |
22 (rlm.rlm-commands/help) | |
23 (cortex.import/mega-import-jme3) | |
24 | |
25 (defvar *app-settings* | |
26 (doto (AppSettings. true) | |
27 (.setFullscreen false) | |
28 (.setTitle "Aurellem.") | |
29 ;; disable 32 bit stuff for now | |
30 ;;(.setAudioRenderer "Send") | |
31 ) | |
32 "These settings control how the game is displayed on the screen for | |
33 debugging purposes. Use binding forms to change this if desired. | |
34 Full-screen mode does not work on some computers.") | |
35 | |
36 (defn asset-manager | |
37 "returns a new, configured assetManager" [] | |
38 (JmeSystem/newAssetManager | |
39 (.getResource | |
40 (.getContextClassLoader (Thread/currentThread)) | |
41 "com/jme3/asset/Desktop.cfg"))) | |
42 | |
43 (defmacro no-exceptions | |
44 "Sweet relief like I never knew." | |
45 [& forms] | |
46 `(try ~@forms (catch Exception e# (.printStackTrace e#)))) | |
47 | |
48 (defn thread-exception-removal [] | |
49 (println "removing exceptions from " (Thread/currentThread)) | |
50 (.setUncaughtExceptionHandler | |
51 (Thread/currentThread) | |
52 (proxy [Thread$UncaughtExceptionHandler] [] | |
53 (uncaughtException | |
54 [thread thrown] | |
55 (println "uncaught-exception thrown in " thread) | |
56 (println (.getMessage thrown)))))) | |
57 | |
58 (def println-repl (bound-fn [& args] (apply println args))) | |
59 | |
60 (use '[pokemon [lpsolve :only [constant-map]]]) | |
61 | |
62 (defn no-op [& _]) | |
63 | |
64 (defn all-keys | |
65 "Construct a map of strings representing all the manual inputs from | |
66 either the keyboard or mouse." | |
67 [] | |
68 (let [inputs (constant-map KeyInput)] | |
69 (assoc | |
70 (zipmap (map (fn [field] | |
71 (.toLowerCase (re-gsub #"_" "-" field))) (vals inputs)) | |
72 (map (fn [val] (KeyTrigger. val)) (keys inputs))) | |
73 ;;explicitly add mouse controls | |
74 "mouse-left" (MouseButtonTrigger. 0) | |
75 "mouse-middle" (MouseButtonTrigger. 2) | |
76 "mouse-right" (MouseButtonTrigger. 1)))) | |
77 | |
78 (defn initialize-inputs | |
79 "more java-interop cruft to establish keybindings for a particular virtual world" | |
80 [game input-manager key-map] | |
81 (doall (map (fn [[name trigger]] | |
82 (.addMapping ^InputManager input-manager | |
83 name (into-array (class trigger) [trigger]))) key-map)) | |
84 (doall (map (fn [name] | |
85 (.addListener ^InputManager input-manager game | |
86 (into-array String [name]))) (keys key-map)))) | |
87 | |
88 #+end_src | |
89 | |
90 These functions are all for debug controlling of the world through | |
91 keyboard and mouse. | |
92 | |
93 We reuse =constant-map= from =pokemon.lpsolve= to get the numerical | |
94 values for all the keys defined in the =KeyInput= class. The | |
95 documentation for =constant-map= is: | |
96 | |
97 #+begin_src clojure :results output | |
98 (doc pokemon.lpsolve/constant-map) | |
99 #+end_src | |
100 | |
101 #+results: | |
102 : ------------------------- | |
103 : pokemon.lpsolve/constant-map | |
104 : ([class]) | |
105 : Takes a class and creates a map of the static constant integer | |
106 : fields with their names. This helps with C wrappers where they have | |
107 : just defined a bunch of integer constants instead of enums | |
108 | |
109 | |
110 Then, =all-keys= converts the constant names like =KEY_J= to the more | |
111 clojure-like =key-j=, and returns a map from these keys to | |
112 jMonkeyEngine KeyTrigger objects, the use of which will soon become | |
113 apparent. =all-keys= also adds the three mouse button controls to the | |
114 map. | |
115 | |
116 #+srcname: world | |
117 #+begin_src clojure :results silent | |
118 (in-ns 'cortex.world) | |
119 | |
120 (defn traverse | |
121 "apply f to every non-node, deeply" | |
122 [f node] | |
123 (if (isa? (class node) Node) | |
124 (dorun (map (partial traverse f) (.getChildren node))) | |
125 (f node))) | |
126 | |
127 (def gravity (Vector3f. 0 -9.81 0)) | |
128 | |
129 (defn world | |
130 [root-node key-map setup-fn update-fn] | |
131 (let [physics-manager (BulletAppState.) | |
132 shadow-renderer (BasicShadowRenderer. (asset-manager) (int 256)) | |
133 ;;maybe use a better shadow renderer someday! | |
134 ;;shadow-renderer (PssmShadowRenderer. (asset-manager) 256 1) | |
135 ] | |
136 (doto | |
137 (proxy [SimpleApplication ActionListener] [] | |
138 (simpleInitApp | |
139 [] | |
140 (no-exceptions | |
141 (.setTimer this (IsoTimer. 60)) | |
142 ;; Create key-map. | |
143 (.setFrustumFar (.getCamera this) 300) | |
144 (initialize-inputs this (.getInputManager this) (all-keys)) | |
145 ;; Don't take control of the mouse | |
146 (org.lwjgl.input.Mouse/setGrabbed false) | |
147 ;; add all objects to the world | |
148 (.attachChild (.getRootNode this) root-node) | |
149 ;; enable physics | |
150 ;; add a physics manager | |
151 (.attach (.getStateManager this) physics-manager) | |
152 (.setGravity (.getPhysicsSpace physics-manager) gravity) | |
153 | |
154 | |
155 ;; go through every object and add it to the physics manager | |
156 ;; if relavant. | |
157 (traverse (fn [geom] | |
158 (dorun | |
159 (for [n (range (.getNumControls geom))] | |
160 (do | |
161 (println-repl "adding control " (.getControl geom n)) | |
162 (.add (.getPhysicsSpace physics-manager) | |
163 (.getControl geom n)))))) | |
164 (.getRootNode this)) | |
165 ;;(.addAll (.getPhysicsSpace physics-manager) (.getRootNode this)) | |
166 | |
167 (setup-fn this) | |
168 (.setDirection shadow-renderer | |
169 (.normalizeLocal (Vector3f. -1 -1 -1))) | |
170 (.addProcessor (.getViewPort this) shadow-renderer) | |
171 (.setShadowMode (.getRootNode this) RenderQueue$ShadowMode/Off) | |
172 )) | |
173 (simpleUpdate | |
174 [tpf] | |
175 (no-exceptions | |
176 (update-fn this tpf))) | |
177 (onAction | |
178 [binding value tpf] | |
179 ;; whenever a key is pressed, call the function returned from | |
180 ;; key-map. | |
181 (no-exceptions | |
182 (if-let [react (key-map binding)] | |
183 (react this value))))) | |
184 ;; don't show a menu to change options. | |
185 | |
186 (.setShowSettings false) | |
187 (.setPauseOnLostFocus false) | |
188 (.setSettings *app-settings*)))) | |
189 | |
190 (defn apply-map | |
191 "Like apply, but works for maps and functions that expect an implicit map | |
192 and nothing else as in (fn [& {}]). | |
193 ------- Example ------- | |
194 (defn jjj [& {:keys [www] :or {www \"oh yeah\"} :as env}] (println www)) | |
195 (apply-map jjj {:www \"whatever\"}) | |
196 -->\"whatever\"" | |
197 [fn m] | |
198 (apply fn (reduce #(into %1 %2) [] m))) | |
199 | |
200 #+end_src | |
201 | |
202 | |
203 =world= is the most important function here. | |
204 *** TODO more documentation | |
205 | |
206 #+srcname: world-shapes | |
207 #+begin_src clojure :results silent | |
208 (in-ns 'cortex.world) | |
209 (defrecord shape-description | |
210 [name | |
211 color | |
212 mass | |
213 friction | |
214 texture | |
215 material | |
216 position | |
217 rotation | |
218 shape | |
219 physical?]) | |
220 | |
221 (def base-shape | |
222 (shape-description. | |
223 "default-shape" | |
224 false | |
225 ;;ColorRGBA/Blue | |
226 1.0 ;; mass | |
227 1.0 ;; friction | |
228 ;; texture | |
229 "Textures/Terrain/BrickWall/BrickWall.jpg" | |
230 ;; material | |
231 "Common/MatDefs/Misc/Unshaded.j3md" | |
232 Vector3f/ZERO | |
233 Quaternion/IDENTITY | |
234 (Box. Vector3f/ZERO 0.5 0.5 0.5) | |
235 true)) | |
236 | |
237 (defn make-shape | |
238 [#^shape-description d] | |
239 (let [asset-manager (if (:asset-manager d) (:asset-manager d) (asset-manager)) | |
240 mat (Material. asset-manager (:material d)) | |
241 geom (Geometry. (:name d) (:shape d))] | |
242 (if (:texture d) | |
243 (let [key (TextureKey. (:texture d))] | |
244 (.setGenerateMips key true) | |
245 (.setTexture mat "ColorMap" (.loadTexture asset-manager key)))) | |
246 (if (:color d) (.setColor mat "Color" (:color d))) | |
247 (.setMaterial geom mat) | |
248 (if-let [rotation (:rotation d)] (.rotate geom rotation)) | |
249 (.setLocalTranslation geom (:position d)) | |
250 (if (:physical? d) | |
251 (let [impact-shape (doto (GImpactCollisionShape. | |
252 (.getMesh geom)) (.setMargin 0)) | |
253 physics-control (RigidBodyControl. | |
254 ;;impact-shape ;; comment to disable | |
255 (float (:mass d)))] | |
256 (.createJmeMesh impact-shape) | |
257 (.addControl geom physics-control) | |
258 ;;(.setSleepingThresholds physics-control (float 0) (float 0)) | |
259 (.setFriction physics-control (:friction d)))) | |
260 ;;the default is to keep this node in the physics engine forever. | |
261 ;;these commands must come after the control is added to the geometry. | |
262 ;; | |
263 geom)) | |
264 | |
265 (defn box | |
266 ([l w h & {:as options}] | |
267 (let [options (merge base-shape options)] | |
268 (make-shape (assoc options | |
269 :shape (Box. l w h))))) | |
270 ([] (box 0.5 0.5 0.5))) | |
271 | |
272 (defn sphere | |
273 ([r & {:as options}] | |
274 (let [options (merge base-shape options)] | |
275 (make-shape (assoc options | |
276 :shape (Sphere. 32 32 (float r)))))) | |
277 ([] (sphere 0.5))) | |
278 | |
279 (defn add-element | |
280 ([game element node] | |
281 (.addAll | |
282 (.getPhysicsSpace | |
283 (.getState | |
284 (.getStateManager game) | |
285 BulletAppState)) | |
286 element) | |
287 (.attachChild node element)) | |
288 ([game element] | |
289 (add-element game element (.getRootNode game)))) | |
290 | |
291 | |
292 (defn set-gravity* | |
293 [game gravity] | |
294 (traverse | |
295 (fn [geom] | |
296 (if-let | |
297 [control (.getControl geom RigidBodyControl)] | |
298 (do | |
299 (.setGravity control gravity) | |
300 (.applyImpulse control Vector3f/ZERO Vector3f/ZERO) | |
301 ))) | |
302 (.getRootNode game))) | |
303 | |
304 #+end_src | |
305 | |
306 These are convienence functions for creating JME objects and | |
307 manipulating a world. | |
308 |