annotate org/util.org @ 487:19b55aaf4462

complete first draft of user guide.
author Robert McIntyre <rlm@mit.edu>
date Sat, 29 Mar 2014 20:17:58 -0400
parents 763d13f77e03
children 01934317b25b
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@34 9 [TABLE-OF-CONTENTS]
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@34 14 * Imports
rlm@28 15
rlm@66 16 #+name: import
rlm@23 17 #+begin_src clojure :results silent
rlm@23 18 (ns cortex.import
rlm@388 19 (:import java.io.File java.util.jar.JarFile))
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@306 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@388 35 (defn jme-jars []
rlm@388 36 (map
rlm@388 37 #(JarFile. (File. %))
rlm@388 38 (filter (partial re-matches #".*jME3.*")
rlm@388 39 (clojure.string/split
rlm@388 40 (System/getProperty "java.class.path") #":"))))
rlm@388 41
rlm@388 42 (defn jme-class-names []
rlm@23 43 (filter
rlm@23 44 jme-class?
rlm@388 45 (map
rlm@388 46 (comp
rlm@388 47 #(.replace % File/separator ".")
rlm@388 48 #(clojure.string/replace % ".class" ""))
rlm@388 49 (filter
rlm@388 50 (partial re-matches #".*\.class$")
rlm@388 51 (mapcat
rlm@388 52 #(map
rlm@388 53 str
rlm@388 54 (enumeration-seq
rlm@388 55 (.entries %)))
rlm@388 56 (jme-jars))))))
rlm@388 57
rlm@23 58 (defn mega-import-jme3
rlm@23 59 "Import ALL the jme classes. For REPL use."
rlm@23 60 []
rlm@388 61 (dorun
rlm@459 62 (import com.aurellem.capture.IsoTimer)
rlm@388 63 (map (comp permissive-import symbol) (jme-class-names))))
rlm@23 64 #+end_src
rlm@23 65
rlm@29 66 jMonkeyEngine3 has a plethora of classes which can be overwhelming to
rlm@29 67 manage. This code uses reflection to import all of them. Once I'm
rlm@29 68 happy with the general structure of a namespace I can deal with
rlm@29 69 importing only the classes it actually needs.
rlm@29 70
rlm@306 71 The =mega-import-jme3= is quite useful for debugging purposes since
rlm@34 72 it allows completion for almost all of JME's classes from the REPL.
rlm@23 73
rlm@306 74 Out of curiosity, let's see just how many classes =mega-import-jme3=
rlm@23 75 imports:
rlm@23 76
rlm@29 77 #+begin_src clojure :exports both :results output
rlm@455 78 (println (clojure.core/count (cortex.import/jme-class-names)) "classes")
rlm@23 79 #+end_src
rlm@23 80
rlm@23 81 #+results:
rlm@455 82 : 938 classes
rlm@23 83
rlm@25 84
rlm@34 85 * Utilities
rlm@23 86
rlm@29 87 The utilities here come in three main groups:
rlm@29 88 - Changing settings in a running =Application=
rlm@29 89 - Creating objects
rlm@50 90 - Debug Actions
rlm@29 91 - Visualizing objects
rlm@23 92
rlm@29 93 *** Changing Settings
rlm@24 94
rlm@66 95 #+name: util
rlm@25 96 #+begin_src clojure
rlm@29 97 (ns cortex.util
rlm@34 98 "Utility functions for making jMonkeyEngine3 easier to program from
rlm@34 99 clojure."
rlm@29 100 {:author "Robert McIntyre"}
rlm@29 101 (:use cortex.world)
rlm@29 102 (:import com.jme3.math.Vector3f)
rlm@29 103 (:import com.jme3.math.Quaternion)
rlm@29 104 (:import com.jme3.asset.TextureKey)
rlm@29 105 (:import com.jme3.bullet.control.RigidBodyControl)
rlm@29 106 (:import com.jme3.bullet.collision.shapes.GImpactCollisionShape)
rlm@29 107 (:import com.jme3.scene.shape.Box)
rlm@29 108 (:import com.jme3.scene.Node)
rlm@29 109 (:import com.jme3.scene.shape.Sphere)
rlm@47 110 (:import com.jme3.light.AmbientLight)
rlm@29 111 (:import com.jme3.light.DirectionalLight)
rlm@114 112 (:import (com.jme3.math Triangle ColorRGBA))
rlm@29 113 (:import com.jme3.bullet.BulletAppState)
rlm@29 114 (:import com.jme3.material.Material)
rlm@40 115 (:import com.jme3.scene.Geometry)
rlm@114 116 (:import java.awt.image.BufferedImage)
rlm@114 117 (:import javax.swing.JPanel)
rlm@114 118 (:import javax.swing.JFrame)
rlm@335 119 (:import ij.ImagePlus)
rlm@114 120 (:import javax.swing.SwingUtilities)
rlm@192 121 (:import com.jme3.scene.plugins.blender.BlenderModelLoader)
rlm@40 122 (:import (java.util.logging Level Logger)))
rlm@40 123
rlm@320 124 (def println-repl
rlm@29 125 "println called from the LWJGL thread will not go to the REPL, but
rlm@114 126 instead to whatever terminal started the JVM process. This function
rlm@320 127 will always output to the REPL"
rlm@320 128 (bound-fn [& args] (apply println args)))
rlm@25 129
rlm@29 130 (defn position-camera
rlm@34 131 "Change the position of the in-world camera."
rlm@400 132 ([world #^Vector3f position #^Quaternion rotation]
rlm@34 133 (doto (.getCamera world)
rlm@297 134 (.setLocation position)
rlm@297 135 (.setRotation rotation)))
rlm@400 136 ([world [position rotation]]
rlm@400 137 (position-camera world position rotation)))
rlm@400 138
rlm@25 139
rlm@29 140 (defn enable-debug
rlm@34 141 "Turn on debug wireframes for every object in this simulation."
rlm@29 142 [world]
rlm@29 143 (.enableDebug
rlm@29 144 (.getPhysicsSpace
rlm@29 145 (.getState
rlm@29 146 (.getStateManager world)
rlm@29 147 BulletAppState))
rlm@29 148 (asset-manager)))
rlm@29 149
rlm@78 150 (defn speed-up
rlm@78 151 "Increase the dismally slow speed of the world's camera."
rlm@451 152 ([world] (speed-up world 1))
rlm@451 153 ([world amount]
rlm@451 154 (.setMoveSpeed (.getFlyByCamera world)
rlm@451 155 (float (* amount 60)))
rlm@451 156 (.setRotationSpeed (.getFlyByCamera world)
rlm@451 157 (float (* amount 3)))
rlm@451 158 world))
rlm@78 159
rlm@40 160 (defn no-logging
rlm@40 161 "Disable all of jMonkeyEngine's logging."
rlm@40 162 []
rlm@40 163 (.setLevel (Logger/getLogger "com.jme3") Level/OFF))
rlm@40 164
rlm@40 165 (defn set-accuracy
rlm@40 166 "Change the accuracy at which the World's Physics is calculated."
rlm@40 167 [world new-accuracy]
rlm@40 168 (let [physics-manager
rlm@40 169 (.getState
rlm@40 170 (.getStateManager world) BulletAppState)]
rlm@40 171 (.setAccuracy
rlm@40 172 (.getPhysicsSpace physics-manager)
rlm@40 173 (float new-accuracy))))
rlm@40 174
rlm@40 175
rlm@34 176 (defn set-gravity
rlm@29 177 "In order to change the gravity of a scene, it is not only necessary
rlm@29 178 to set the gravity variable, but to \"tap\" every physics object in
rlm@29 179 the scene to reactivate physics calculations."
rlm@34 180 [world gravity]
rlm@25 181 (traverse
rlm@25 182 (fn [geom]
rlm@25 183 (if-let
rlm@29 184 ;; only set gravity for physical objects.
rlm@25 185 [control (.getControl geom RigidBodyControl)]
rlm@25 186 (do
rlm@25 187 (.setGravity control gravity)
rlm@29 188 ;; tappsies!
rlm@29 189 (.applyImpulse control Vector3f/ZERO Vector3f/ZERO))))
rlm@34 190 (.getRootNode world)))
rlm@29 191
rlm@29 192 (defn add-element
rlm@34 193 "Add the Spatial to the world's environment"
rlm@34 194 ([world element node]
rlm@29 195 (.addAll
rlm@29 196 (.getPhysicsSpace
rlm@29 197 (.getState
rlm@34 198 (.getStateManager world)
rlm@29 199 BulletAppState))
rlm@29 200 element)
rlm@29 201 (.attachChild node element))
rlm@34 202 ([world element]
rlm@34 203 (add-element world element (.getRootNode world))))
rlm@29 204
rlm@29 205 (defn apply-map
rlm@29 206 "Like apply, but works for maps and functions that expect an
rlm@29 207 implicit map and nothing else as in (fn [& {}]).
rlm@29 208 ------- Example -------
rlm@29 209 (defn demo [& {:keys [www] :or {www \"oh yeah\"} :as env}]
rlm@29 210 (println www))
rlm@29 211 (apply-map demo {:www \"hello!\"})
rlm@29 212 -->\"hello\""
rlm@29 213 [fn m]
rlm@29 214 (apply fn (reduce #(into %1 %2) [] m)))
rlm@29 215
rlm@151 216 (defn map-vals
rlm@151 217 "Transform a map by applying a function to its values,
rlm@151 218 keeping the keys the same."
rlm@151 219 [f m] (zipmap (keys m) (map f (vals m))))
rlm@151 220
rlm@164 221 (defn runonce
rlm@164 222 "Decorator. returns a function which will run only once.
rlm@164 223 Inspired by Halloway's version from Lancet."
rlm@164 224 {:author "Robert McIntyre"}
rlm@164 225 [function]
rlm@164 226 (let [sentinel (Object.)
rlm@164 227 result (atom sentinel)]
rlm@164 228 (fn [& args]
rlm@164 229 (locking sentinel
rlm@164 230 (if (= @result sentinel)
rlm@164 231 (reset! result (apply function args))
rlm@164 232 @result)))))
rlm@164 233
rlm@151 234
rlm@25 235 #+end_src
rlm@25 236
rlm@47 237 #+results: util
rlm@297 238 : #'cortex.util/runonce
rlm@73 239
rlm@25 240
rlm@29 241 *** Creating Basic Shapes
rlm@29 242
rlm@66 243 #+name: shapes
rlm@25 244 #+begin_src clojure :results silent
rlm@25 245 (in-ns 'cortex.util)
rlm@29 246
rlm@153 247 (defn load-bullet
rlm@306 248 "Running this function unpacks the native bullet libraries and makes
rlm@153 249 them available."
rlm@153 250 []
rlm@153 251 (let [sim (world (Node.) {} no-op no-op)]
rlm@153 252 (doto sim
rlm@153 253 (.enqueue
rlm@153 254 (fn []
rlm@153 255 (.stop sim)))
rlm@153 256 (.start))))
rlm@153 257
rlm@153 258
rlm@25 259 (defrecord shape-description
rlm@25 260 [name
rlm@25 261 color
rlm@25 262 mass
rlm@25 263 friction
rlm@25 264 texture
rlm@25 265 material
rlm@25 266 position
rlm@25 267 rotation
rlm@25 268 shape
rlm@29 269 physical?
rlm@29 270 GImpact?
rlm@29 271 ])
rlm@25 272
rlm@320 273 (def base-shape
rlm@320 274 "Basic settings for shapes."
rlm@320 275 (shape-description.
rlm@320 276 "default-shape"
rlm@320 277 false
rlm@320 278 ;;ColorRGBA/Blue
rlm@320 279 1.0 ;; mass
rlm@320 280 1.0 ;; friction
rlm@320 281 ;; texture
rlm@320 282 "Textures/Terrain/BrickWall/BrickWall.jpg"
rlm@320 283 ;; material
rlm@320 284 "Common/MatDefs/Misc/Unshaded.j3md"
rlm@320 285 Vector3f/ZERO
rlm@320 286 Quaternion/IDENTITY
rlm@320 287 (Box. Vector3f/ZERO 0.5 0.5 0.5)
rlm@320 288 true
rlm@320 289 false))
rlm@25 290
rlm@25 291 (defn make-shape
rlm@25 292 [#^shape-description d]
rlm@29 293 (let [asset-manager (asset-manager)
rlm@25 294 mat (Material. asset-manager (:material d))
rlm@25 295 geom (Geometry. (:name d) (:shape d))]
rlm@25 296 (if (:texture d)
rlm@25 297 (let [key (TextureKey. (:texture d))]
rlm@363 298 (.setGenerateMips key true)
rlm@363 299 (.setTexture mat "ColorMap" (.loadTexture asset-manager key))
rlm@34 300 ))
rlm@25 301 (if (:color d) (.setColor mat "Color" (:color d)))
rlm@25 302 (.setMaterial geom mat)
rlm@25 303 (if-let [rotation (:rotation d)] (.rotate geom rotation))
rlm@25 304 (.setLocalTranslation geom (:position d))
rlm@25 305 (if (:physical? d)
rlm@29 306 (let [physics-control
rlm@29 307 (if (:GImpact d)
rlm@29 308 ;; Create an accurate mesh collision shape if desired.
rlm@29 309 (RigidBodyControl.
rlm@29 310 (doto (GImpactCollisionShape.
rlm@29 311 (.getMesh geom))
rlm@29 312 (.createJmeMesh)
rlm@73 313 ;;(.setMargin 0)
rlm@73 314 )
rlm@29 315 (float (:mass d)))
rlm@29 316 ;; otherwise use jme3's default
rlm@29 317 (RigidBodyControl. (float (:mass d))))]
rlm@25 318 (.addControl geom physics-control)
rlm@25 319 ;;(.setSleepingThresholds physics-control (float 0) (float 0))
rlm@25 320 (.setFriction physics-control (:friction d))))
rlm@25 321 geom))
rlm@25 322
rlm@25 323 (defn box
rlm@25 324 ([l w h & {:as options}]
rlm@25 325 (let [options (merge base-shape options)]
rlm@25 326 (make-shape (assoc options
rlm@25 327 :shape (Box. l w h)))))
rlm@25 328 ([] (box 0.5 0.5 0.5)))
rlm@25 329
rlm@25 330 (defn sphere
rlm@25 331 ([r & {:as options}]
rlm@25 332 (let [options (merge base-shape options)]
rlm@25 333 (make-shape (assoc options
rlm@25 334 :shape (Sphere. 32 32 (float r))))))
rlm@25 335 ([] (sphere 0.5)))
rlm@62 336
rlm@192 337 (defn x-ray
rlm@306 338 "A useful material for debugging -- it can be seen no matter what
rlm@306 339 object occludes it."
rlm@192 340 [#^ColorRGBA color]
rlm@192 341 (doto (Material. (asset-manager)
rlm@192 342 "Common/MatDefs/Misc/Unshaded.j3md")
rlm@192 343 (.setColor "Color" color)
rlm@192 344 (-> (.getAdditionalRenderState)
rlm@192 345 (.setDepthTest false))))
rlm@74 346
rlm@62 347 (defn node-seq
rlm@62 348 "Take a node and return a seq of all its children
rlm@62 349 recursively. There will be no nodes left in the resulting
rlm@62 350 structure"
rlm@62 351 [#^Node node]
rlm@62 352 (tree-seq #(isa? (class %) Node) #(.getChildren %) node))
rlm@62 353
rlm@62 354 (defn nodify
rlm@62 355 "Take a sequence of things that can be attached to a node and return
rlm@62 356 a node with all of them attached"
rlm@62 357 ([name children]
rlm@62 358 (let [node (Node. name)]
rlm@62 359 (dorun (map #(.attachChild node %) children))
rlm@62 360 node))
rlm@62 361 ([children] (nodify "" children)))
rlm@62 362
rlm@192 363 (defn load-blender-model
rlm@192 364 "Load a .blend file using an asset folder relative path."
rlm@192 365 [^String model]
rlm@192 366 (.loadModel
rlm@192 367 (doto (asset-manager)
rlm@192 368 (.registerLoader BlenderModelLoader
rlm@192 369 (into-array String ["blend"]))) model))
rlm@192 370
rlm@62 371
rlm@458 372
rlm@458 373 (def brick-length 0.48)
rlm@458 374 (def brick-width 0.24)
rlm@458 375 (def brick-height 0.12)
rlm@458 376 (def gravity (Vector3f. 0 -9.81 0))
rlm@458 377
rlm@458 378 (import com.jme3.math.Vector2f)
rlm@458 379 (import com.jme3.renderer.queue.RenderQueue$ShadowMode)
rlm@458 380 (import com.jme3.texture.Texture$WrapMode)
rlm@458 381
rlm@458 382 (defn brick* [position]
rlm@458 383 (println "get brick.")
rlm@458 384 (doto (box brick-length brick-height brick-width
rlm@458 385 :position position :name "brick"
rlm@458 386 :material "Common/MatDefs/Misc/Unshaded.j3md"
rlm@458 387 :texture "Textures/Terrain/BrickWall/BrickWall.jpg"
rlm@458 388 :mass 34)
rlm@458 389 (->
rlm@458 390 (.getMesh)
rlm@458 391 (.scaleTextureCoordinates (Vector2f. 1 0.5)))
rlm@458 392 (.setShadowMode RenderQueue$ShadowMode/CastAndReceive)
rlm@458 393 )
rlm@458 394 )
rlm@458 395
rlm@458 396
rlm@458 397 (defn floor*
rlm@458 398 "make a sturdy, unmovable physical floor"
rlm@458 399 []
rlm@458 400 (box 10 0.1 5 :name "floor" :mass 0
rlm@458 401 :color ColorRGBA/Gray :position (Vector3f. 0 0 0)))
rlm@458 402
rlm@458 403 (defn floor* []
rlm@458 404 (doto (box 10 0.1 5 :name "floor" ;10 0.1 5 ; 240 0.1 240
rlm@458 405 :material "Common/MatDefs/Misc/Unshaded.j3md"
rlm@458 406 :texture "Textures/BronzeCopper030.jpg"
rlm@458 407 :position (Vector3f. 0 0 0 )
rlm@458 408 :mass 0)
rlm@458 409 (->
rlm@458 410 (.getMesh)
rlm@458 411 (.scaleTextureCoordinates (Vector2f. 3 6)));64 64
rlm@458 412 (->
rlm@458 413 (.getMaterial)
rlm@458 414 (.getTextureParam "ColorMap")
rlm@458 415 (.getTextureValue)
rlm@458 416 (.setWrap Texture$WrapMode/Repeat))
rlm@458 417 (.setShadowMode RenderQueue$ShadowMode/Receive)
rlm@458 418 ))
rlm@458 419
rlm@458 420
rlm@458 421 (defn brick-wall* []
rlm@458 422 (let [node (Node. "brick-wall")]
rlm@458 423 (dorun
rlm@458 424 (map
rlm@458 425 (comp #(.attachChild node %) brick*)
rlm@458 426 (for [y (range 10)
rlm@458 427 x (range 4)
rlm@458 428 z (range 1)]
rlm@458 429 (Vector3f.
rlm@458 430 (+ (* 2 x brick-length)
rlm@458 431 (if (even? (+ y z))
rlm@458 432 (/ brick-length 4) (/ brick-length -4)))
rlm@458 433 (+ (* brick-height (inc (* 2 y))))
rlm@458 434 (* 2 z brick-width) ))))
rlm@458 435 (.setShadowMode node RenderQueue$ShadowMode/CastAndReceive)
rlm@458 436 node))
rlm@458 437
rlm@458 438
rlm@29 439 #+end_src
rlm@25 440
rlm@25 441
rlm@50 442 *** Debug Actions
rlm@66 443 #+name: debug-actions
rlm@29 444 #+begin_src clojure :results silent
rlm@60 445 (in-ns 'cortex.util)
rlm@60 446
rlm@47 447 (defn basic-light-setup
rlm@306 448 "returns a sequence of lights appropriate for fully lighting a scene"
rlm@47 449 []
rlm@47 450 (conj
rlm@47 451 (doall
rlm@47 452 (map
rlm@47 453 (fn [direction]
rlm@47 454 (doto (DirectionalLight.)
rlm@47 455 (.setDirection direction)
rlm@47 456 (.setColor ColorRGBA/White)))
rlm@47 457 [;; six faces of a cube
rlm@47 458 Vector3f/UNIT_X
rlm@47 459 Vector3f/UNIT_Y
rlm@47 460 Vector3f/UNIT_Z
rlm@47 461 (.mult Vector3f/UNIT_X (float -1))
rlm@47 462 (.mult Vector3f/UNIT_Y (float -1))
rlm@47 463 (.mult Vector3f/UNIT_Z (float -1))]))
rlm@47 464 (doto (AmbientLight.)
rlm@47 465 (.setColor ColorRGBA/White))))
rlm@47 466
rlm@47 467 (defn light-up-everything
rlm@306 468 "Add lights to a world appropriate for quickly seeing everything
rlm@47 469 in the scene. Adds six DirectionalLights facing in orthogonal
rlm@47 470 directions, and one AmbientLight to provide overall lighting
rlm@47 471 coverage."
rlm@47 472 [world]
rlm@47 473 (dorun
rlm@47 474 (map
rlm@47 475 #(.addLight (.getRootNode world) %)
rlm@47 476 (basic-light-setup))))
rlm@50 477
rlm@50 478 (defn fire-cannon-ball
rlm@50 479 "Creates a function that fires a cannon-ball from the current game's
rlm@50 480 camera. The cannon-ball will be attached to the node if provided, or
rlm@50 481 to the game's RootNode if no node is provided."
rlm@50 482 ([node]
rlm@50 483 (fn [game value]
rlm@50 484 (if (not value)
rlm@50 485 (let [camera (.getCamera game)
rlm@50 486 cannon-ball
rlm@363 487 (sphere 0.4
rlm@363 488 ;;:texture nil
rlm@50 489 :material "Common/MatDefs/Misc/Unshaded.j3md"
rlm@363 490 :color ColorRGBA/Blue
rlm@216 491 :name "cannonball!"
rlm@50 492 :position
rlm@50 493 (.add (.getLocation camera)
rlm@50 494 (.mult (.getDirection camera) (float 1)))
rlm@363 495 :mass 25)] ;200 0.05
rlm@50 496 (.setLinearVelocity
rlm@50 497 (.getControl cannon-ball RigidBodyControl)
rlm@50 498 (.mult (.getDirection camera) (float 50))) ;50
rlm@216 499 (add-element game cannon-ball (if node node (.getRootNode
rlm@216 500 game)))
rlm@216 501 cannon-ball))))
rlm@50 502 ([]
rlm@50 503 (fire-cannon-ball false)))
rlm@60 504
rlm@60 505 (def standard-debug-controls
rlm@60 506 {"key-space" (fire-cannon-ball)})
rlm@60 507
rlm@60 508
rlm@160 509 (defn tap [obj direction force]
rlm@160 510 (let [control (.getControl obj RigidBodyControl)]
rlm@160 511 (.applyTorque
rlm@160 512 control
rlm@160 513 (.mult (.getPhysicsRotation control)
rlm@160 514 (.mult (.normalize direction) (float force))))))
rlm@160 515
rlm@160 516
rlm@160 517 (defn with-movement
rlm@160 518 [object
rlm@160 519 [up down left right roll-up roll-down :as keyboard]
rlm@160 520 forces
rlm@160 521 [root-node
rlm@160 522 keymap
rlm@306 523 initialization
rlm@160 524 world-loop]]
rlm@160 525 (let [add-keypress
rlm@160 526 (fn [state keymap key]
rlm@160 527 (merge keymap
rlm@160 528 {key
rlm@160 529 (fn [_ pressed?]
rlm@160 530 (reset! state pressed?))}))
rlm@160 531 move-up? (atom false)
rlm@160 532 move-down? (atom false)
rlm@160 533 move-left? (atom false)
rlm@160 534 move-right? (atom false)
rlm@160 535 roll-left? (atom false)
rlm@160 536 roll-right? (atom false)
rlm@160 537
rlm@160 538 directions [(Vector3f. 0 1 0)(Vector3f. 0 -1 0)
rlm@160 539 (Vector3f. 0 0 1)(Vector3f. 0 0 -1)
rlm@160 540 (Vector3f. -1 0 0)(Vector3f. 1 0 0)]
rlm@160 541 atoms [move-left? move-right? move-up? move-down?
rlm@160 542 roll-left? roll-right?]
rlm@160 543
rlm@160 544 keymap* (reduce merge
rlm@160 545 (map #(add-keypress %1 keymap %2)
rlm@160 546 atoms
rlm@160 547 keyboard))
rlm@160 548
rlm@160 549 splice-loop (fn []
rlm@160 550 (dorun
rlm@160 551 (map
rlm@160 552 (fn [sym direction force]
rlm@160 553 (if @sym
rlm@160 554 (tap object direction force)))
rlm@160 555 atoms directions forces)))
rlm@160 556
rlm@160 557 world-loop* (fn [world tpf]
rlm@160 558 (world-loop world tpf)
rlm@160 559 (splice-loop))]
rlm@160 560 [root-node
rlm@160 561 keymap*
rlm@306 562 initialization
rlm@160 563 world-loop*]))
rlm@160 564
rlm@205 565 (import com.jme3.font.BitmapText)
rlm@205 566 (import com.jme3.scene.control.AbstractControl)
rlm@205 567 (import com.aurellem.capture.IsoTimer)
rlm@60 568
rlm@306 569 (defn display-dilated-time
rlm@205 570 "Shows the time as it is flowing in the simulation on a HUD display.
rlm@205 571 Useful for making videos."
rlm@205 572 [world timer]
rlm@205 573 (let [font (.loadFont (asset-manager) "Interface/Fonts/Default.fnt")
rlm@205 574 text (BitmapText. font false)]
rlm@205 575 (.setLocalTranslation text 300 (.getLineHeight text) 0)
rlm@205 576 (.addControl
rlm@205 577 text
rlm@205 578 (proxy [AbstractControl] []
rlm@205 579 (controlUpdate [tpf]
rlm@205 580 (.setText text (format
rlm@205 581 "%.2f"
rlm@341 582 (float (.getTimeInSeconds timer)))))
rlm@205 583 (controlRender [_ _])))
rlm@205 584 (.attachChild (.getGuiNode world) text)))
rlm@50 585 #+end_src
rlm@50 586
rlm@50 587
rlm@50 588 *** Viewing Objects
rlm@50 589
rlm@66 590 #+name: world-view
rlm@50 591 #+begin_src clojure :results silent
rlm@50 592 (in-ns 'cortex.util)
rlm@50 593
rlm@50 594 (defprotocol Viewable
rlm@50 595 (view [something]))
rlm@50 596
rlm@50 597 (extend-type com.jme3.scene.Geometry
rlm@50 598 Viewable
rlm@50 599 (view [geo]
rlm@50 600 (view (doto (Node.)(.attachChild geo)))))
rlm@47 601
rlm@29 602 (extend-type com.jme3.scene.Node
rlm@29 603 Viewable
rlm@29 604 (view
rlm@29 605 [node]
rlm@29 606 (.start
rlm@29 607 (world
rlm@29 608 node
rlm@29 609 {}
rlm@29 610 (fn [world]
rlm@29 611 (enable-debug world)
rlm@29 612 (set-gravity world Vector3f/ZERO)
rlm@47 613 (light-up-everything world))
rlm@29 614 no-op))))
rlm@62 615
rlm@62 616 (extend-type com.jme3.math.ColorRGBA
rlm@62 617 Viewable
rlm@62 618 (view
rlm@62 619 [color]
rlm@62 620 (view (doto (Node.)
rlm@62 621 (.attachChild (box 1 1 1 :color color))))))
rlm@62 622
rlm@336 623 (extend-type ij.ImagePlus
rlm@336 624 Viewable
rlm@336 625 (view [image]
rlm@336 626 (.show image)))
rlm@336 627
rlm@335 628 (extend-type java.awt.image.BufferedImage
rlm@335 629 Viewable
rlm@335 630 (view
rlm@335 631 [image]
rlm@336 632 (view (ImagePlus. "view-buffered-image" image))))
rlm@336 633
rlm@336 634
rlm@62 635 (defprotocol Textual
rlm@62 636 (text [something]
rlm@62 637 "Display a detailed textual analysis of the given object."))
rlm@62 638
rlm@62 639 (extend-type com.jme3.scene.Node
rlm@62 640 Textual
rlm@62 641 (text [node]
rlm@62 642 (println "Total Vertexes: " (.getVertexCount node))
rlm@62 643 (println "Total Triangles: " (.getTriangleCount node))
rlm@62 644 (println "Controls :")
rlm@62 645 (dorun (map #(text (.getControl node %)) (range (.getNumControls node))))
rlm@62 646 (println "Has " (.getQuantity node) " Children:")
rlm@62 647 (doall (map text (.getChildren node)))))
rlm@62 648
rlm@62 649 (extend-type com.jme3.animation.AnimControl
rlm@62 650 Textual
rlm@62 651 (text [control]
rlm@62 652 (let [animations (.getAnimationNames control)]
rlm@62 653 (println "Animation Control with " (count animations) " animation(s):")
rlm@62 654 (dorun (map println animations)))))
rlm@62 655
rlm@62 656 (extend-type com.jme3.animation.SkeletonControl
rlm@62 657 Textual
rlm@62 658 (text [control]
rlm@62 659 (println "Skeleton Control with the following skeleton:")
rlm@62 660 (println (.getSkeleton control))))
rlm@62 661
rlm@62 662 (extend-type com.jme3.bullet.control.KinematicRagdollControl
rlm@62 663 Textual
rlm@62 664 (text [control]
rlm@62 665 (println "Ragdoll Control")))
rlm@62 666
rlm@62 667 (extend-type com.jme3.scene.Geometry
rlm@62 668 Textual
rlm@62 669 (text [control]
rlm@62 670 (println "...geo...")))
rlm@108 671
rlm@108 672 (extend-type Triangle
rlm@108 673 Textual
rlm@108 674 (text [t]
rlm@108 675 (println "Triangle: " \newline (.get1 t) \newline
rlm@108 676 (.get2 t) \newline (.get3 t))))
rlm@108 677
rlm@25 678 #+end_src
rlm@25 679
rlm@29 680 Here I make the =Viewable= protocol and extend it to JME's types. Now
rlm@34 681 JME3's =hello-world= can be written as easily as:
rlm@29 682
rlm@29 683 #+begin_src clojure :results silent
rlm@29 684 (cortex.util/view (cortex.util/box))
rlm@29 685 #+end_src
rlm@29 686
rlm@29 687
rlm@400 688 * code generation
rlm@24 689 #+begin_src clojure :tangle ../src/cortex/import.clj
rlm@24 690 <<import>>
rlm@24 691 #+end_src
rlm@25 692
rlm@25 693
rlm@29 694 #+begin_src clojure :tangle ../src/cortex/util.clj :noweb yes
rlm@25 695 <<util>>
rlm@25 696 <<shapes>>
rlm@50 697 <<debug-actions>>
rlm@29 698 <<world-view>>
rlm@25 699 #+end_src
rlm@25 700
rlm@29 701
rlm@29 702
rlm@29 703
rlm@29 704
rlm@29 705
rlm@29 706
rlm@29 707
rlm@29 708
rlm@29 709
rlm@29 710
rlm@29 711
rlm@29 712
rlm@29 713
rlm@29 714
rlm@29 715
rlm@29 716