view org/util.org @ 45:f080f1e49fba

fixed imports, moved fox
author Robert McIntyre <rlm@mit.edu>
date Tue, 08 Nov 2011 02:11:01 -0700
parents bc93abad23ee
children ee55966ce7f6
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)
100 (:import (java.util.logging Level Logger)))
104 (defvar println-repl
105 (bound-fn [& args] (apply println args))
106 "println called from the LWJGL thread will not go to the REPL, but
107 instead to whatever terminal started the JVM process. This function
108 will always output to the REPL")
110 (defn position-camera
111 "Change the position of the in-world camera."
112 ([world position direction up]
113 (doto (.getCamera world)
114 (.setLocation )
115 (.lookAt direction up)))
116 ([world position direction]
117 (position-camera
118 world position direction Vector3f/UNIT_Y)))
120 (defn enable-debug
121 "Turn on debug wireframes for every object in this simulation."
122 [world]
123 (.enableDebug
124 (.getPhysicsSpace
125 (.getState
126 (.getStateManager world)
127 BulletAppState))
128 (asset-manager)))
130 (defn no-logging
131 "Disable all of jMonkeyEngine's logging."
132 []
133 (.setLevel (Logger/getLogger "com.jme3") Level/OFF))
135 (defn set-accuracy
136 "Change the accuracy at which the World's Physics is calculated."
137 [world new-accuracy]
138 (let [physics-manager
139 (.getState
140 (.getStateManager world) BulletAppState)]
141 (.setAccuracy
142 (.getPhysicsSpace physics-manager)
143 (float new-accuracy))))
146 (defn set-gravity
147 "In order to change the gravity of a scene, it is not only necessary
148 to set the gravity variable, but to \"tap\" every physics object in
149 the scene to reactivate physics calculations."
150 [world gravity]
151 (traverse
152 (fn [geom]
153 (if-let
154 ;; only set gravity for physical objects.
155 [control (.getControl geom RigidBodyControl)]
156 (do
157 (.setGravity control gravity)
158 ;; tappsies!
159 (.applyImpulse control Vector3f/ZERO Vector3f/ZERO))))
160 (.getRootNode world)))
162 (defn add-element
163 "Add the Spatial to the world's environment"
164 ([world element node]
165 (.addAll
166 (.getPhysicsSpace
167 (.getState
168 (.getStateManager world)
169 BulletAppState))
170 element)
171 (.attachChild node element))
172 ([world element]
173 (add-element world element (.getRootNode world))))
175 (defn apply-map
176 "Like apply, but works for maps and functions that expect an
177 implicit map and nothing else as in (fn [& {}]).
178 ------- Example -------
179 (defn demo [& {:keys [www] :or {www \"oh yeah\"} :as env}]
180 (println www))
181 (apply-map demo {:www \"hello!\"})
182 -->\"hello\""
183 [fn m]
184 (apply fn (reduce #(into %1 %2) [] m)))
186 #+end_src
189 *** Creating Basic Shapes
191 #+srcname: shapes
192 #+begin_src clojure :results silent
193 (in-ns 'cortex.util)
195 (defrecord shape-description
196 [name
197 color
198 mass
199 friction
200 texture
201 material
202 position
203 rotation
204 shape
205 physical?
206 GImpact?
207 ])
209 (defvar base-shape
210 (shape-description.
211 "default-shape"
212 false
213 ;;ColorRGBA/Blue
214 1.0 ;; mass
215 1.0 ;; friction
216 ;; texture
217 "Textures/Terrain/BrickWall/BrickWall.jpg"
218 ;; material
219 "Common/MatDefs/Misc/Unshaded.j3md"
220 Vector3f/ZERO
221 Quaternion/IDENTITY
222 (Box. Vector3f/ZERO 0.5 0.5 0.5)
223 true
224 false)
225 "Basic settings for shapes.")
227 (defn make-shape
228 [#^shape-description d]
229 (let [asset-manager (asset-manager)
230 mat (Material. asset-manager (:material d))
231 geom (Geometry. (:name d) (:shape d))]
232 (if (:texture d)
233 (let [key (TextureKey. (:texture d))]
234 ;;(.setGenerateMips key true)
235 ;;(.setTexture mat "ColorMap" (.loadTexture asset-manager key))
236 ))
237 (if (:color d) (.setColor mat "Color" (:color d)))
238 (.setMaterial geom mat)
239 (if-let [rotation (:rotation d)] (.rotate geom rotation))
240 (.setLocalTranslation geom (:position d))
241 (if (:physical? d)
242 (let [physics-control
243 (if (:GImpact d)
244 ;; Create an accurate mesh collision shape if desired.
245 (RigidBodyControl.
246 (doto (GImpactCollisionShape.
247 (.getMesh geom))
248 (.createJmeMesh)
249 (.setMargin 0))
250 (float (:mass d)))
251 ;; otherwise use jme3's default
252 (RigidBodyControl. (float (:mass d))))]
253 (.addControl geom physics-control)
254 ;;(.setSleepingThresholds physics-control (float 0) (float 0))
255 (.setFriction physics-control (:friction d))))
256 geom))
258 (defn box
259 ([l w h & {:as options}]
260 (let [options (merge base-shape options)]
261 (make-shape (assoc options
262 :shape (Box. l w h)))))
263 ([] (box 0.5 0.5 0.5)))
265 (defn sphere
266 ([r & {:as options}]
267 (let [options (merge base-shape options)]
268 (make-shape (assoc options
269 :shape (Sphere. 32 32 (float r))))))
270 ([] (sphere 0.5)))
271 #+end_src
273 *** Viewing Objects
275 #+srcname: world-view
276 #+begin_src clojure :results silent
277 (in-ns 'cortex.util)
279 (defprotocol Viewable
280 (view [something]))
282 (extend-type com.jme3.scene.Geometry
283 Viewable
284 (view [geo]
285 (view (doto (Node.)(.attachChild geo)))))
287 (extend-type com.jme3.scene.Node
288 Viewable
289 (view
290 [node]
291 (.start
292 (world
293 node
294 {}
295 (fn [world]
296 (enable-debug world)
297 (set-gravity world Vector3f/ZERO)
298 (let [sun
299 (doto (DirectionalLight.)
300 (.setDirection
301 (.normalizeLocal (Vector3f. 1 0 -2)))
302 (.setColor ColorRGBA/White))]
303 ;; lights are required to view some objects.
304 (.addLight (.getRootNode world) sun)))
305 no-op))))
306 #+end_src
308 Here I make the =Viewable= protocol and extend it to JME's types. Now
309 JME3's =hello-world= can be written as easily as:
311 #+begin_src clojure :results silent
312 (cortex.util/view (cortex.util/box))
313 #+end_src
316 * COMMENT code generation
317 #+begin_src clojure :tangle ../src/cortex/import.clj
318 <<import>>
319 #+end_src
322 #+begin_src clojure :tangle ../src/cortex/util.clj :noweb yes
323 <<util>>
324 <<shapes>>
325 <<world-view>>
326 #+end_src