view org/util.org @ 30:0206878c28b4

going to start on games.org
author Robert McIntyre <rlm@mit.edu>
date Mon, 24 Oct 2011 12:43:58 -0700
parents 6372c108c5c6
children 183744c179e6
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 * Utilities
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.
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 jMonkeyEngine 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 ([game position direction up]
109 (doto (.getCamera game)
110 (.setLocation )
111 (.lookAt direction up)))
112 ([game position direction]
113 (position-camera
114 game position direction Vector3f/UNIT_Y)))
116 (defn enable-debug
117 "Turn on the debug wireframes for every object in this simulation"
118 [world]
119 (.enableDebug
120 (.getPhysicsSpace
121 (.getState
122 (.getStateManager world)
123 BulletAppState))
124 (asset-manager)))
126 (defn set-gravity
127 "In order to change the gravity of a scene, it is not only necessary
128 to set the gravity variable, but to \"tap\" every physics object in
129 the scene to reactivate physics calculations."
130 [game gravity]
131 (traverse
132 (fn [geom]
133 (if-let
134 ;; only set gravity for physical objects.
135 [control (.getControl geom RigidBodyControl)]
136 (do
137 (.setGravity control gravity)
138 ;; tappsies!
139 (.applyImpulse control Vector3f/ZERO Vector3f/ZERO))))
140 (.getRootNode game)))
142 (defn add-element
143 "Add the Spatial to the game's environment"
144 ([game element node]
145 (.addAll
146 (.getPhysicsSpace
147 (.getState
148 (.getStateManager game)
149 BulletAppState))
150 element)
151 (.attachChild node element))
152 ([game element]
153 (add-element game element (.getRootNode game))))
155 (defn apply-map
156 "Like apply, but works for maps and functions that expect an
157 implicit map and nothing else as in (fn [& {}]).
158 ------- Example -------
159 (defn demo [& {:keys [www] :or {www \"oh yeah\"} :as env}]
160 (println www))
161 (apply-map demo {:www \"hello!\"})
162 -->\"hello\""
163 [fn m]
164 (apply fn (reduce #(into %1 %2) [] m)))
166 #+end_src
169 *** Creating Basic Shapes
171 #+srcname: shapes
172 #+begin_src clojure :results silent
173 (in-ns 'cortex.util)
175 (defrecord shape-description
176 [name
177 color
178 mass
179 friction
180 texture
181 material
182 position
183 rotation
184 shape
185 physical?
186 GImpact?
187 ])
189 (def base-shape
190 (shape-description.
191 "default-shape"
192 false
193 ;;ColorRGBA/Blue
194 1.0 ;; mass
195 1.0 ;; friction
196 ;; texture
197 "Textures/Terrain/BrickWall/BrickWall.jpg"
198 ;; material
199 "Common/MatDefs/Misc/Unshaded.j3md"
200 Vector3f/ZERO
201 Quaternion/IDENTITY
202 (Box. Vector3f/ZERO 0.5 0.5 0.5)
203 true
204 false))
206 (defn make-shape
207 [#^shape-description d]
208 (let [asset-manager (asset-manager)
209 mat (Material. asset-manager (:material d))
210 geom (Geometry. (:name d) (:shape d))]
211 (if (:texture d)
212 (let [key (TextureKey. (:texture d))]
213 (.setGenerateMips key true)
214 (.setTexture mat "ColorMap" (.loadTexture asset-manager key))))
215 (if (:color d) (.setColor mat "Color" (:color d)))
216 (.setMaterial geom mat)
217 (if-let [rotation (:rotation d)] (.rotate geom rotation))
218 (.setLocalTranslation geom (:position d))
219 (if (:physical? d)
220 (let [physics-control
221 (if (:GImpact d)
222 ;; Create an accurate mesh collision shape if desired.
223 (RigidBodyControl.
224 (doto (GImpactCollisionShape.
225 (.getMesh geom))
226 (.createJmeMesh)
227 (.setMargin 0))
228 (float (:mass d)))
229 ;; otherwise use jme3's default
230 (RigidBodyControl. (float (:mass d))))]
231 (.addControl geom physics-control)
232 ;;(.setSleepingThresholds physics-control (float 0) (float 0))
233 (.setFriction physics-control (:friction d))))
234 geom))
236 (defn box
237 ([l w h & {:as options}]
238 (let [options (merge base-shape options)]
239 (make-shape (assoc options
240 :shape (Box. l w h)))))
241 ([] (box 0.5 0.5 0.5)))
243 (defn sphere
244 ([r & {:as options}]
245 (let [options (merge base-shape options)]
246 (make-shape (assoc options
247 :shape (Sphere. 32 32 (float r))))))
248 ([] (sphere 0.5)))
249 #+end_src
252 *** Viewing Objects
254 #+srcname: world-view
255 #+begin_src clojure :results silent
256 (in-ns 'cortex.util)
258 (defprotocol Viewable
259 (view [something]))
261 (extend-type com.jme3.scene.Geometry
262 Viewable
263 (view [geo]
264 (view (doto (Node.)(.attachChild geo)))))
266 (extend-type com.jme3.scene.Node
267 Viewable
268 (view
269 [node]
270 (.start
271 (world
272 node
273 {}
274 (fn [world]
275 (enable-debug world)
276 (set-gravity world Vector3f/ZERO)
277 (let [sun
278 (doto (DirectionalLight.)
279 (.setDirection
280 (.normalizeLocal (Vector3f. 1 0 -2)))
281 (.setColor ColorRGBA/White))]
282 (.addLight (.getRootNode world) sun)))
283 no-op))))
284 #+end_src
286 Here I make the =Viewable= protocol and extend it to JME's types. Now
287 hello-world can be written as easily as:
289 #+begin_src clojure :results silent
290 (cortex.util/view (cortex.util/box))
291 #+end_src
294 * COMMENT code generation
295 #+begin_src clojure :tangle ../src/cortex/import.clj
296 <<import>>
297 #+end_src
300 #+begin_src clojure :tangle ../src/cortex/util.clj :noweb yes
301 <<util>>
302 <<shapes>>
303 <<world-view>>
304 #+end_src