rlm@23
|
1 #+title: Helper Utilities
|
rlm@23
|
2 #+author: Robert McIntyre
|
rlm@23
|
3 #+email: rlm@mit.edu
|
rlm@23
|
4 #+description: Simulating senses for AI research using JMonkeyEngine3
|
rlm@23
|
5 #+SETUPFILE: ../../aurellem/org/setup.org
|
rlm@23
|
6 #+INCLUDE: ../../aurellem/org/level-0.org
|
rlm@23
|
7 #+babel: :mkdirp yes :noweb yes :exports both
|
rlm@23
|
8
|
rlm@23
|
9 ** Imports
|
rlm@23
|
10 jMonkeyEngine has a plethora of classes which can be overwhelming at
|
rlm@23
|
11 first. So that I one can get right to coding, it's good to take the
|
rlm@23
|
12 time right now and make a "import all" function which brings in all of
|
rlm@23
|
13 the important jme3 classes. Once I'm happy with the general structure
|
rlm@23
|
14 of a namespace I can deal with importing only the classes it actually
|
rlm@23
|
15 needs.
|
rlm@23
|
16
|
rlm@23
|
17 #+srcname: import
|
rlm@23
|
18 #+begin_src clojure :results silent
|
rlm@23
|
19 (ns cortex.import
|
rlm@23
|
20 (:require swank.util.class-browse))
|
rlm@23
|
21
|
rlm@23
|
22 (defn permissive-import
|
rlm@23
|
23 [classname]
|
rlm@23
|
24 (eval `(try (import '~classname)
|
rlm@23
|
25 (catch java.lang.Exception e#
|
rlm@23
|
26 (println "couldn't import " '~classname))))
|
rlm@23
|
27 classname)
|
rlm@23
|
28
|
rlm@23
|
29 (defn jme-class? [classname]
|
rlm@23
|
30 (and
|
rlm@23
|
31 (.startsWith classname "com.jme3.")
|
rlm@23
|
32 ;; Don't import the Lwjgl stuff since it can throw exceptions
|
rlm@23
|
33 ;; upon being loaded.
|
rlm@23
|
34 (not (re-matches #".*Lwjgl.*" classname))))
|
rlm@23
|
35
|
rlm@23
|
36 (defn jme-classes
|
rlm@23
|
37 "returns a list of all jme3 classes"
|
rlm@23
|
38 []
|
rlm@23
|
39 (filter
|
rlm@23
|
40 jme-class?
|
rlm@23
|
41 (map :name
|
rlm@23
|
42 swank.util.class-browse/available-classes)))
|
rlm@23
|
43
|
rlm@23
|
44 (defn mega-import-jme3
|
rlm@23
|
45 "Import ALL the jme classes. For REPL use."
|
rlm@23
|
46 []
|
rlm@23
|
47 (doall
|
rlm@23
|
48 (map (comp permissive-import symbol) (jme-classes))))
|
rlm@23
|
49 #+end_src
|
rlm@23
|
50
|
rlm@23
|
51 The =mega-import-jme3= is quite usefull for debugging purposes since
|
rlm@23
|
52 it allows completion for almost all of JME's classes.
|
rlm@23
|
53
|
rlm@23
|
54 Out of curiousity, let's see just how many classes =mega-import-jme3=
|
rlm@23
|
55 imports:
|
rlm@23
|
56
|
rlm@23
|
57 #+begin_src clojure :exports both
|
rlm@23
|
58 (clojure.core/count (cortex.import/jme-classes))
|
rlm@23
|
59 #+end_src
|
rlm@23
|
60
|
rlm@23
|
61 #+results:
|
rlm@23
|
62 : 955
|
rlm@23
|
63
|
rlm@25
|
64
|
rlm@23
|
65 #+srcname: world-view
|
rlm@23
|
66 #+begin_src clojure :results silent
|
rlm@25
|
67 (ns cortex.util)
|
rlm@25
|
68 (require 'cortex.import)
|
rlm@25
|
69 (cortex.import/mega-import-jme3)
|
rlm@25
|
70 (use 'cortex.world)
|
rlm@23
|
71 (defprotocol Viewable
|
rlm@23
|
72 (view [something]))
|
rlm@23
|
73
|
rlm@23
|
74 (extend-type com.jme3.scene.Geometry
|
rlm@23
|
75 Viewable
|
rlm@23
|
76 (view [geo]
|
rlm@23
|
77 (view (doto (Node.)(.attachChild geo)))))
|
rlm@23
|
78
|
rlm@23
|
79 (extend-type com.jme3.scene.Node
|
rlm@23
|
80 Viewable
|
rlm@23
|
81 (view [node]
|
rlm@23
|
82 (.start
|
rlm@23
|
83 (world node
|
rlm@23
|
84 {}
|
rlm@23
|
85 (fn [world]
|
rlm@23
|
86 (.enableDebug
|
rlm@23
|
87 (.getPhysicsSpace
|
rlm@23
|
88 (.getState
|
rlm@23
|
89 (.getStateManager world)
|
rlm@23
|
90 BulletAppState))
|
rlm@23
|
91 (asset-manager))
|
rlm@23
|
92 (set-gravity* world Vector3f/ZERO)
|
rlm@23
|
93 ;; (set-gravity* world (Vector3f. 0 (float -0.4) 0))
|
rlm@23
|
94 (let [sun (doto (DirectionalLight.)
|
rlm@23
|
95 (.setDirection (.normalizeLocal (Vector3f. 1 0 -2)))
|
rlm@23
|
96 (.setColor ColorRGBA/White))]
|
rlm@23
|
97 (.addLight (.getRootNode world) sun)))
|
rlm@23
|
98 no-op))))
|
rlm@23
|
99 #+end_src
|
rlm@23
|
100
|
rlm@23
|
101 Here I make the =Viewable= protocol and extend it to JME's types. Now
|
rlm@23
|
102 hello-world can be written as easily as:
|
rlm@23
|
103
|
rlm@23
|
104 #+begin_src clojure :results silent
|
rlm@23
|
105 (cortex.world/view (cortex.world/box))
|
rlm@23
|
106 #+end_src
|
rlm@24
|
107
|
rlm@24
|
108
|
rlm@25
|
109 #+srcname: util
|
rlm@25
|
110 #+begin_src clojure
|
rlm@25
|
111 (in-ns 'cortex.util)
|
rlm@25
|
112
|
rlm@25
|
113 (def println-repl (bound-fn [& args] (apply println args)))
|
rlm@25
|
114
|
rlm@25
|
115 (defn position-camera [game]
|
rlm@25
|
116 (doto (.getCamera game)
|
rlm@25
|
117 (.setLocation (Vector3f. 0 6 6))
|
rlm@25
|
118 (.lookAt Vector3f/ZERO (Vector3f. 0 1 0))))
|
rlm@25
|
119
|
rlm@25
|
120 (defn set-gravity*
|
rlm@25
|
121 [game gravity]
|
rlm@25
|
122 (traverse
|
rlm@25
|
123 (fn [geom]
|
rlm@25
|
124 (if-let
|
rlm@25
|
125 [control (.getControl geom RigidBodyControl)]
|
rlm@25
|
126 (do
|
rlm@25
|
127 (.setGravity control gravity)
|
rlm@25
|
128 (.applyImpulse control Vector3f/ZERO Vector3f/ZERO)
|
rlm@25
|
129 )))
|
rlm@25
|
130 (.getRootNode game)))
|
rlm@25
|
131 #+end_src
|
rlm@25
|
132
|
rlm@25
|
133
|
rlm@25
|
134 #+srcname: shapes
|
rlm@25
|
135 #+begin_src clojure :results silent
|
rlm@25
|
136 (in-ns 'cortex.util)
|
rlm@25
|
137 (defrecord shape-description
|
rlm@25
|
138 [name
|
rlm@25
|
139 color
|
rlm@25
|
140 mass
|
rlm@25
|
141 friction
|
rlm@25
|
142 texture
|
rlm@25
|
143 material
|
rlm@25
|
144 position
|
rlm@25
|
145 rotation
|
rlm@25
|
146 shape
|
rlm@25
|
147 physical?])
|
rlm@25
|
148
|
rlm@25
|
149 (def base-shape
|
rlm@25
|
150 (shape-description.
|
rlm@25
|
151 "default-shape"
|
rlm@25
|
152 false
|
rlm@25
|
153 ;;ColorRGBA/Blue
|
rlm@25
|
154 1.0 ;; mass
|
rlm@25
|
155 1.0 ;; friction
|
rlm@25
|
156 ;; texture
|
rlm@25
|
157 "Textures/Terrain/BrickWall/BrickWall.jpg"
|
rlm@25
|
158 ;; material
|
rlm@25
|
159 "Common/MatDefs/Misc/Unshaded.j3md"
|
rlm@25
|
160 Vector3f/ZERO
|
rlm@25
|
161 Quaternion/IDENTITY
|
rlm@25
|
162 (Box. Vector3f/ZERO 0.5 0.5 0.5)
|
rlm@25
|
163 true))
|
rlm@25
|
164
|
rlm@25
|
165 (defn make-shape
|
rlm@25
|
166 [#^shape-description d]
|
rlm@25
|
167 (let [asset-manager (if (:asset-manager d) (:asset-manager d) (asset-manager))
|
rlm@25
|
168 mat (Material. asset-manager (:material d))
|
rlm@25
|
169 geom (Geometry. (:name d) (:shape d))]
|
rlm@25
|
170 (if (:texture d)
|
rlm@25
|
171 (let [key (TextureKey. (:texture d))]
|
rlm@25
|
172 (.setGenerateMips key true)
|
rlm@25
|
173 (.setTexture mat "ColorMap" (.loadTexture asset-manager key))))
|
rlm@25
|
174 (if (:color d) (.setColor mat "Color" (:color d)))
|
rlm@25
|
175 (.setMaterial geom mat)
|
rlm@25
|
176 (if-let [rotation (:rotation d)] (.rotate geom rotation))
|
rlm@25
|
177 (.setLocalTranslation geom (:position d))
|
rlm@25
|
178 (if (:physical? d)
|
rlm@25
|
179 (let [impact-shape (doto (GImpactCollisionShape.
|
rlm@25
|
180 (.getMesh geom)) (.setMargin 0))
|
rlm@25
|
181 physics-control (RigidBodyControl.
|
rlm@25
|
182 ;;impact-shape ;; comment to disable
|
rlm@25
|
183 (float (:mass d)))]
|
rlm@25
|
184 (.createJmeMesh impact-shape)
|
rlm@25
|
185 (.addControl geom physics-control)
|
rlm@25
|
186 ;;(.setSleepingThresholds physics-control (float 0) (float 0))
|
rlm@25
|
187 (.setFriction physics-control (:friction d))))
|
rlm@25
|
188 ;;the default is to keep this node in the physics engine forever.
|
rlm@25
|
189 ;;these commands must come after the control is added to the geometry.
|
rlm@25
|
190 ;;
|
rlm@25
|
191 geom))
|
rlm@25
|
192
|
rlm@25
|
193 (defn box
|
rlm@25
|
194 ([l w h & {:as options}]
|
rlm@25
|
195 (let [options (merge base-shape options)]
|
rlm@25
|
196 (make-shape (assoc options
|
rlm@25
|
197 :shape (Box. l w h)))))
|
rlm@25
|
198 ([] (box 0.5 0.5 0.5)))
|
rlm@25
|
199
|
rlm@25
|
200 (defn sphere
|
rlm@25
|
201 ([r & {:as options}]
|
rlm@25
|
202 (let [options (merge base-shape options)]
|
rlm@25
|
203 (make-shape (assoc options
|
rlm@25
|
204 :shape (Sphere. 32 32 (float r))))))
|
rlm@25
|
205 ([] (sphere 0.5)))
|
rlm@25
|
206
|
rlm@25
|
207 (defn add-element
|
rlm@25
|
208 ([game element node]
|
rlm@25
|
209 (.addAll
|
rlm@25
|
210 (.getPhysicsSpace
|
rlm@25
|
211 (.getState
|
rlm@25
|
212 (.getStateManager game)
|
rlm@25
|
213 BulletAppState))
|
rlm@25
|
214 element)
|
rlm@25
|
215 (.attachChild node element))
|
rlm@25
|
216 ([game element]
|
rlm@25
|
217 (add-element game element (.getRootNode game))))
|
rlm@25
|
218
|
rlm@25
|
219
|
rlm@26
|
220 (defn apply-map
|
rlm@26
|
221 "Like apply, but works for maps and functions that expect an
|
rlm@26
|
222 implicit map and nothing else as in (fn [& {}]).
|
rlm@26
|
223 ------- Example -------
|
rlm@26
|
224 (defn demo [& {:keys [www] :or {www \"oh yeah\"} :as env}]
|
rlm@26
|
225 (println www))
|
rlm@26
|
226 (apply-map demo {:www \"hello!\"})
|
rlm@26
|
227 -->\"hello\""
|
rlm@26
|
228 [fn m]
|
rlm@26
|
229 (apply fn (reduce #(into %1 %2) [] m)))
|
rlm@25
|
230
|
rlm@25
|
231 #+end_src
|
rlm@25
|
232
|
rlm@25
|
233
|
rlm@24
|
234
|
rlm@24
|
235 * COMMENT code generation
|
rlm@24
|
236 #+begin_src clojure :tangle ../src/cortex/import.clj
|
rlm@24
|
237 <<import>>
|
rlm@24
|
238 #+end_src
|
rlm@25
|
239
|
rlm@25
|
240
|
rlm@25
|
241 #+begin_src clojure :tangle ../src/cortex/util.clj
|
rlm@25
|
242 <<world-view>>
|
rlm@25
|
243 <<util>>
|
rlm@25
|
244 <<shapes>>
|
rlm@25
|
245 #+end_src
|
rlm@25
|
246
|