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