Mercurial > cortex
comparison org/world.org @ 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 |
comparison
equal
deleted
inserted
replaced
24:e965675ec4d0 | 25:775d97247dd0 |
---|---|
1 #+title: A world for the creatures to live | 1 #+title: A World for the Creatures |
2 #+author: Robert McIntyre | 2 #+author: Robert McIntyre |
3 #+email: rlm@mit.edu | 3 #+email: rlm@mit.edu |
4 #+description: Simulating senses for AI research using JMonkeyEngine3 | 4 #+description: Creating a Virtual World for AI constructs using clojure and JME3 |
5 #+keywords: JME3, clojure, virtual world, exception handling | |
5 #+SETUPFILE: ../../aurellem/org/setup.org | 6 #+SETUPFILE: ../../aurellem/org/setup.org |
6 #+INCLUDE: ../../aurellem/org/level-0.org | 7 #+INCLUDE: ../../aurellem/org/level-0.org |
7 #+babel: :mkdirp yes :noweb yes :exports both | 8 #+babel: :mkdirp yes :noweb yes :exports both |
8 | 9 |
9 | 10 * The World |
10 | 11 |
11 *** World | 12 There's no point in having senses if there's nothing to experience. In |
12 | 13 this section I make some tools with which to build virtual worlds for |
13 It is comvienent to wrap the JME elements that deal with creating a | 14 my characters to inhabit. If you look at the tutorials at [[http://www.jmonkeyengine.org/wiki/doku.php/jme3:beginner][the jme3 |
14 world, creation of basic objects, and Keyboard input with a nicer | 15 website]], you will see a pattern in how virtual worlds are normally |
15 interface (at least for my purposes). | 16 built. I call this "the Java way" of making worlds. |
16 | 17 |
17 #+srcname: world-inputs | 18 - The Java way: |
18 #+begin_src clojure :results silent | 19 - Create a class that extends =SimpleApplication= or =Application= |
19 (ns cortex.world) | 20 - Implement setup functions that create all the scene objects using |
20 (require 'cortex.import) | 21 the inherited =assetManager= and call them by overriding the |
21 (use 'clojure.contrib.def) | 22 =simpleInitApp= method. |
22 (rlm.rlm-commands/help) | 23 - Create =ActionListeners= and add them to the =inputManager= |
23 (cortex.import/mega-import-jme3) | 24 inherited from =Application= to handle key-bindings. |
25 - Override =simpleUpdate= to implement game logic. | |
26 - Running/Testing an Application involves creating a new JVM, | |
27 running the App, and then closing everything down. | |
28 | |
29 | |
30 - A more Clojureish way: | |
31 - Use a map from keys->functions to specify key-bindings. | |
32 - Use functions to create objects separately from any particular | |
33 application. | |
34 - Use an REPL -- this means that there's only ever one JVM, and | |
35 Applications come and go. | |
36 | |
37 Since most development work using jMonkeyEngine is done in Java, jme3 | |
38 supports "the Java way" quite well out of the box. To work "the | |
39 clojure way", it necessary to wrap the jme3 elements that deal with | |
40 the Application life-cycle with a REPL driven interface. | |
41 | |
42 The most important modifications are: | |
43 | |
44 - Separation of Object life-cycles with the Application life-cycle. | |
45 - Functional interface to the underlying =Application= and | |
46 =SimpleApplication= classes. | |
47 | |
48 ** Header | |
49 #+srcname: header | |
50 #+begin_src clojure :results silent | |
51 (ns cortex.world | |
52 "World Creation, abstracion over jme3's input system, and REPL | |
53 driven exception handling" | |
54 {:author "Robert McIntyre"} | |
55 | |
56 (:use (clojure.contrib (def :only (defvar)))) | |
57 (:use [pokemon [lpsolve :only [constant-map]]]) | |
58 (:use [clojure.contrib [str-utils :only [re-gsub]]]) | |
59 | |
60 (:import com.jme3.math.Vector3f) | |
61 (:import com.jme3.scene.Node) | |
62 (:import com.jme3.system.AppSettings) | |
63 (:import com.jme3.system.JmeSystem) | |
64 (:import com.jme3.system.IsoTimer) | |
65 (:import com.jme3.input.KeyInput) | |
66 (:import com.jme3.input.controls.KeyTrigger) | |
67 (:import com.jme3.input.controls.MouseButtonTrigger) | |
68 (:import com.jme3.input.InputManager) | |
69 (:import com.jme3.bullet.BulletAppState) | |
70 (:import com.jme3.shadow.BasicShadowRenderer) | |
71 (:import com.jme3.app.SimpleApplication) | |
72 (:import com.jme3.input.controls.ActionListener) | |
73 (:import com.jme3.renderer.queue.RenderQueue$ShadowMode) | |
74 (:import org.lwjgl.input.Mouse)) | |
75 #+end_src | |
76 | |
77 ** General Settings | |
78 #+srcname: settings | |
79 #+begin_src clojure | |
80 (in-ns 'cortex.world) | |
24 | 81 |
25 (defvar *app-settings* | 82 (defvar *app-settings* |
26 (doto (AppSettings. true) | 83 (doto (AppSettings. true) |
27 (.setFullscreen false) | 84 (.setFullscreen false) |
28 (.setTitle "Aurellem.") | 85 (.setTitle "Aurellem.") |
29 ;; disable 32 bit stuff for now | 86 ;; disable 32 bit stuff for now |
30 ;;(.setAudioRenderer "Send") | 87 ;;(.setAudioRenderer "Send") |
31 ) | 88 ) |
32 "These settings control how the game is displayed on the screen for | 89 "These settings control how the game is displayed on the screen for |
33 debugging purposes. Use binding forms to change this if desired. | 90 debugging purposes. Use binding forms to change this if desired. |
34 Full-screen mode does not work on some computers.") | 91 Full-screen mode does not work on some computers.") |
35 | 92 |
36 (defn asset-manager | 93 (defn asset-manager |
37 "returns a new, configured assetManager" [] | 94 "returns a new, configured assetManager" [] |
38 (JmeSystem/newAssetManager | 95 (JmeSystem/newAssetManager |
39 (.getResource | 96 (.getResource |
40 (.getContextClassLoader (Thread/currentThread)) | 97 (.getContextClassLoader (Thread/currentThread)) |
41 "com/jme3/asset/Desktop.cfg"))) | 98 "com/jme3/asset/Desktop.cfg"))) |
99 #+end_src | |
100 | |
101 Normally, people just use the =AssetManager= inherited from | |
102 =Application= whenever they extend that class. However, | |
103 =AssetManagers= are useful on their own to create objects/ materials, | |
104 independent from any particular application. =(asset-manager)= makes | |
105 object creation less tightly bound to Application initialization. | |
106 | |
107 | |
108 ** Exception Protection | |
109 #+srcname: exceptions | |
110 #+begin_src clojure | |
111 (in-ns 'cortex.world) | |
42 | 112 |
43 (defmacro no-exceptions | 113 (defmacro no-exceptions |
44 "Sweet relief like I never knew." | 114 "Sweet relief like I never knew." |
45 [& forms] | 115 [& forms] |
46 `(try ~@forms (catch Exception e# (.printStackTrace e#)))) | 116 `(try ~@forms (catch Exception e# (.printStackTrace e#)))) |
47 | 117 |
48 (defn thread-exception-removal [] | 118 (defn thread-exception-removal |
49 (println "removing exceptions from " (Thread/currentThread)) | 119 "Exceptions thrown in the graphics rendering thread generally cause |
120 the entire REPL to crash! It is good to suppress them while trying | |
121 things out to shorten the debug loop." | |
122 [] | |
50 (.setUncaughtExceptionHandler | 123 (.setUncaughtExceptionHandler |
51 (Thread/currentThread) | 124 (Thread/currentThread) |
52 (proxy [Thread$UncaughtExceptionHandler] [] | 125 (proxy [Thread$UncaughtExceptionHandler] [] |
53 (uncaughtException | 126 (uncaughtException |
54 [thread thrown] | 127 [thread thrown] |
55 (println "uncaught-exception thrown in " thread) | 128 (println "uncaught-exception thrown in " thread) |
56 (println (.getMessage thrown)))))) | 129 (println (.getMessage thrown)))))) |
57 | 130 |
58 (def println-repl (bound-fn [& args] (apply println args))) | 131 #+end_src |
59 | 132 |
60 (use '[pokemon [lpsolve :only [constant-map]]]) | 133 Exceptions thrown in the LWJGL render thread, if not caught, will |
61 | 134 destroy the entire JVM process including the REPL and slow development |
62 (defn no-op [& _]) | 135 to a crawl. It is better to try to continue on in the face of |
136 exceptions and keep the REPL alive as long as possible. Normally it | |
137 is possible to just exit the faulty Application, fix the bug, | |
138 reevaluate the appropriate forms, and be on your way, without | |
139 restarting the JVM. | |
140 | |
141 ** Input | |
142 #+srcname: input | |
143 #+begin_src clojure | |
144 (in-ns 'cortex.world) | |
63 | 145 |
64 (defn all-keys | 146 (defn all-keys |
65 "Construct a map of strings representing all the manual inputs from | 147 "Uses reflection to generate a map of string names to jme3 trigger |
66 either the keyboard or mouse." | 148 objects, which govern input from the keyboard and mouse" |
67 [] | 149 [] |
68 (let [inputs (constant-map KeyInput)] | 150 (let [inputs (pokemon.lpsolve/constant-map KeyInput)] |
69 (assoc | 151 (assoc |
70 (zipmap (map (fn [field] | 152 (zipmap (map (fn [field] |
71 (.toLowerCase (re-gsub #"_" "-" field))) (vals inputs)) | 153 (.toLowerCase (re-gsub #"_" "-" field))) (vals inputs)) |
72 (map (fn [val] (KeyTrigger. val)) (keys inputs))) | 154 (map (fn [val] (KeyTrigger. val)) (keys inputs))) |
73 ;;explicitly add mouse controls | 155 ;;explicitly add mouse controls |
74 "mouse-left" (MouseButtonTrigger. 0) | 156 "mouse-left" (MouseButtonTrigger. 0) |
75 "mouse-middle" (MouseButtonTrigger. 2) | 157 "mouse-middle" (MouseButtonTrigger. 2) |
76 "mouse-right" (MouseButtonTrigger. 1)))) | 158 "mouse-right" (MouseButtonTrigger. 1)))) |
77 | 159 |
78 (defn initialize-inputs | 160 (defn initialize-inputs |
79 "more java-interop cruft to establish keybindings for a particular virtual world" | 161 "Establish key-bindings for a particular virtual world." |
80 [game input-manager key-map] | 162 [game input-manager key-map] |
81 (doall (map (fn [[name trigger]] | 163 (doall |
82 (.addMapping ^InputManager input-manager | 164 (map (fn [[name trigger]] |
83 name (into-array (class trigger) [trigger]))) key-map)) | 165 (.addMapping |
84 (doall (map (fn [name] | 166 ^InputManager input-manager |
85 (.addListener ^InputManager input-manager game | 167 name (into-array (class trigger) |
86 (into-array String [name]))) (keys key-map)))) | 168 [trigger]))) key-map)) |
87 | 169 (doall |
88 #+end_src | 170 (map (fn [name] |
89 | 171 (.addListener |
90 These functions are all for debug controlling of the world through | 172 ^InputManager input-manager game |
91 keyboard and mouse. | 173 (into-array String [name]))) (keys key-map)))) |
92 | 174 |
93 We reuse =constant-map= from =pokemon.lpsolve= to get the numerical | 175 #+end_src |
176 | |
177 These functions are for controlling the world through the keyboard and | |
178 mouse. | |
179 | |
180 I reuse =constant-map= from [[../../pokemon-types/html/lpsolve.html#sec-3-3-4][=pokemon.lpsolve=]] to get the numerical | |
94 values for all the keys defined in the =KeyInput= class. The | 181 values for all the keys defined in the =KeyInput= class. The |
95 documentation for =constant-map= is: | 182 documentation for =constant-map= is: |
96 | 183 |
97 #+begin_src clojure :results output | 184 #+begin_src clojure :results output |
98 (doc pokemon.lpsolve/constant-map) | 185 (doc pokemon.lpsolve/constant-map) |
104 : ([class]) | 191 : ([class]) |
105 : Takes a class and creates a map of the static constant integer | 192 : 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 | 193 : fields with their names. This helps with C wrappers where they have |
107 : just defined a bunch of integer constants instead of enums | 194 : just defined a bunch of integer constants instead of enums |
108 | 195 |
109 | 196 =(all-keys)= converts the constant names like =KEY_J= to the more |
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 | 197 clojure-like =key-j=, and returns a map from these keys to |
112 jMonkeyEngine KeyTrigger objects, the use of which will soon become | 198 jMonkeyEngine =KeyTrigger= objects, which jMonkeyEngine3 uses as it's |
113 apparent. =all-keys= also adds the three mouse button controls to the | 199 abstraction over the physical keys. =all-keys= also adds the three |
114 map. | 200 mouse button controls to the map. |
115 | 201 |
202 | |
203 #+begin_src clojure :exports both :results output | |
204 (require 'clojure.contrib.pprint) | |
205 (clojure.contrib.pprint/pprint | |
206 (take 10 (keys (cortex.world/all-keys)))) | |
207 #+end_src | |
208 | |
209 #+results: | |
210 #+begin_example | |
211 ("key-n" | |
212 "key-apps" | |
213 "key-pgup" | |
214 "key-f8" | |
215 "key-o" | |
216 "key-at" | |
217 "key-f9" | |
218 "key-0" | |
219 "key-p" | |
220 "key-subtract") | |
221 #+end_example | |
222 | |
223 #+begin_src clojure :exports both :results output | |
224 (clojure.contrib.pprint/pprint | |
225 (take 10 (vals (cortex.world/all-keys)))) | |
226 #+end_src | |
227 | |
228 #+results: | |
229 #+begin_example | |
230 (#<KeyTrigger com.jme3.input.controls.KeyTrigger@6ec21e52> | |
231 #<KeyTrigger com.jme3.input.controls.KeyTrigger@a54d24d> | |
232 #<KeyTrigger com.jme3.input.controls.KeyTrigger@1ba5e91b> | |
233 #<KeyTrigger com.jme3.input.controls.KeyTrigger@296af9cb> | |
234 #<KeyTrigger com.jme3.input.controls.KeyTrigger@2e3593ab> | |
235 #<KeyTrigger com.jme3.input.controls.KeyTrigger@3f71d740> | |
236 #<KeyTrigger com.jme3.input.controls.KeyTrigger@4aeacb4a> | |
237 #<KeyTrigger com.jme3.input.controls.KeyTrigger@7cc88db2> | |
238 #<KeyTrigger com.jme3.input.controls.KeyTrigger@52cee11e> | |
239 #<KeyTrigger com.jme3.input.controls.KeyTrigger@c1da30b>) | |
240 #+end_example | |
241 | |
242 | |
243 | |
244 ** World Creation | |
116 #+srcname: world | 245 #+srcname: world |
117 #+begin_src clojure :results silent | 246 #+begin_src clojure :results silent |
118 (in-ns 'cortex.world) | 247 (in-ns 'cortex.world) |
248 | |
249 (defn no-op | |
250 "Takes any number of arguments and does nothing." | |
251 [& _]) | |
119 | 252 |
120 (defn traverse | 253 (defn traverse |
121 "apply f to every non-node, deeply" | 254 "apply f to every non-node, deeply" |
122 [f node] | 255 [f node] |
123 (if (isa? (class node) Node) | 256 (if (isa? (class node) Node) |
124 (dorun (map (partial traverse f) (.getChildren node))) | 257 (dorun (map (partial traverse f) (.getChildren node))) |
125 (f node))) | 258 (f node))) |
126 | 259 |
127 (def gravity (Vector3f. 0 -9.81 0)) | |
128 | |
129 (defn world | 260 (defn world |
261 "the =world= function takes care of the details of initializing a | |
262 SimpleApplication. | |
263 | |
264 ***** Arguments: | |
265 | |
266 - root-node : a com.jme3.scene.Node object which contains all of | |
267 the objects that should be in the simulation. | |
268 | |
269 - key-map : a map from strings describing keys to functions that | |
270 should be executed whenever that key is pressed. | |
271 the functions should take a SimpleApplication object and a | |
272 boolean value. The SimpleApplication is the current simulation | |
273 that is running, and the boolean is true if the key is being | |
274 pressed, and false if it is being released. As an example, | |
275 | |
276 {\"key-j\" (fn [game value] (if value (println \"key j pressed\")))} | |
277 | |
278 is a valid key-map which will cause the simulation to print a | |
279 message whenever the 'j' key on the keyboard is pressed. | |
280 | |
281 - setup-fn : a function that takes a SimpleApplication object. It | |
282 is called once when initializing the simulation. Use it to | |
283 create things like lights, change the gravity, initialize debug | |
284 nodes, etc. | |
285 | |
286 - update-fn : this function takes a SimpleApplication object and a | |
287 float and is called every frame of the simulation. The float | |
288 tells how many seconds is has been since the last frame was | |
289 rendered, according to whatever clock jme is currently | |
290 using. The default is to use IsoTimer which will result in this | |
291 value always being the same. | |
292 " | |
130 [root-node key-map setup-fn update-fn] | 293 [root-node key-map setup-fn update-fn] |
131 (let [physics-manager (BulletAppState.) | 294 (let [physics-manager (BulletAppState.) |
132 shadow-renderer (BasicShadowRenderer. (asset-manager) (int 256)) | 295 shadow-renderer (BasicShadowRenderer. |
133 ;;maybe use a better shadow renderer someday! | 296 (asset-manager) (int 256))] |
134 ;;shadow-renderer (PssmShadowRenderer. (asset-manager) 256 1) | 297 (doto |
135 ] | 298 (proxy [SimpleApplication ActionListener] [] |
136 (doto | 299 (simpleInitApp |
137 (proxy [SimpleApplication ActionListener] [] | 300 [] |
138 (simpleInitApp | 301 (no-exceptions |
139 [] | 302 ;; allow AI entities as much time as they need to think. |
140 (no-exceptions | 303 (.setTimer this (IsoTimer. 60)) |
141 (.setTimer this (IsoTimer. 60)) | 304 (.setFrustumFar (.getCamera this) 300) |
142 ;; Create key-map. | 305 ;; Create default key-map. |
143 (.setFrustumFar (.getCamera this) 300) | 306 (initialize-inputs this (.getInputManager this) (all-keys)) |
144 (initialize-inputs this (.getInputManager this) (all-keys)) | 307 ;; Don't take control of the mouse |
145 ;; Don't take control of the mouse | 308 (org.lwjgl.input.Mouse/setGrabbed false) |
146 (org.lwjgl.input.Mouse/setGrabbed false) | 309 ;; add all objects to the world |
147 ;; add all objects to the world | 310 (.attachChild (.getRootNode this) root-node) |
148 (.attachChild (.getRootNode this) root-node) | 311 ;; enable physics |
149 ;; enable physics | 312 ;; add a physics manager |
150 ;; add a physics manager | 313 (.attach (.getStateManager this) physics-manager) |
151 (.attach (.getStateManager this) physics-manager) | 314 (.setGravity (.getPhysicsSpace physics-manager) |
152 (.setGravity (.getPhysicsSpace physics-manager) gravity) | 315 (Vector3f. 0 -9.81 0)) |
153 | 316 ;; go through every object and add it to the physics |
154 | 317 ;; manager if relevant. |
155 ;; go through every object and add it to the physics manager | 318 (traverse (fn [geom] |
156 ;; if relavant. | 319 (dorun |
157 (traverse (fn [geom] | 320 (for [n (range (.getNumControls geom))] |
158 (dorun | 321 (do |
159 (for [n (range (.getNumControls geom))] | 322 (.add (.getPhysicsSpace physics-manager) |
160 (do | 323 (.getControl geom n)))))) |
161 (println-repl "adding control " (.getControl geom n)) | 324 (.getRootNode this)) |
162 (.add (.getPhysicsSpace physics-manager) | 325 ;;(.addAll (.getPhysicsSpace physics-manager) (.getRootNode this)) |
163 (.getControl geom n)))))) | 326 |
164 (.getRootNode this)) | 327 ;; set some basic defaults for the shadow renderer. |
165 ;;(.addAll (.getPhysicsSpace physics-manager) (.getRootNode this)) | 328 ;; these can be undone in the setup function |
166 | 329 (.setDirection shadow-renderer |
167 (setup-fn this) | 330 (.normalizeLocal (Vector3f. -1 -1 -1))) |
168 (.setDirection shadow-renderer | 331 (.addProcessor (.getViewPort this) shadow-renderer) |
169 (.normalizeLocal (Vector3f. -1 -1 -1))) | 332 (.setShadowMode (.getRootNode this) |
170 (.addProcessor (.getViewPort this) shadow-renderer) | 333 RenderQueue$ShadowMode/Off) |
171 (.setShadowMode (.getRootNode this) RenderQueue$ShadowMode/Off) | 334 ;; call the supplied setup-fn |
172 )) | 335 (if setup-fn |
173 (simpleUpdate | 336 (setup-fn this)))) |
174 [tpf] | 337 (simpleUpdate |
175 (no-exceptions | 338 [tpf] |
176 (update-fn this tpf))) | 339 (no-exceptions |
177 (onAction | 340 (update-fn this tpf))) |
178 [binding value tpf] | 341 (onAction |
179 ;; whenever a key is pressed, call the function returned from | 342 [binding value tpf] |
180 ;; key-map. | 343 ;; whenever a key is pressed, call the function returned |
181 (no-exceptions | 344 ;; from key-map. |
182 (if-let [react (key-map binding)] | 345 (no-exceptions |
183 (react this value))))) | 346 (if-let [react (key-map binding)] |
184 ;; don't show a menu to change options. | 347 (react this value))))) |
185 | 348 ;; don't show a menu to change options. |
186 (.setShowSettings false) | 349 (.setShowSettings false) |
187 (.setPauseOnLostFocus false) | 350 ;; continue running simulation even if the window has lost |
188 (.setSettings *app-settings*)))) | 351 ;; focus. |
352 (.setPauseOnLostFocus false) | |
353 (.setSettings *app-settings*)))) | |
189 | 354 |
190 (defn apply-map | 355 (defn apply-map |
191 "Like apply, but works for maps and functions that expect an implicit map | 356 "Like apply, but works for maps and functions that expect an |
192 and nothing else as in (fn [& {}]). | 357 implicit map and nothing else as in (fn [& {}]). |
193 ------- Example ------- | 358 ------- Example ------- |
194 (defn jjj [& {:keys [www] :or {www \"oh yeah\"} :as env}] (println www)) | 359 (defn demo [& {:keys [www] :or {www \"oh yeah\"} :as env}] |
195 (apply-map jjj {:www \"whatever\"}) | 360 (println www)) |
196 -->\"whatever\"" | 361 (apply-map demo {:www \"hello!\"}) |
362 -->\"hello\"" | |
197 [fn m] | 363 [fn m] |
198 (apply fn (reduce #(into %1 %2) [] m))) | 364 (apply fn (reduce #(into %1 %2) [] m))) |
199 | 365 |
200 #+end_src | 366 #+end_src |
201 | 367 |
202 | 368 |
203 =world= is the most important function here. | 369 =(world)= is the most important function here. It presents a more |
204 *** TODO more documentation | 370 functional interface to the Application life-cycle, and all it's |
205 | 371 objects except =root-node= are plain clojure data structures. It's now |
206 #+srcname: world-shapes | 372 possible to extend functionally by composing multiple functions |
207 #+begin_src clojure :results silent | 373 together, and to add more keyboard-driven actions by combining clojure |
208 (in-ns 'cortex.world) | 374 maps. |
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 | |
309 | |
310 | 375 |
311 | 376 |
312 | 377 |
313 * COMMENT code generation | 378 * COMMENT code generation |
314 | |
315 #+begin_src clojure :tangle ../src/cortex/world.clj | 379 #+begin_src clojure :tangle ../src/cortex/world.clj |
316 <<world-inputs>> | 380 <<header>> |
381 <<settings>> | |
382 <<exceptions>> | |
383 <<input>> | |
317 <<world>> | 384 <<world>> |
318 <<world-shapes>> | 385 #+end_src |
319 #+end_src |