Mercurial > cortex
view org/cortex.org @ 22:157b416152ea
continuing splitting
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sun, 23 Oct 2011 23:35:04 -0700 |
parents | 01e1427126af |
children | cab2da252494 |
line wrap: on
line source
1 #+title: Simulated Senses2 #+author: Robert McIntyre3 #+email: rlm@mit.edu4 #+description: Simulating senses for AI research using JMonkeyEngine35 #+SETUPFILE: ../../aurellem/org/setup.org6 #+INCLUDE: ../../aurellem/org/level-0.org7 #+babel: :mkdirp yes :noweb yes :exports both10 * Simulation Base12 ** Imports13 jMonkeyEngine has a plethora of classes which can be overwhelming at14 first. So that I one can get right to coding, it's good to take the15 time right now and make a "import all" function which brings in all of16 the important jme3 classes. Once I'm happy with the general structure17 of a namespace I can deal with importing only the classes it actually18 needs.20 #+srcname: import21 #+begin_src clojure :results silent22 (ns cortex.import23 (:require swank.util.class-browse))25 (defn permissive-import26 [classname]27 (eval `(try (import '~classname)28 (catch java.lang.Exception e#29 (println "couldn't import " '~classname))))30 classname)32 (defn jme-class? [classname]33 (and34 (.startsWith classname "com.jme3.")35 ;; Don't import the Lwjgl stuff since it can throw exceptions36 ;; upon being loaded.37 (not (re-matches #".*Lwjgl.*" classname))))39 (defn jme-classes40 "returns a list of all jme3 classes"41 []42 (filter43 jme-class?44 (map :name45 swank.util.class-browse/available-classes)))47 (defn mega-import-jme348 "Import ALL the jme classes. For REPL use."49 []50 (doall51 (map (comp permissive-import symbol) (jme-classes))))52 #+end_src54 The =mega-import-jme3= is quite usefull for debugging purposes since55 it allows completion for almost all of JME's classes.57 Out of curiousity, let's see just how many classes =mega-import-jme3=58 imports:60 #+begin_src clojure :exports both61 (clojure.core/count (cortex.import/jme-classes))62 #+end_src64 #+results:65 : 95567 ** Simplification68 *** World70 It is comvienent to wrap the JME elements that deal with creating a71 world, creation of basic objects, and Keyboard input with a nicer72 interface (at least for my purposes).74 #+srcname: world-inputs75 #+begin_src clojure :results silent76 (ns cortex.world)77 (require 'cortex.import)78 (use 'clojure.contrib.def)79 (rlm.rlm-commands/help)80 (cortex.import/mega-import-jme3)82 (defvar *app-settings*83 (doto (AppSettings. true)84 (.setFullscreen false)85 (.setTitle "Aurellem.")86 ;; disable 32 bit stuff for now87 ;;(.setAudioRenderer "Send")88 )89 "These settings control how the game is displayed on the screen for90 debugging purposes. Use binding forms to change this if desired.91 Full-screen mode does not work on some computers.")93 (defn asset-manager94 "returns a new, configured assetManager" []95 (JmeSystem/newAssetManager96 (.getResource97 (.getContextClassLoader (Thread/currentThread))98 "com/jme3/asset/Desktop.cfg")))100 (defmacro no-exceptions101 "Sweet relief like I never knew."102 [& forms]103 `(try ~@forms (catch Exception e# (.printStackTrace e#))))105 (defn thread-exception-removal []106 (println "removing exceptions from " (Thread/currentThread))107 (.setUncaughtExceptionHandler108 (Thread/currentThread)109 (proxy [Thread$UncaughtExceptionHandler] []110 (uncaughtException111 [thread thrown]112 (println "uncaught-exception thrown in " thread)113 (println (.getMessage thrown))))))115 (def println-repl (bound-fn [& args] (apply println args)))117 (use '[pokemon [lpsolve :only [constant-map]]])119 (defn no-op [& _])121 (defn all-keys122 "Construct a map of strings representing all the manual inputs from123 either the keyboard or mouse."124 []125 (let [inputs (constant-map KeyInput)]126 (assoc127 (zipmap (map (fn [field]128 (.toLowerCase (re-gsub #"_" "-" field))) (vals inputs))129 (map (fn [val] (KeyTrigger. val)) (keys inputs)))130 ;;explicitly add mouse controls131 "mouse-left" (MouseButtonTrigger. 0)132 "mouse-middle" (MouseButtonTrigger. 2)133 "mouse-right" (MouseButtonTrigger. 1))))135 (defn initialize-inputs136 "more java-interop cruft to establish keybindings for a particular virtual world"137 [game input-manager key-map]138 (doall (map (fn [[name trigger]]139 (.addMapping ^InputManager input-manager140 name (into-array (class trigger) [trigger]))) key-map))141 (doall (map (fn [name]142 (.addListener ^InputManager input-manager game143 (into-array String [name]))) (keys key-map))))145 #+end_src147 These functions are all for debug controlling of the world through148 keyboard and mouse.150 We reuse =constant-map= from =pokemon.lpsolve= to get the numerical151 values for all the keys defined in the =KeyInput= class. The152 documentation for =constant-map= is:154 #+begin_src clojure :results output155 (doc pokemon.lpsolve/constant-map)156 #+end_src158 #+results:159 : -------------------------160 : pokemon.lpsolve/constant-map161 : ([class])162 : Takes a class and creates a map of the static constant integer163 : fields with their names. This helps with C wrappers where they have164 : just defined a bunch of integer constants instead of enums167 Then, =all-keys= converts the constant names like =KEY_J= to the more168 clojure-like =key-j=, and returns a map from these keys to169 jMonkeyEngine KeyTrigger objects, the use of which will soon become170 apparent. =all-keys= also adds the three mouse button controls to the171 map.173 #+srcname: world174 #+begin_src clojure :results silent175 (in-ns 'cortex.world)177 (defn traverse178 "apply f to every non-node, deeply"179 [f node]180 (if (isa? (class node) Node)181 (dorun (map (partial traverse f) (.getChildren node)))182 (f node)))184 (def gravity (Vector3f. 0 -9.81 0))186 (defn world187 [root-node key-map setup-fn update-fn]188 (let [physics-manager (BulletAppState.)189 shadow-renderer (BasicShadowRenderer. (asset-manager) (int 256))190 ;;maybe use a better shadow renderer someday!191 ;;shadow-renderer (PssmShadowRenderer. (asset-manager) 256 1)192 ]193 (doto194 (proxy [SimpleApplication ActionListener] []195 (simpleInitApp196 []197 (no-exceptions198 (.setTimer this (IsoTimer. 60))199 ;; Create key-map.200 (.setFrustumFar (.getCamera this) 300)201 (initialize-inputs this (.getInputManager this) (all-keys))202 ;; Don't take control of the mouse203 (org.lwjgl.input.Mouse/setGrabbed false)204 ;; add all objects to the world205 (.attachChild (.getRootNode this) root-node)206 ;; enable physics207 ;; add a physics manager208 (.attach (.getStateManager this) physics-manager)209 (.setGravity (.getPhysicsSpace physics-manager) gravity)212 ;; go through every object and add it to the physics manager213 ;; if relavant.214 (traverse (fn [geom]215 (dorun216 (for [n (range (.getNumControls geom))]217 (do218 (println-repl "adding control " (.getControl geom n))219 (.add (.getPhysicsSpace physics-manager)220 (.getControl geom n))))))221 (.getRootNode this))222 ;;(.addAll (.getPhysicsSpace physics-manager) (.getRootNode this))224 (setup-fn this)225 (.setDirection shadow-renderer226 (.normalizeLocal (Vector3f. -1 -1 -1)))227 (.addProcessor (.getViewPort this) shadow-renderer)228 (.setShadowMode (.getRootNode this) RenderQueue$ShadowMode/Off)229 ))230 (simpleUpdate231 [tpf]232 (no-exceptions233 (update-fn this tpf)))234 (onAction235 [binding value tpf]236 ;; whenever a key is pressed, call the function returned from237 ;; key-map.238 (no-exceptions239 (if-let [react (key-map binding)]240 (react this value)))))241 ;; don't show a menu to change options.243 (.setShowSettings false)244 (.setPauseOnLostFocus false)245 (.setSettings *app-settings*))))247 (defn apply-map248 "Like apply, but works for maps and functions that expect an implicit map249 and nothing else as in (fn [& {}]).250 ------- Example -------251 (defn jjj [& {:keys [www] :or {www \"oh yeah\"} :as env}] (println www))252 (apply-map jjj {:www \"whatever\"})253 -->\"whatever\""254 [fn m]255 (apply fn (reduce #(into %1 %2) [] m)))257 #+end_src260 =world= is the most important function here.261 *** TODO more documentation263 #+srcname: world-shapes264 #+begin_src clojure :results silent265 (in-ns 'cortex.world)266 (defrecord shape-description267 [name268 color269 mass270 friction271 texture272 material273 position274 rotation275 shape276 physical?])278 (def base-shape279 (shape-description.280 "default-shape"281 false282 ;;ColorRGBA/Blue283 1.0 ;; mass284 1.0 ;; friction285 ;; texture286 "Textures/Terrain/BrickWall/BrickWall.jpg"287 ;; material288 "Common/MatDefs/Misc/Unshaded.j3md"289 Vector3f/ZERO290 Quaternion/IDENTITY291 (Box. Vector3f/ZERO 0.5 0.5 0.5)292 true))294 (defn make-shape295 [#^shape-description d]296 (let [asset-manager (if (:asset-manager d) (:asset-manager d) (asset-manager))297 mat (Material. asset-manager (:material d))298 geom (Geometry. (:name d) (:shape d))]299 (if (:texture d)300 (let [key (TextureKey. (:texture d))]301 (.setGenerateMips key true)302 (.setTexture mat "ColorMap" (.loadTexture asset-manager key))))303 (if (:color d) (.setColor mat "Color" (:color d)))304 (.setMaterial geom mat)305 (if-let [rotation (:rotation d)] (.rotate geom rotation))306 (.setLocalTranslation geom (:position d))307 (if (:physical? d)308 (let [impact-shape (doto (GImpactCollisionShape.309 (.getMesh geom)) (.setMargin 0))310 physics-control (RigidBodyControl.311 ;;impact-shape ;; comment to disable312 (float (:mass d)))]313 (.createJmeMesh impact-shape)314 (.addControl geom physics-control)315 ;;(.setSleepingThresholds physics-control (float 0) (float 0))316 (.setFriction physics-control (:friction d))))317 ;;the default is to keep this node in the physics engine forever.318 ;;these commands must come after the control is added to the geometry.319 ;;320 geom))322 (defn box323 ([l w h & {:as options}]324 (let [options (merge base-shape options)]325 (make-shape (assoc options326 :shape (Box. l w h)))))327 ([] (box 0.5 0.5 0.5)))329 (defn sphere330 ([r & {:as options}]331 (let [options (merge base-shape options)]332 (make-shape (assoc options333 :shape (Sphere. 32 32 (float r))))))334 ([] (sphere 0.5)))336 (defn add-element337 ([game element node]338 (.addAll339 (.getPhysicsSpace340 (.getState341 (.getStateManager game)342 BulletAppState))343 element)344 (.attachChild node element))345 ([game element]346 (add-element game element (.getRootNode game))))349 (defn set-gravity*350 [game gravity]351 (traverse352 (fn [geom]353 (if-let354 [control (.getControl geom RigidBodyControl)]355 (do356 (.setGravity control gravity)357 (.applyImpulse control Vector3f/ZERO Vector3f/ZERO)358 )))359 (.getRootNode game)))361 #+end_src363 These are convienence functions for creating JME objects and364 manipulating a world.366 #+srcname: world-view367 #+begin_src clojure :results silent368 (in-ns 'cortex.world)370 (defprotocol Viewable371 (view [something]))373 (extend-type com.jme3.scene.Geometry374 Viewable375 (view [geo]376 (view (doto (Node.)(.attachChild geo)))))378 (extend-type com.jme3.scene.Node379 Viewable380 (view [node]381 (.start382 (world node383 {}384 (fn [world]385 (.enableDebug386 (.getPhysicsSpace387 (.getState388 (.getStateManager world)389 BulletAppState))390 (asset-manager))391 (set-gravity* world Vector3f/ZERO)392 ;; (set-gravity* world (Vector3f. 0 (float -0.4) 0))393 (let [sun (doto (DirectionalLight.)394 (.setDirection (.normalizeLocal (Vector3f. 1 0 -2)))395 (.setColor ColorRGBA/White))]396 (.addLight (.getRootNode world) sun)))397 no-op))))399 (defn position-camera [game]400 (doto (.getCamera game)401 (.setLocation (Vector3f. 0 6 6))402 (.lookAt Vector3f/ZERO (Vector3f. 0 1 0))))404 #+end_src406 Here I make the =Viewable= protocol and extend it to JME's types. Now407 hello-world can be written as easily as:409 #+begin_src clojure :results silent410 (cortex.world/view (cortex.world/box))411 #+end_src413 ** Hello414 Here are the jmonkeyengine "Hello" programs translated to clojure.415 *** Hello Simple App416 Here is the hello world example for jme3 in clojure.417 It's a more or less direct translation from the java source418 from419 http://jmonkeyengine.org/wiki/doku.php/jme3:beginner:hello_simpleapplication.421 Of note is the fact that since we don't have access to the422 =AssetManager= via extendig =SimpleApplication=, we have to build one423 ourselves.425 #+srcname: hello-simple-app426 #+begin_src clojure :results silent427 (ns hello.hello-simple-app)428 (require 'cortex.import)429 (use 'clojure.contrib.def)430 (rlm.rlm-commands/help)431 (cortex.import/mega-import-jme3)432 (use 'cortex.world)435 (def cube (Box. Vector3f/ZERO 1 1 1))437 (def geom (Geometry. "Box" cube))439 (def mat (Material. (asset-manager) "Common/MatDefs/Misc/Unshaded.j3md"))441 (.setColor mat "Color" ColorRGBA/Blue)443 (.setMaterial geom mat)445 (defn simple-app []446 (doto447 (proxy [SimpleApplication] []448 (simpleInitApp449 []450 ;; Don't take control of the mouse451 (org.lwjgl.input.Mouse/setGrabbed false)452 (.attachChild (.getRootNode this) geom)))453 ;; don't show a menu to change options.454 (.setShowSettings false)455 (.setPauseOnLostFocus false)456 (.setSettings *app-settings*)))457 #+end_src459 Running this program will begin a new jMonkeyEngine game which460 displays a single blue cube.462 #+begin_src clojure :exports code :results silent463 (.start (hello.hello-simple-app/simple-app))464 #+end_src466 #+caption: the simplest JME game.467 [[./images/simple-app.jpg]]471 *** Hello Physics472 From http://jmonkeyengine.org/wiki/doku.php/jme3:beginner:hello_physics474 #+srcname: brick-wall-header475 #+begin_src clojure :results silent476 (ns hello.brick-wall)477 (require 'cortex.import)478 (use 'clojure.contrib.def)479 (rlm.rlm-commands/help)480 (cortex.import/mega-import-jme3)481 (use '[pokemon [lpsolve :only [constant-map]]])482 (use 'cortex.world)483 #+end_src485 #+srcname: brick-wall-body486 #+begin_src clojure :results silent487 (in-ns 'hello.brick-wall)489 (defn floor490 "make a sturdy, unmovable physical floor"491 []492 (box 20 1 20 :mass 0 :color false :position (Vector3f. 0 -2 0)))494 (def brick-length 0.48)495 (def brick-width 0.24)496 (def brick-height 0.12)499 (defn brick* [position]500 (doto (box brick-length brick-height brick-width501 :position position :name "brick"502 :material "Common/MatDefs/Misc/Unshaded.j3md"503 :texture "Textures/Terrain/BrickWall/BrickWall.jpg"504 :mass 36)505 (->506 (.getMesh)507 (.scaleTextureCoordinates (Vector2f. 1 0.5)))508 ;;(.setShadowMode RenderQueue$ShadowMode/CastAndReceive)509 )510 )512 (defn inception-brick-wall513 "construct a physical brick wall"514 []515 (let [node (Node. "brick-wall")]516 (dorun517 (map (comp #(.attachChild node %) brick*)518 (for519 [x (range 15)520 y (range 10)521 z (range 1)]522 (Vector3f.523 (* brick-length x 1.03)524 (* brick-width y y 10)525 (* brick-height z)))))526 node))528 (defn gravity-toggle529 [new-value]530 (fn [game value]531 (println-repl "set gravity to " new-value)532 (if value533 (set-gravity* game new-value)534 (set-gravity* game gravity))))536 (defn fire-cannon-ball537 ([node]538 (fn [game value]539 (if (not value)540 (let [camera (.getCamera game)541 cannon-ball542 (sphere 0.7543 :material "Common/MatDefs/Misc/Unshaded.j3md"544 :texture "Textures/PokeCopper.jpg"545 :position546 (.add (.getLocation camera)547 (.mult (.getDirection camera) (float 1)))548 :mass 3)] ;200 0.05549 (.setShadowMode cannon-ball RenderQueue$ShadowMode/CastAndReceive)550 (.setLinearVelocity551 (.getControl cannon-ball RigidBodyControl)552 (.mult (.getDirection camera) (float 50))) ;50553 (add-element game cannon-ball (if node node (.getRootNode game)))))))554 ([]555 (fire-cannon-ball false)))558 (defn floor* []559 (doto (box 10 0.1 5 :name "floor" ;10 0.1 5 ; 240 0.1 240560 :material "Common/MatDefs/Misc/Unshaded.j3md"561 :texture "Textures/Terrain/Pond/Pond.png"562 :position (Vector3f. 0 -0.1 0 )563 :mass 0)564 (->565 (.getMesh)566 (.scaleTextureCoordinates (Vector2f. 3 6)));64 64567 (->568 (.getMaterial)569 (.getTextureParam "ColorMap")570 (.getTextureValue)571 (.setWrap Texture$WrapMode/Repeat))572 (.setShadowMode RenderQueue$ShadowMode/Receive)573 ))575 (defn brick-wall* []576 (let [node (Node. "brick-wall")]577 (dorun578 (map579 (comp #(.attachChild node %) brick*)580 (for [y (range 15)581 x (range 4)582 z (range 1)]583 (Vector3f.584 (+ (* 2 x brick-length)585 (if (even? (+ y z))586 (/ brick-length 4) (/ brick-length -4)))587 (+ (* brick-height (inc (* 2 y))))588 (* 2 z brick-width) ))))589 (.setShadowMode node RenderQueue$ShadowMode/CastAndReceive)590 node))592 (defn brick-wall-game-run []593 (doto594 (world595 (doto (Node.) (.attachChild (floor*))596 (.attachChild (brick-wall*))597 )598 {"key-i" (gravity-toggle (Vector3f. 0 0 -9.81))599 "key-m" (gravity-toggle (Vector3f. 0 0 9.81))600 "key-l" (gravity-toggle (Vector3f. 9.81 0 0))601 "key-j" (gravity-toggle (Vector3f. -9.81 0 0))602 "key-k" (gravity-toggle Vector3f/ZERO)603 "key-u" (gravity-toggle (Vector3f. 0 9.81 0))604 "key-o" (gravity-toggle (Vector3f. 0 -9.81 0))605 "key-f" (fn[game value]606 (if (not value) (add-element game (brick-wall*))))607 "key-return" (fire-cannon-ball)}608 position-camera609 (fn [& _]))610 (.start)))611 #+end_src613 #+begin_src clojure :results silent614 (hello.brick-wall/brick-wall-game-run)615 #+end_src617 #+caption: the brick wall standing618 [[./images/brick-wall-standing.jpg]]620 #+caption: the brick wall after it has been knocked over by a "pok\eacute{}ball"621 [[./images/brick-wall-knocked-down.jpg]]623 *** Other Brick Games624 #+srcname: other-games625 #+begin_src clojure :results silent626 (ns cortex.other-games627 {:author "Dylan Holmes"})628 (use 'cortex.world)629 (use 'hello.brick-wall)630 (use 'cortex.import)631 (cortex.import/mega-import-jme3)633 (defn scad [position]634 (doto (box 0.1 0.1 0.1635 :position position :name "brick"636 :material "Common/MatDefs/Misc/Unshaded.j3md"637 :texture "Textures/Terrain/BrickWall/BrickWall.jpg"638 :mass 20)639 (->640 (.getMesh)641 (.scaleTextureCoordinates (Vector2f. 1 0.5))642 )643 (-> (.getControl RigidBodyControl)644 (.setLinearVelocity (Vector3f. 0 100 0))645 )647 ;;(.setShadowMode RenderQueue$ShadowMode/Cast)648 ))651 (defn shrapnel []652 (let [node (Node. "explosion-day")]653 (dorun654 (map655 (comp #(.attachChild node %) scad)656 (for [y (range 15)657 x (range 4)658 z (range 1)]659 (Vector3f.660 (+ (* 2 x brick-height)661 (if (even? (+ y z)) (/ brick-height 4) (/ brick-height -4)))662 (+ (* brick-height (inc (* 2 y))))663 (* 2 z brick-height) ))))664 node))667 (def domino-height 0.48)668 (def domino-thickness 0.12)669 (def domino-width 0.24)671 (def domino-thickness 0.05)672 (def domino-width 0.5)673 (def domino-height 1)675 (defn domino676 ([position]677 (domino position (Quaternion/IDENTITY)))678 ([position rotation]679 (doto (box domino-width domino-height domino-thickness680 :position position :name "domino"681 :material "Common/MatDefs/Misc/Unshaded.j3md"682 :texture "Textures/Terrain/BrickWall/BrickWall.jpg"683 :mass 1684 :rotation rotation)685 (.setShadowMode RenderQueue$ShadowMode/CastAndReceive)686 )))689 (defn domino-row []690 (let [node (Node. "domino-row")]691 (dorun692 (map693 (comp #(.attachChild node %) domino)694 (for [695 z (range 10)696 x (range 5)697 ]698 (Vector3f.699 (+ (* z domino-width) (* x 5 domino-width))700 (/ domino-height 1)701 (* -5.5 domino-thickness z) ))))703 node))705 (defn domino-cycle []706 (let [node (Node. "domino-cycle")]707 (dorun708 (map709 (comp #(.attachChild node %) (partial apply domino) )710 (for [n (range 720)]711 (let [space (* domino-height 5.5)712 r (fn[n] (* (+ n 3) domino-width 0.5))713 t (fn[n] (reduce714 +715 (map716 (fn dt[n] (/ space (* 2 (Math/PI) (r n))))717 (range n))))718 t (t n)719 r (r n)720 ct (Math/cos t)721 st (Math/sin t)722 ]723 (list724 (Vector3f.725 (* -1 r st)726 (/ domino-height 1)727 (* r ct))728 (.fromAngleAxis (Quaternion.)729 (- (/ 3.1415926 2) t) (Vector3f. 0 1 0))730 )))731 ))732 node))735 (defn domino-game-run []736 (doto737 (world738 (doto (Node.) (.attachChild (floor*))739 )740 {"key-i" (gravity-toggle (Vector3f. 0 0 -9.81))741 "key-m" (gravity-toggle (Vector3f. 0 0 9.81))742 "key-l" (gravity-toggle (Vector3f. 9.81 0 0))743 "key-j" (gravity-toggle (Vector3f. -9.81 0 0))744 "key-k" (gravity-toggle (Vector3f. 0 9.81 0) )745 "key-u" (fn[g v] ((gravity-toggle (Vector3f. 0 -0 0)) g true))746 "key-o" (gravity-toggle (Vector3f. 0 -9.81 0))748 "key-space"749 (fn[game value]751 (if (not value)752 (let [d (domino (Vector3f. 0 (/ domino-height 0.25) 0)753 (.fromAngleAxis (Quaternion.)754 (/ Math/PI 2) (Vector3f. 0 1 0)))]755 (add-element game d))))756 "key-f"757 (fn[game value](if (not value) (add-element game (domino-cycle))))758 "key-return" (fire-cannon-ball)}759 position-camera760 (fn [& _]))761 (.start)))762 #+end_src764 #+begin_src clojure :results silent765 (cortex.other-games/domino-game-run)766 #+end_src768 #+caption: floating dominos769 [[./images/dominos.jpg]]771 *** Hello Loop772 #+srcname: hello-loop773 #+begin_src clojure :results silent774 (ns hello.loop)775 (use 'cortex.world)776 (use 'cortex.import)777 (cortex.import/mega-import-jme3)778 (rlm.rlm-commands/help)780 (defn blue-cube []781 (box 1 1 1782 :color ColorRGBA/Blue783 :texture false784 :material "Common/MatDefs/Misc/Unshaded.j3md"785 :name "blue-cube"786 :physical? false))788 (defn blue-cube-game []789 (let [cube (blue-cube)790 root (doto (Node.) (.attachChild cube))]791 (world root792 {}793 no-op794 (fn [game tpf]795 (.rotate cube 0.0 (* 2 tpf) 0.0)))))796 #+end_src798 *** Hello Collision800 #+srcname: hello-collision801 #+begin_src clojure :results silent802 (ns hello.collision)803 (use 'cortex.world)804 (use 'cortex.import)805 (use 'clojure.contrib.def)808 (cortex.import/mega-import-jme3)809 (rlm.rlm-commands/help)810 (use '[hello [brick-wall :only [fire-cannon-ball brick-wall*]]])813 (defn environment []814 (let815 [scene-model816 (doto817 (.loadModel818 (doto (asset-manager)819 (.registerLocator820 "/home/r/cortex/assets/zips/town.zip" ZipLocator))821 "main.scene")822 (.setLocalScale (float 2.0)))823 collision-shape824 (CollisionShapeFactory/createMeshShape #^Node scene-model)825 landscape (RigidBodyControl. collision-shape 0)]826 (.setShadowMode scene-model RenderQueue$ShadowMode/CastAndReceive)827 (.addControl scene-model landscape)828 scene-model))830 (defn player-fn []831 (doto832 (CharacterControl.833 (CapsuleCollisionShape. (float 1.5) (float 6)(float 1))834 (float 0.05))835 (.setJumpSpeed 20)836 (.setFallSpeed 30)837 (.setGravity 30) ;30838 (.setPhysicsLocation (Vector3f. 0 10 0))))840 (defn lights []841 [(doto (AmbientLight.) (.setColor (.mult (ColorRGBA. 1 1 1 1) (float 1))))842 (doto (AmbientLight.) (.setColor (.mult (ColorRGBA. 1 0.7 0 1) (float 1))))843 (doto (DirectionalLight.)844 (.setColor (.mult ColorRGBA/White (float 0.9) ))845 (.setDirection (.normalizeLocal (Vector3f. 2.8 -28 2.8))))])847 (defn night-lights []848 [(doto (AmbientLight.) (.setColor (.mult (ColorRGBA. 0.275 0.467 0.784 1) (float 0.3))))849 (doto (DirectionalLight.)850 (.setColor (.mult ColorRGBA/White (float 0.2) ))851 (.setDirection (.normalizeLocal (Vector3f. 2.8 -28 2.8))))])853 (def player (atom (player-fn)))855 (defn setup-fn [game]856 (dorun (map #(.addLight (.getRootNode game) %) (lights)))857 ;; set the color of the sky858 (.setBackgroundColor (.getViewPort game) (ColorRGBA. 0.3 0.4 0.9 1))859 ;(.setBackgroundColor (.getViewPort game) (ColorRGBA. 0 0 0 1)860 (doto (.getFlyByCamera game)861 (.setMoveSpeed (float 100))862 (.setRotationSpeed 3))863 (.add864 (.getPhysicsSpace865 (.getState (.getStateManager game) BulletAppState))866 @player)868 (doto (Node.) (.attachChild (.getRootNode game))869 (.attachChild (brick-wall*))870 )872 )875 (def walking-up? (atom false))876 (def walking-down? (atom false))877 (def walking-left? (atom false))878 (def walking-right? (atom false))880 (defn set-walk [walk-atom game value]881 ;;(println-repl "setting stuff to " value)882 (reset! walk-atom value))884 (defn responses []885 {"key-w" (partial set-walk walking-up?)886 "key-d" (partial set-walk walking-right?)887 "key-s" (partial set-walk walking-down?)888 "key-a" (partial set-walk walking-left?)889 "key-return" (fire-cannon-ball)890 "key-space" (fn [game value] (.jump @player))891 })893 (defn update-fn894 [game tpf]895 (let [camera (.getCamera game)896 cam-dir (.multLocal897 (.clone898 (.getDirection camera)) (float 0.6))899 cam-left (.multLocal900 (.clone901 (.getLeft camera)) (float 0.4))902 walk-direction (Vector3f. 0 0 0)]904 (cond905 @walking-up? (.addLocal walk-direction cam-dir)906 @walking-right? (.addLocal walk-direction (.negate cam-left))907 @walking-down? (.addLocal walk-direction (.negate cam-dir))908 @walking-left? (.addLocal walk-direction cam-left))909 (.setWalkDirection @player walk-direction)910 (.setLocation camera (.getPhysicsLocation @player))))912 (defn run-game []913 (.start914 (world (environment)915 (responses)916 setup-fn917 update-fn)))918 #+end_src920 *** Hello Terrain921 #+srcname: hello-terrain922 #+begin_src clojure :results silent923 (ns hello.terrain)924 (use 'cortex.world)925 (use 'cortex.import)926 (use 'clojure.contrib.def)927 (import jme3tools.converters.ImageToAwt)930 (cortex.import/mega-import-jme3)931 (rlm.rlm-commands/help)932 (use '[hello [brick-wall :only [fire-cannon-ball brick-wall*]]])935 (defn setup-fn [type game]936 (.setMoveSpeed (.getFlyByCamera game) 50)937 (.setFrustumFar (.getCamera game) 10000)938 (let [env (environment type)939 cameras [(.getCamera game)]940 control (TerrainLodControl. env cameras)]941 ;;(.addControl env control)942 (.attachChild (.getRootNode game) env)))944 (defn environment [type]945 (let946 [mat_terrain947 (Material. (asset-manager) "Common/MatDefs/Terrain/Terrain.j3md")948 grass (.loadTexture (asset-manager) "Textures/Terrain/splat/grass.jpg")949 dirt (.loadTexture (asset-manager) "Textures/Terrain/splat/dirt.jpg")950 rock (.loadTexture (asset-manager) "Textures/Terrain/splat/road.jpg")951 heightmap-image (.loadTexture (asset-manager)952 ({:mountain "Textures/Terrain/splat/mountains512.png"953 :fortress "Textures/Terrain/splat/fortress512.png"954 }type))955 heightmap (ImageBasedHeightMap.956 (ImageToAwt/convert (.getImage heightmap-image) false true 0))957 terrain (do (.load heightmap)958 (TerrainQuad. "my terrain" 65 513 (.getHeightMap heightmap)))959 ]961 (dorun (map #(.setWrap % Texture$WrapMode/Repeat)962 [grass dirt rock]))964 (doto mat_terrain965 (.setTexture "Tex1" grass)966 (.setFloat "Tex1Scale" (float 64))968 (.setTexture "Tex2" dirt)969 (.setFloat "Tex2Scale" (float 32))971 (.setTexture "Tex3" rock)972 (.setFloat "Tex3Scale" (float 128))974 (.setTexture "Alpha"975 (.loadTexture976 (asset-manager)977 ({:mountain "Textures/Terrain/splat/alphamap.png"978 :fortress "Textures/Terrain/splat/alphamap2.png"} type))))980 (doto terrain981 (.setMaterial mat_terrain)982 (.setLocalTranslation 0 -100 0)983 (.setLocalScale 2 1 2))))987 (defn run-terrain-game [type]988 (.start989 (world990 (Node.)991 {}992 (partial setup-fn type)993 no-op)))994 #+end_src998 #+srcname: hello-animation999 #+begin_src clojure :results silent1000 (ns hello.animation)1001 (use 'cortex.world)1002 (use 'cortex.import)1003 (use 'clojure.contrib.def)1004 (cortex.import/mega-import-jme3)1005 (rlm.rlm-commands/help)1006 (use '[hello [collision :only [lights]]])1008 (defn stand1009 [channel]1010 (doto channel1011 (.setAnim "stand" (float 0.5))1012 (.setLoopMode LoopMode/DontLoop)1013 (.setSpeed (float 1))))1015 (defn anim-listener []1016 (proxy [AnimEventListener] []1017 (onAnimChange1018 [control channel animation-name]1019 (println-repl "RLM --- onAnimChange"))1020 (onAnimCycleDone1021 [control channel animation-name]1022 (if (= animation-name "Walk")1023 (stand channel)1024 ))))1026 (defn setup-fn [channel game]1027 (dorun (map #(.addLight (.getRootNode game) %) (lights)))1028 ;; set the color of the sky1029 (.setBackgroundColor (.getViewPort game) (ColorRGBA. 0.3 0.4 0.9 1))1030 ;(.setBackgroundColor (.getViewPort game) (ColorRGBA. 0 0 0 1)1031 (.setAnim channel "stand")1032 (doto (.getFlyByCamera game)1033 (.setMoveSpeed (float 10))1034 (.setRotationSpeed 1)))1036 (defn walk [channel]1037 (println-repl "zzz")1038 (doto channel1039 (.setAnim "Walk" (float 0.5))1040 (.setLoopMode LoopMode/Loop)))1043 (defn key-map [channel]1044 {"key-space" (fn [game value]1045 (if (not value)1046 (walk channel)))})1048 (defn player []1049 (let [model (.loadModel (asset-manager) "Models/Oto/Oto.mesh.xml")1050 control (.getControl model AnimControl)]1051 (.setLocalScale model (float 0.5))1052 (.clearListeners control)1053 (.addListener control (anim-control))1054 model))1058 (defn run-anim-game []1059 (let [ninja (player)1060 control (.getControl ninja AnimControl)1061 channel (.createChannel control)]1062 (.start1063 (world1064 ninja1065 (key-map channel)1066 (partial setup-fn channel)1067 no-op))))1068 #+end_src1070 *** Hello Materials1071 #+srcname: material1072 #+begin_src clojure :results silent1073 (ns hello.material)1074 (use 'cortex.world)1075 (use 'cortex.import)1076 (use 'clojure.contrib.def)1077 (cortex.import/mega-import-jme3)1078 (rlm.rlm-commands/help)1080 (defn simple-cube []1081 (box 1 1 11082 :position (Vector3f. -3 1.1 0)1083 :material "Common/MatDefs/Misc/Unshaded.j3md"1084 :texture "Interface/Logo/Monkey.jpg"1085 :physical? false))1087 (defn leaky-box []1088 (box 1 1 11089 :position (Vector3f. 3 -1 0)1090 :material "Common/MatDefs/Misc/ColoredTextured.j3md"1091 :texture "Textures/ColoredTex/Monkey.png"1092 :color (ColorRGBA. 1 0 1 1)1093 :physical? false))1095 (defn transparent-box []1096 (doto1097 (box 1 1 0.11098 :position Vector3f/ZERO1099 :name "window frame"1100 :material "Common/MatDefs/Misc/Unshaded.j3md"1101 :texture "Textures/ColoredTex/Monkey.png"1102 :physical? false)1103 (-> (.getMaterial)1104 (.getAdditionalRenderState)1105 (.setBlendMode RenderState$BlendMode/Alpha))1106 (.setQueueBucket RenderQueue$Bucket/Transparent)))1108 (defn bumpy-sphere []1109 (doto1110 (sphere 21111 :position (Vector3f. 0 2 -2)1112 :name "Shiny rock"1113 :material "Common/MatDefs/Light/Lighting.j3md"1114 :texture false1115 :physical? false)1116 (-> (.getMesh)1117 (doto1118 (.setTextureMode Sphere$TextureMode/Projected)1119 (TangentBinormalGenerator/generate)))1120 (-> (.getMaterial)1121 (doto1122 (.setTexture "DiffuseMap" (.loadTexture (asset-manager)1123 "Textures/Terrain/Pond/Pond.png"))1124 (.setTexture "NormalMap" (.loadTexture (asset-manager)1125 "Textures/Terrain/Pond/Pond_normal.png"))1126 (.setFloat "Shininess" (float 5))))1127 (.rotate (float 1.6) 0 0)))1130 (defn start-game []1131 (.start1132 (world1133 (let [root (Node.)]1134 (dorun (map #(.attachChild root %)1135 [(simple-cube) (leaky-box) (transparent-box) (bumpy-sphere)]))1136 root)1137 {}1138 (fn [world]1139 (let [sun (doto (DirectionalLight.)1140 (.setDirection (.normalizeLocal (Vector3f. 1 0 -2)))1141 (.setColor ColorRGBA/White))]1142 (.addLight (.getRootNode world) sun)))1143 no-op1144 )))1145 #+end_src1149 * The Body1150 ** Eyes1152 Ultimately I want to make creatures with eyes. Each eye can be1153 independely moved and should see its own version of the world1154 depending on where it is.1155 #+srcname: eyes1156 #+begin_src clojure1157 (ns body.eye)1158 (use 'cortex.world)1159 (use 'cortex.import)1160 (use 'clojure.contrib.def)1161 (cortex.import/mega-import-jme3)1162 (rlm.rlm-commands/help)1163 (import java.nio.ByteBuffer)1164 (import java.awt.image.BufferedImage)1165 (import java.awt.Color)1166 (import java.awt.Dimension)1167 (import java.awt.Graphics)1168 (import java.awt.Graphics2D)1169 (import java.awt.event.WindowAdapter)1170 (import java.awt.event.WindowEvent)1171 (import java.awt.image.BufferedImage)1172 (import java.nio.ByteBuffer)1173 (import javax.swing.JFrame)1174 (import javax.swing.JPanel)1175 (import javax.swing.SwingUtilities)1176 (import javax.swing.ImageIcon)1177 (import javax.swing.JOptionPane)1178 (import java.awt.image.ImageObserver)1182 (defn scene-processor1183 "deals with converting FrameBuffers to BufferedImages so1184 that the continuation function can be defined only in terms1185 of what it does with BufferedImages"1186 [continuation]1187 (let [byte-buffer (atom nil)1188 renderer (atom nil)1189 image (atom nil)]1190 (proxy [SceneProcessor] []1191 (initialize1192 [renderManager viewPort]1193 (let [cam (.getCamera viewPort)1194 width (.getWidth cam)1195 height (.getHeight cam)]1196 (reset! renderer (.getRenderer renderManager))1197 (reset! byte-buffer1198 (BufferUtils/createByteBuffer1199 (* width height 4)))1200 (reset! image (BufferedImage. width height1201 BufferedImage/TYPE_4BYTE_ABGR))))1202 (isInitialized [] (not (nil? @byte-buffer)))1203 (reshape [_ _ _])1204 (preFrame [_])1205 (postQueue [_])1206 (postFrame1207 [#^FrameBuffer fb]1208 (.clear @byte-buffer)1209 (.readFrameBuffer @renderer fb @byte-buffer)1210 (Screenshots/convertScreenShot @byte-buffer @image)1211 (continuation @image))1212 (cleanup []))))1214 (defn add-eye1215 "Add an eye to the world, and call continuation on1216 every frame produced"1217 [world camera continuation]1218 (let [width (.getWidth camera)1219 height (.getHeight camera)1220 render-manager (.getRenderManager world)1221 viewport (.createMainView render-manager "eye-view" camera)]1222 (doto viewport1223 (.setBackgroundColor ColorRGBA/Black)1224 (.setClearFlags true true true)1225 (.addProcessor (scene-processor continuation))1226 (.attachScene (.getRootNode world)))))1228 (defn make-display-frame [display width height]1229 (SwingUtilities/invokeLater1230 (fn []1231 (.setPreferredSize display (Dimension. width height))1232 (doto (JFrame. "Eye Camera!")1233 (-> (.getContentPane) (.add display))1234 (.setDefaultCloseOperation JFrame/DISPOSE_ON_CLOSE)1235 (.pack)1236 (.setLocationRelativeTo nil)1237 (.setResizable false)1238 (.setVisible true)))))1240 (defn image-monitor [#^BufferedImage image]1241 (proxy [JPanel] []1242 (paintComponent1243 [g]1244 (proxy-super paintComponent g)1245 (locking image1246 (.drawImage g image 0 01247 (proxy [ImageObserver]1248 []1249 (imageUpdate1250 []1251 (proxy-super imageUpdate))))))))1253 (defn movie-image []1254 (let [setup1255 (runonce1256 (fn [#^BufferedImage image]1257 (let [width (.getWidth image)1258 height (.getHeight image)1259 display (image-monitor image)1260 frame (make-display-frame display width height)]1261 display)))]1262 (fn [#^BufferedImage image]1263 (.repaint (setup image)))))1266 (defn observer1267 "place thy eye!"1268 [world camera]1269 (let [eye camera1270 width (.getWidth eye)1271 height (.getHeight eye)]1272 (no-exceptions1273 (add-eye1274 world1275 eye1276 (movie-image)))))1277 #+end_src1279 #+srcname: test-vision1280 #+begin_src clojure1282 (ns test.vision)1283 (use 'cortex.world)1284 (use 'cortex.import)1285 (use 'clojure.contrib.def)1286 (use 'body.eye)1287 (cortex.import/mega-import-jme3)1288 (rlm.rlm-commands/help)1289 (import java.nio.ByteBuffer)1290 (import java.awt.image.BufferedImage)1291 (import java.awt.Color)1292 (import java.awt.Dimension)1293 (import java.awt.Graphics)1294 (import java.awt.Graphics2D)1295 (import java.awt.event.WindowAdapter)1296 (import java.awt.event.WindowEvent)1297 (import java.awt.image.BufferedImage)1298 (import java.nio.ByteBuffer)1299 (import javax.swing.JFrame)1300 (import javax.swing.JPanel)1301 (import javax.swing.SwingUtilities)1302 (import javax.swing.ImageIcon)1303 (import javax.swing.JOptionPane)1304 (import java.awt.image.ImageObserver)1307 (def width 200)1308 (def height 200)1310 (defn camera []1311 (doto (Camera. width height)1312 (.setFrustumPerspective 45 1 1 1000)1313 (.setLocation (Vector3f. -3 0 -5))1314 (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)))1316 (defn camera2 []1317 (doto (Camera. width height)1318 (.setFrustumPerspective 45 1 1 1000)1319 (.setLocation (Vector3f. 3 0 -5))1320 (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)))1322 (defn setup-fn [world]1323 (let [eye (camera)1324 width (.getWidth eye)1325 height (.getHeight eye)]1326 (no-exceptions1327 (add-eye1328 world1329 eye1330 (runonce visual))1331 (add-eye1332 world1333 (camera2)1334 (runonce visual)))))1336 (defn spider-eye [position]1337 (doto (Camera. 200 200 )1338 (.setFrustumPerspective 45 1 1 1000)1339 (.setLocation position)1340 (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)))1342 (defn setup-fn* [world]1343 (let [eye (camera)1344 width (.getWidth eye)1345 height (.getHeight eye)]1346 ;;(.setClearFlags (.getViewPort world) true true true)1347 (observer world (.getCamera world))1348 (observer world (spider-eye (Vector3f. 3 0 -5)))1349 ;;(observer world (spider-eye (Vector3f. 0 0 -5)))1350 ;; (observer world (spider-eye (Vector3f. -3 0 -5)))1351 ;; (observer world (spider-eye (Vector3f. 0 3 -5)))1352 ;; (observer world (spider-eye (Vector3f. 0 -3 -5)))1353 ;; (observer world (spider-eye (Vector3f. 3 3 -5)))1354 ;; (observer world (spider-eye (Vector3f. -3 3 -5)))1355 ;; (observer world (spider-eye (Vector3f. 3 -3 -5)))1356 ;; (observer world (spider-eye (Vector3f. -3 -3 -5)))1358 )1359 world)1361 (defn test-world []1362 (let [thing (box 1 1 1 :physical? false)]1363 (world1364 (doto (Node.)1365 (.attachChild thing))1366 {}1367 setup-fn1368 (fn [world tpf]1369 (.rotate thing (* tpf 0.2) 0 0)1370 ))))1373 #+end_src1376 #+results: eyes1377 : #'body.eye/test-world1379 Note the use of continuation passing style for connecting the eye to a1380 function to process the output. The example code will create two1381 videos of the same rotating cube from different angles, sutiable for1382 stereoscopic vision.1389 * COMMENT code generation1390 #+begin_src clojure :tangle ../src/cortex/import.clj1391 <<import>>1392 #+end_src1394 #+begin_src clojure :tangle ../src/hello/brick_wall.clj1395 <<brick-wall-header>>1396 <<brick-wall-body>>1397 #+end_src1399 #+begin_src clojure :tangle ../src/hello/hello_simple_app.clj1400 <<hello-simple-app>>1401 #+end_src1403 #+begin_src clojure :tangle ../src/cortex/world.clj1404 <<world-inputs>>1405 <<world>>1406 <<world-shapes>>1407 <<world-view>>1408 #+end_src1410 #+begin_src clojure :tangle ../src/cortex/other_games.clj1411 <<other-games>>1412 #+end_src1414 #+begin_src clojure :tangle ../src/hello/loop.clj1415 <<hello-loop>>1416 #+end_src1418 #+begin_src clojure :tangle ../src/hello/collision.clj1419 <<hello-collision>>1420 #+end_src1422 #+begin_src clojure :tangle ../src/hello/terrain.clj1423 <<hello-terrain>>1424 #+end_src1426 #+begin_src clojure :tangle ../src/hello/animation.clj1427 <<hello-animation>>1428 #+end_src1430 #+begin_src clojure :tangle ../src/hello/material.clj1431 <<material>>1432 #+end_src1434 #+begin_src clojure :tangle ../src/body/eye.clj1435 <<eyes>>1436 #+end_src1438 #+begin_src clojure :tangle ../src/test/vision.clj1439 <<test-vision>>1440 #+end_src