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