view org/util.org @ 34:183744c179e6

MASSIVE cleanup, especially in the vision code
author Robert McIntyre <rlm@mit.edu>
date Thu, 03 Nov 2011 08:28:26 -0700
parents 0206878c28b4
children bc93abad23ee
line wrap: on
line source
1 #+title: Clojure Utilities for jMonkeyEngine3
2 #+author: Robert McIntyre
3 #+email: rlm@mit.edu
4 #+description:
5 #+keywords: JME3, clojure, import, utilities
6 #+SETUPFILE: ../../aurellem/org/setup.org
7 #+INCLUDE: ../../aurellem/org/level-0.org
9 [TABLE-OF-CONTENTS]
11 These are a collection of functions to make programming jMonkeyEngine
12 in clojure easier.
14 * Imports
16 #+srcname: import
17 #+begin_src clojure :results silent
18 (ns cortex.import
19 (:require swank.util.class-browse))
21 (defn permissive-import
22 [classname]
23 (eval `(try (import '~classname)
24 (catch java.lang.Exception e#
25 (println "couldn't import " '~classname))))
26 classname)
28 (defn jme-class? [classname]
29 (and
30 (.startsWith classname "com.jme3.")
31 ;; Don't import the Lwjgl stuff since it can throw exceptions
32 ;; upon being loaded.
33 (not (re-matches #".*Lwjgl.*" classname))))
35 (defn jme-classes
36 "returns a list of all jme3 classes"
37 []
38 (filter
39 jme-class?
40 (map :name
41 swank.util.class-browse/available-classes)))
43 (defn mega-import-jme3
44 "Import ALL the jme classes. For REPL use."
45 []
46 (doall
47 (map (comp permissive-import symbol) (jme-classes))))
48 #+end_src
50 jMonkeyEngine3 has a plethora of classes which can be overwhelming to
51 manage. This code uses reflection to import all of them. Once I'm
52 happy with the general structure of a namespace I can deal with
53 importing only the classes it actually needs.
55 The =mega-import-jme3= is quite usefull for debugging purposes since
56 it allows completion for almost all of JME's classes from the REPL.
58 Out of curiousity, let's see just how many classes =mega-import-jme3=
59 imports:
61 #+begin_src clojure :exports both :results output
62 (println (clojure.core/count (cortex.import/jme-classes)) "classes")
63 #+end_src
65 #+results:
66 : 955 classes
69 * Utilities
71 The utilities here come in three main groups:
72 - Changing settings in a running =Application=
73 - Creating objects
74 - Visualizing objects
77 *** Changing Settings
79 #+srcname: util
80 #+begin_src clojure
81 (ns cortex.util
82 "Utility functions for making jMonkeyEngine3 easier to program from
83 clojure."
84 {:author "Robert McIntyre"}
85 (:use cortex.world)
86 (:use clojure.contrib.def)
87 (:import com.jme3.math.Vector3f)
88 (:import com.jme3.math.Quaternion)
89 (:import com.jme3.asset.TextureKey)
90 (:import com.jme3.bullet.control.RigidBodyControl)
91 (:import com.jme3.bullet.collision.shapes.GImpactCollisionShape)
92 (:import com.jme3.scene.shape.Box)
93 (:import com.jme3.scene.Node)
94 (:import com.jme3.scene.shape.Sphere)
95 (:import com.jme3.light.DirectionalLight)
96 (:import com.jme3.math.ColorRGBA)
97 (:import com.jme3.bullet.BulletAppState)
98 (:import com.jme3.material.Material)
99 (:import com.jme3.scene.Geometry))
101 (defvar println-repl
102 (bound-fn [& args] (apply println args))
103 "println called from the LWJGL thread will not go to the REPL, but
104 instead to whatever terminal started the JVM process. This function
105 will always output to the REPL")
107 (defn position-camera
108 "Change the position of the in-world camera."
109 ([world position direction up]
110 (doto (.getCamera world)
111 (.setLocation )
112 (.lookAt direction up)))
113 ([world position direction]
114 (position-camera
115 world position direction Vector3f/UNIT_Y)))
117 (defn enable-debug
118 "Turn on debug wireframes for every object in this simulation."
119 [world]
120 (.enableDebug
121 (.getPhysicsSpace
122 (.getState
123 (.getStateManager world)
124 BulletAppState))
125 (asset-manager)))
127 (defn set-gravity
128 "In order to change the gravity of a scene, it is not only necessary
129 to set the gravity variable, but to \"tap\" every physics object in
130 the scene to reactivate physics calculations."
131 [world gravity]
132 (traverse
133 (fn [geom]
134 (if-let
135 ;; only set gravity for physical objects.
136 [control (.getControl geom RigidBodyControl)]
137 (do
138 (.setGravity control gravity)
139 ;; tappsies!
140 (.applyImpulse control Vector3f/ZERO Vector3f/ZERO))))
141 (.getRootNode world)))
143 (defn add-element
144 "Add the Spatial to the world's environment"
145 ([world element node]
146 (.addAll
147 (.getPhysicsSpace
148 (.getState
149 (.getStateManager world)
150 BulletAppState))
151 element)
152 (.attachChild node element))
153 ([world element]
154 (add-element world element (.getRootNode world))))
156 (defn apply-map
157 "Like apply, but works for maps and functions that expect an
158 implicit map and nothing else as in (fn [& {}]).
159 ------- Example -------
160 (defn demo [& {:keys [www] :or {www \"oh yeah\"} :as env}]
161 (println www))
162 (apply-map demo {:www \"hello!\"})
163 -->\"hello\""
164 [fn m]
165 (apply fn (reduce #(into %1 %2) [] m)))
167 #+end_src
170 *** Creating Basic Shapes
172 #+srcname: shapes
173 #+begin_src clojure :results silent
174 (in-ns 'cortex.util)
176 (defrecord shape-description
177 [name
178 color
179 mass
180 friction
181 texture
182 material
183 position
184 rotation
185 shape
186 physical?
187 GImpact?
188 ])
190 (defvar base-shape
191 (shape-description.
192 "default-shape"
193 false
194 ;;ColorRGBA/Blue
195 1.0 ;; mass
196 1.0 ;; friction
197 ;; texture
198 "Textures/Terrain/BrickWall/BrickWall.jpg"
199 ;; material
200 "Common/MatDefs/Misc/Unshaded.j3md"
201 Vector3f/ZERO
202 Quaternion/IDENTITY
203 (Box. Vector3f/ZERO 0.5 0.5 0.5)
204 true
205 false)
206 "Basic settings for shapes.")
208 (defn make-shape
209 [#^shape-description d]
210 (let [asset-manager (asset-manager)
211 mat (Material. asset-manager (:material d))
212 geom (Geometry. (:name d) (:shape d))]
213 (if (:texture d)
214 (let [key (TextureKey. (:texture d))]
215 ;;(.setGenerateMips key true)
216 ;;(.setTexture mat "ColorMap" (.loadTexture asset-manager key))
217 ))
218 (if (:color d) (.setColor mat "Color" (:color d)))
219 (.setMaterial geom mat)
220 (if-let [rotation (:rotation d)] (.rotate geom rotation))
221 (.setLocalTranslation geom (:position d))
222 (if (:physical? d)
223 (let [physics-control
224 (if (:GImpact d)
225 ;; Create an accurate mesh collision shape if desired.
226 (RigidBodyControl.
227 (doto (GImpactCollisionShape.
228 (.getMesh geom))
229 (.createJmeMesh)
230 (.setMargin 0))
231 (float (:mass d)))
232 ;; otherwise use jme3's default
233 (RigidBodyControl. (float (:mass d))))]
234 (.addControl geom physics-control)
235 ;;(.setSleepingThresholds physics-control (float 0) (float 0))
236 (.setFriction physics-control (:friction d))))
237 geom))
239 (defn box
240 ([l w h & {:as options}]
241 (let [options (merge base-shape options)]
242 (make-shape (assoc options
243 :shape (Box. l w h)))))
244 ([] (box 0.5 0.5 0.5)))
246 (defn sphere
247 ([r & {:as options}]
248 (let [options (merge base-shape options)]
249 (make-shape (assoc options
250 :shape (Sphere. 32 32 (float r))))))
251 ([] (sphere 0.5)))
252 #+end_src
254 *** Viewing Objects
256 #+srcname: world-view
257 #+begin_src clojure :results silent
258 (in-ns 'cortex.util)
260 (defprotocol Viewable
261 (view [something]))
263 (extend-type com.jme3.scene.Geometry
264 Viewable
265 (view [geo]
266 (view (doto (Node.)(.attachChild geo)))))
268 (extend-type com.jme3.scene.Node
269 Viewable
270 (view
271 [node]
272 (.start
273 (world
274 node
275 {}
276 (fn [world]
277 (enable-debug world)
278 (set-gravity world Vector3f/ZERO)
279 (let [sun
280 (doto (DirectionalLight.)
281 (.setDirection
282 (.normalizeLocal (Vector3f. 1 0 -2)))
283 (.setColor ColorRGBA/White))]
284 ;; lights are required to view some objects.
285 (.addLight (.getRootNode world) sun)))
286 no-op))))
287 #+end_src
289 Here I make the =Viewable= protocol and extend it to JME's types. Now
290 JME3's =hello-world= can be written as easily as:
292 #+begin_src clojure :results silent
293 (cortex.util/view (cortex.util/box))
294 #+end_src
297 * COMMENT code generation
298 #+begin_src clojure :tangle ../src/cortex/import.clj
299 <<import>>
300 #+end_src
303 #+begin_src clojure :tangle ../src/cortex/util.clj :noweb yes
304 <<util>>
305 <<shapes>>
306 <<world-view>>
307 #+end_src