Mercurial > cortex
view org/util.org @ 69:39e4e1542e4a
updated test-suite
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 09 Dec 2011 23:11:28 -0600 |
parents | 1381a6ebd08b |
children | 0235c32152af |
line wrap: on
line source
1 #+title: Clojure Utilities for jMonkeyEngine32 #+author: Robert McIntyre3 #+email: rlm@mit.edu4 #+description:5 #+keywords: JME3, clojure, import, utilities6 #+SETUPFILE: ../../aurellem/org/setup.org7 #+INCLUDE: ../../aurellem/org/level-0.org9 [TABLE-OF-CONTENTS]11 These are a collection of functions to make programming jMonkeyEngine12 in clojure easier.14 * Imports16 #+name: import17 #+begin_src clojure :results silent18 (ns cortex.import19 (:require swank.util.class-browse))21 (defn permissive-import22 [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 (and30 (.startsWith classname "com.jme3.")31 ;; Don't import the Lwjgl stuff since it can throw exceptions32 ;; upon being loaded.33 (not (re-matches #".*Lwjgl.*" classname))))35 (defn jme-classes36 "returns a list of all jme3 classes"37 []38 (filter39 jme-class?40 (map :name41 swank.util.class-browse/available-classes)))43 (defn mega-import-jme344 "Import ALL the jme classes. For REPL use."45 []46 (doall47 (map (comp permissive-import symbol) (jme-classes))))48 #+end_src50 jMonkeyEngine3 has a plethora of classes which can be overwhelming to51 manage. This code uses reflection to import all of them. Once I'm52 happy with the general structure of a namespace I can deal with53 importing only the classes it actually needs.55 The =mega-import-jme3= is quite usefull for debugging purposes since56 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 output62 (println (clojure.core/count (cortex.import/jme-classes)) "classes")63 #+end_src65 #+results:66 : 955 classes69 * Utilities71 The utilities here come in three main groups:72 - Changing settings in a running =Application=73 - Creating objects74 - Debug Actions75 - Visualizing objects79 *** Changing Settings81 #+name: util82 #+begin_src clojure83 (ns cortex.util84 "Utility functions for making jMonkeyEngine3 easier to program from85 clojure."86 {:author "Robert McIntyre"}87 (:use cortex.world)88 (:use clojure.contrib.def)89 (:import com.jme3.math.Vector3f)90 (:import com.jme3.math.Quaternion)91 (:import com.jme3.asset.TextureKey)92 (:import com.jme3.bullet.control.RigidBodyControl)93 (:import com.jme3.bullet.collision.shapes.GImpactCollisionShape)94 (:import com.jme3.scene.shape.Box)95 (:import com.jme3.scene.Node)96 (:import com.jme3.scene.shape.Sphere)97 (:import com.jme3.light.AmbientLight)98 (:import com.jme3.light.DirectionalLight)99 (:import com.jme3.math.ColorRGBA)100 (:import com.jme3.bullet.BulletAppState)101 (:import com.jme3.material.Material)102 (:import com.jme3.scene.Geometry)103 (:import (java.util.logging Level Logger)))107 (defvar println-repl108 (bound-fn [& args] (apply println args))109 "println called from the LWJGL thread will not go to the REPL, but110 instead to whatever terminal started the JVM process. This function111 will always output to the REPL")113 (defn position-camera114 "Change the position of the in-world camera."115 ([world position direction up]116 (doto (.getCamera world)117 (.setLocation )118 (.lookAt direction up)))119 ([world position direction]120 (position-camera121 world position direction Vector3f/UNIT_Y)))123 (defn enable-debug124 "Turn on debug wireframes for every object in this simulation."125 [world]126 (.enableDebug127 (.getPhysicsSpace128 (.getState129 (.getStateManager world)130 BulletAppState))131 (asset-manager)))133 (defn no-logging134 "Disable all of jMonkeyEngine's logging."135 []136 (.setLevel (Logger/getLogger "com.jme3") Level/OFF))138 (defn set-accuracy139 "Change the accuracy at which the World's Physics is calculated."140 [world new-accuracy]141 (let [physics-manager142 (.getState143 (.getStateManager world) BulletAppState)]144 (.setAccuracy145 (.getPhysicsSpace physics-manager)146 (float new-accuracy))))149 (defn set-gravity150 "In order to change the gravity of a scene, it is not only necessary151 to set the gravity variable, but to \"tap\" every physics object in152 the scene to reactivate physics calculations."153 [world gravity]154 (traverse155 (fn [geom]156 (if-let157 ;; only set gravity for physical objects.158 [control (.getControl geom RigidBodyControl)]159 (do160 (.setGravity control gravity)161 ;; tappsies!162 (.applyImpulse control Vector3f/ZERO Vector3f/ZERO))))163 (.getRootNode world)))165 (defn add-element166 "Add the Spatial to the world's environment"167 ([world element node]168 (.addAll169 (.getPhysicsSpace170 (.getState171 (.getStateManager world)172 BulletAppState))173 element)174 (.attachChild node element))175 ([world element]176 (add-element world element (.getRootNode world))))178 (defn apply-map179 "Like apply, but works for maps and functions that expect an180 implicit map and nothing else as in (fn [& {}]).181 ------- Example -------182 (defn demo [& {:keys [www] :or {www \"oh yeah\"} :as env}]183 (println www))184 (apply-map demo {:www \"hello!\"})185 -->\"hello\""186 [fn m]187 (apply fn (reduce #(into %1 %2) [] m)))189 #+end_src191 #+results: util192 : #'cortex.util/apply-map195 *** Creating Basic Shapes197 #+name: shapes198 #+begin_src clojure :results silent199 (in-ns 'cortex.util)201 (defrecord shape-description202 [name203 color204 mass205 friction206 texture207 material208 position209 rotation210 shape211 physical?212 GImpact?213 ])215 (defvar base-shape216 (shape-description.217 "default-shape"218 false219 ;;ColorRGBA/Blue220 1.0 ;; mass221 1.0 ;; friction222 ;; texture223 "Textures/Terrain/BrickWall/BrickWall.jpg"224 ;; material225 "Common/MatDefs/Misc/Unshaded.j3md"226 Vector3f/ZERO227 Quaternion/IDENTITY228 (Box. Vector3f/ZERO 0.5 0.5 0.5)229 true230 false)231 "Basic settings for shapes.")233 (defn make-shape234 [#^shape-description d]235 (let [asset-manager (asset-manager)236 mat (Material. asset-manager (:material d))237 geom (Geometry. (:name d) (:shape d))]238 (if (:texture d)239 (let [key (TextureKey. (:texture d))]240 ;;(.setGenerateMips key true)241 ;;(.setTexture mat "ColorMap" (.loadTexture asset-manager key))242 ))243 (if (:color d) (.setColor mat "Color" (:color d)))244 (.setMaterial geom mat)245 (if-let [rotation (:rotation d)] (.rotate geom rotation))246 (.setLocalTranslation geom (:position d))247 (if (:physical? d)248 (let [physics-control249 (if (:GImpact d)250 ;; Create an accurate mesh collision shape if desired.251 (RigidBodyControl.252 (doto (GImpactCollisionShape.253 (.getMesh geom))254 (.createJmeMesh)255 (.setMargin 0))256 (float (:mass d)))257 ;; otherwise use jme3's default258 (RigidBodyControl. (float (:mass d))))]259 (.addControl geom physics-control)260 ;;(.setSleepingThresholds physics-control (float 0) (float 0))261 (.setFriction physics-control (:friction d))))262 geom))264 (defn box265 ([l w h & {:as options}]266 (let [options (merge base-shape options)]267 (make-shape (assoc options268 :shape (Box. l w h)))))269 ([] (box 0.5 0.5 0.5)))271 (defn sphere272 ([r & {:as options}]273 (let [options (merge base-shape options)]274 (make-shape (assoc options275 :shape (Sphere. 32 32 (float r))))))276 ([] (sphere 0.5)))278 (defn node-seq279 "Take a node and return a seq of all its children280 recursively. There will be no nodes left in the resulting281 structure"282 [#^Node node]283 (tree-seq #(isa? (class %) Node) #(.getChildren %) node))285 (defn nodify286 "Take a sequence of things that can be attached to a node and return287 a node with all of them attached"288 ([name children]289 (let [node (Node. name)]290 (dorun (map #(.attachChild node %) children))291 node))292 ([children] (nodify "" children)))295 #+end_src298 *** Debug Actions299 #+name: debug-actions300 #+begin_src clojure :results silent301 (in-ns 'cortex.util)303 (defn basic-light-setup304 "returns a sequence of lights appropiate for fully lighting a scene"305 []306 (conj307 (doall308 (map309 (fn [direction]310 (doto (DirectionalLight.)311 (.setDirection direction)312 (.setColor ColorRGBA/White)))313 [;; six faces of a cube314 Vector3f/UNIT_X315 Vector3f/UNIT_Y316 Vector3f/UNIT_Z317 (.mult Vector3f/UNIT_X (float -1))318 (.mult Vector3f/UNIT_Y (float -1))319 (.mult Vector3f/UNIT_Z (float -1))]))320 (doto (AmbientLight.)321 (.setColor ColorRGBA/White))))323 (defn light-up-everything324 "Add lights to a world appropiate for quickly seeing everything325 in the scene. Adds six DirectionalLights facing in orthogonal326 directions, and one AmbientLight to provide overall lighting327 coverage."328 [world]329 (dorun330 (map331 #(.addLight (.getRootNode world) %)332 (basic-light-setup))))334 (defn fire-cannon-ball335 "Creates a function that fires a cannon-ball from the current game's336 camera. The cannon-ball will be attached to the node if provided, or337 to the game's RootNode if no node is provided."338 ([node]339 (fn [game value]340 (if (not value)341 (let [camera (.getCamera game)342 cannon-ball343 (sphere 0.7344 :material "Common/MatDefs/Misc/Unshaded.j3md"345 :texture "Textures/PokeCopper.jpg"346 :position347 (.add (.getLocation camera)348 (.mult (.getDirection camera) (float 1)))349 :mass 3)] ;200 0.05350 (.setLinearVelocity351 (.getControl cannon-ball RigidBodyControl)352 (.mult (.getDirection camera) (float 50))) ;50353 (add-element game cannon-ball (if node node (.getRootNode game)))))))354 ([]355 (fire-cannon-ball false)))357 (def standard-debug-controls358 {"key-space" (fire-cannon-ball)})363 #+end_src366 *** Viewing Objects368 #+name: world-view369 #+begin_src clojure :results silent370 (in-ns 'cortex.util)372 (defprotocol Viewable373 (view [something]))375 (extend-type com.jme3.scene.Geometry376 Viewable377 (view [geo]378 (view (doto (Node.)(.attachChild geo)))))380 (extend-type com.jme3.scene.Node381 Viewable382 (view383 [node]384 (.start385 (world386 node387 {}388 (fn [world]389 (enable-debug world)390 (set-gravity world Vector3f/ZERO)391 (light-up-everything world))392 no-op))))394 (extend-type com.jme3.math.ColorRGBA395 Viewable396 (view397 [color]398 (view (doto (Node.)399 (.attachChild (box 1 1 1 :color color))))))401 (defprotocol Textual402 (text [something]403 "Display a detailed textual analysis of the given object."))405 (extend-type com.jme3.scene.Node406 Textual407 (text [node]408 (println "Total Vertexes: " (.getVertexCount node))409 (println "Total Triangles: " (.getTriangleCount node))410 (println "Controls :")411 (dorun (map #(text (.getControl node %)) (range (.getNumControls node))))412 (println "Has " (.getQuantity node) " Children:")413 (doall (map text (.getChildren node)))))415 (extend-type com.jme3.animation.AnimControl416 Textual417 (text [control]418 (let [animations (.getAnimationNames control)]419 (println "Animation Control with " (count animations) " animation(s):")420 (dorun (map println animations)))))422 (extend-type com.jme3.animation.SkeletonControl423 Textual424 (text [control]425 (println "Skeleton Control with the following skeleton:")426 (println (.getSkeleton control))))428 (extend-type com.jme3.bullet.control.KinematicRagdollControl429 Textual430 (text [control]431 (println "Ragdoll Control")))433 (extend-type com.jme3.scene.Geometry434 Textual435 (text [control]436 (println "...geo...")))437 #+end_src439 Here I make the =Viewable= protocol and extend it to JME's types. Now440 JME3's =hello-world= can be written as easily as:442 #+begin_src clojure :results silent443 (cortex.util/view (cortex.util/box))444 #+end_src447 * COMMENT code generation448 #+begin_src clojure :tangle ../src/cortex/import.clj449 <<import>>450 #+end_src453 #+begin_src clojure :tangle ../src/cortex/util.clj :noweb yes454 <<util>>455 <<shapes>>456 <<debug-actions>>457 <<world-view>>458 #+end_src