view org/cortex.org @ 21:01e1427126af

splitting cortex into more manageable pieces
author Robert McIntyre <rlm@mit.edu>
date Sun, 23 Oct 2011 13:49:45 -0700
parents c32f3eb9fdeb
children cab2da252494
line wrap: on
line source
1 #+title: Simulated Senses
2 #+author: Robert McIntyre
3 #+email: rlm@mit.edu
4 #+description: Simulating senses for AI research using JMonkeyEngine3
5 #+SETUPFILE: ../../aurellem/org/setup.org
6 #+INCLUDE: ../../aurellem/org/level-0.org
7 #+babel: :mkdirp yes :noweb yes :exports both
10 * Simulation Base
12 ** Imports
13 jMonkeyEngine has a plethora of classes which can be overwhelming at
14 first. So that I one can get right to coding, it's good to take the
15 time right now and make a "import all" function which brings in all of
16 the important jme3 classes. Once I'm happy with the general structure
17 of a namespace I can deal with importing only the classes it actually
18 needs.
20 #+srcname: import
21 #+begin_src clojure :results silent
22 (ns cortex.import
23 (:require swank.util.class-browse))
25 (defn permissive-import
26 [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 (and
34 (.startsWith classname "com.jme3.")
35 ;; Don't import the Lwjgl stuff since it can throw exceptions
36 ;; upon being loaded.
37 (not (re-matches #".*Lwjgl.*" classname))))
39 (defn jme-classes
40 "returns a list of all jme3 classes"
41 []
42 (filter
43 jme-class?
44 (map :name
45 swank.util.class-browse/available-classes)))
47 (defn mega-import-jme3
48 "Import ALL the jme classes. For REPL use."
49 []
50 (doall
51 (map (comp permissive-import symbol) (jme-classes))))
52 #+end_src
54 The =mega-import-jme3= is quite usefull for debugging purposes since
55 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 both
61 (clojure.core/count (cortex.import/jme-classes))
62 #+end_src
64 #+results:
65 : 955
67 ** Simplification
68 *** World
70 It is comvienent to wrap the JME elements that deal with creating a
71 world, creation of basic objects, and Keyboard input with a nicer
72 interface (at least for my purposes).
74 #+srcname: world-inputs
75 #+begin_src clojure :results silent
76 (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 now
87 ;;(.setAudioRenderer "Send")
88 )
89 "These settings control how the game is displayed on the screen for
90 debugging purposes. Use binding forms to change this if desired.
91 Full-screen mode does not work on some computers.")
93 (defn asset-manager
94 "returns a new, configured assetManager" []
95 (JmeSystem/newAssetManager
96 (.getResource
97 (.getContextClassLoader (Thread/currentThread))
98 "com/jme3/asset/Desktop.cfg")))
100 (defmacro no-exceptions
101 "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 (.setUncaughtExceptionHandler
108 (Thread/currentThread)
109 (proxy [Thread$UncaughtExceptionHandler] []
110 (uncaughtException
111 [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-keys
122 "Construct a map of strings representing all the manual inputs from
123 either the keyboard or mouse."
124 []
125 (let [inputs (constant-map KeyInput)]
126 (assoc
127 (zipmap (map (fn [field]
128 (.toLowerCase (re-gsub #"_" "-" field))) (vals inputs))
129 (map (fn [val] (KeyTrigger. val)) (keys inputs)))
130 ;;explicitly add mouse controls
131 "mouse-left" (MouseButtonTrigger. 0)
132 "mouse-middle" (MouseButtonTrigger. 2)
133 "mouse-right" (MouseButtonTrigger. 1))))
135 (defn initialize-inputs
136 "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-manager
140 name (into-array (class trigger) [trigger]))) key-map))
141 (doall (map (fn [name]
142 (.addListener ^InputManager input-manager game
143 (into-array String [name]))) (keys key-map))))
145 #+end_src
147 These functions are all for debug controlling of the world through
148 keyboard and mouse.
150 We reuse =constant-map= from =pokemon.lpsolve= to get the numerical
151 values for all the keys defined in the =KeyInput= class. The
152 documentation for =constant-map= is:
154 #+begin_src clojure :results output
155 (doc pokemon.lpsolve/constant-map)
156 #+end_src
158 #+results:
159 : -------------------------
160 : pokemon.lpsolve/constant-map
161 : ([class])
162 : Takes a class and creates a map of the static constant integer
163 : fields with their names. This helps with C wrappers where they have
164 : just defined a bunch of integer constants instead of enums
167 Then, =all-keys= converts the constant names like =KEY_J= to the more
168 clojure-like =key-j=, and returns a map from these keys to
169 jMonkeyEngine KeyTrigger objects, the use of which will soon become
170 apparent. =all-keys= also adds the three mouse button controls to the
171 map.
173 #+srcname: world
174 #+begin_src clojure :results silent
175 (in-ns 'cortex.world)
177 (defn traverse
178 "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 world
187 [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 (doto
194 (proxy [SimpleApplication ActionListener] []
195 (simpleInitApp
196 []
197 (no-exceptions
198 (.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 mouse
203 (org.lwjgl.input.Mouse/setGrabbed false)
204 ;; add all objects to the world
205 (.attachChild (.getRootNode this) root-node)
206 ;; enable physics
207 ;; add a physics manager
208 (.attach (.getStateManager this) physics-manager)
209 (.setGravity (.getPhysicsSpace physics-manager) gravity)
212 ;; go through every object and add it to the physics manager
213 ;; if relavant.
214 (traverse (fn [geom]
215 (dorun
216 (for [n (range (.getNumControls geom))]
217 (do
218 (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-renderer
226 (.normalizeLocal (Vector3f. -1 -1 -1)))
227 (.addProcessor (.getViewPort this) shadow-renderer)
228 (.setShadowMode (.getRootNode this) RenderQueue$ShadowMode/Off)
229 ))
230 (simpleUpdate
231 [tpf]
232 (no-exceptions
233 (update-fn this tpf)))
234 (onAction
235 [binding value tpf]
236 ;; whenever a key is pressed, call the function returned from
237 ;; key-map.
238 (no-exceptions
239 (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-map
248 "Like apply, but works for maps and functions that expect an implicit map
249 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_src
260 =world= is the most important function here.
261 *** TODO more documentation
263 #+srcname: world-shapes
264 #+begin_src clojure :results silent
265 (in-ns 'cortex.world)
266 (defrecord shape-description
267 [name
268 color
269 mass
270 friction
271 texture
272 material
273 position
274 rotation
275 shape
276 physical?])
278 (def base-shape
279 (shape-description.
280 "default-shape"
281 false
282 ;;ColorRGBA/Blue
283 1.0 ;; mass
284 1.0 ;; friction
285 ;; texture
286 "Textures/Terrain/BrickWall/BrickWall.jpg"
287 ;; material
288 "Common/MatDefs/Misc/Unshaded.j3md"
289 Vector3f/ZERO
290 Quaternion/IDENTITY
291 (Box. Vector3f/ZERO 0.5 0.5 0.5)
292 true))
294 (defn make-shape
295 [#^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 disable
312 (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 box
323 ([l w h & {:as options}]
324 (let [options (merge base-shape options)]
325 (make-shape (assoc options
326 :shape (Box. l w h)))))
327 ([] (box 0.5 0.5 0.5)))
329 (defn sphere
330 ([r & {:as options}]
331 (let [options (merge base-shape options)]
332 (make-shape (assoc options
333 :shape (Sphere. 32 32 (float r))))))
334 ([] (sphere 0.5)))
336 (defn add-element
337 ([game element node]
338 (.addAll
339 (.getPhysicsSpace
340 (.getState
341 (.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 (traverse
352 (fn [geom]
353 (if-let
354 [control (.getControl geom RigidBodyControl)]
355 (do
356 (.setGravity control gravity)
357 (.applyImpulse control Vector3f/ZERO Vector3f/ZERO)
358 )))
359 (.getRootNode game)))
361 #+end_src
363 These are convienence functions for creating JME objects and
364 manipulating a world.
366 #+srcname: world-view
367 #+begin_src clojure :results silent
368 (in-ns 'cortex.world)
370 (defprotocol Viewable
371 (view [something]))
373 (extend-type com.jme3.scene.Geometry
374 Viewable
375 (view [geo]
376 (view (doto (Node.)(.attachChild geo)))))
378 (extend-type com.jme3.scene.Node
379 Viewable
380 (view [node]
381 (.start
382 (world node
383 {}
384 (fn [world]
385 (.enableDebug
386 (.getPhysicsSpace
387 (.getState
388 (.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_src
406 Here I make the =Viewable= protocol and extend it to JME's types. Now
407 hello-world can be written as easily as:
409 #+begin_src clojure :results silent
410 (cortex.world/view (cortex.world/box))
411 #+end_src
413 ** Hello
414 Here are the jmonkeyengine "Hello" programs translated to clojure.
415 *** Hello Simple App
416 Here is the hello world example for jme3 in clojure.
417 It's a more or less direct translation from the java source
418 from
419 http://jmonkeyengine.org/wiki/doku.php/jme3:beginner:hello_simpleapplication.
421 Of note is the fact that since we don't have access to the
422 =AssetManager= via extendig =SimpleApplication=, we have to build one
423 ourselves.
425 #+srcname: hello-simple-app
426 #+begin_src clojure :results silent
427 (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 (doto
447 (proxy [SimpleApplication] []
448 (simpleInitApp
449 []
450 ;; Don't take control of the mouse
451 (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_src
459 Running this program will begin a new jMonkeyEngine game which
460 displays a single blue cube.
462 #+begin_src clojure :exports code :results silent
463 (.start (hello.hello-simple-app/simple-app))
464 #+end_src
466 #+caption: the simplest JME game.
467 [[./images/simple-app.jpg]]
471 *** Hello Physics
472 From http://jmonkeyengine.org/wiki/doku.php/jme3:beginner:hello_physics
474 #+srcname: brick-wall-header
475 #+begin_src clojure :results silent
476 (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_src
485 #+srcname: brick-wall-body
486 #+begin_src clojure :results silent
487 (in-ns 'hello.brick-wall)
489 (defn floor
490 "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-width
501 :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-wall
513 "construct a physical brick wall"
514 []
515 (let [node (Node. "brick-wall")]
516 (dorun
517 (map (comp #(.attachChild node %) brick*)
518 (for
519 [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-toggle
529 [new-value]
530 (fn [game value]
531 (println-repl "set gravity to " new-value)
532 (if value
533 (set-gravity* game new-value)
534 (set-gravity* game gravity))))
536 (defn fire-cannon-ball
537 ([node]
538 (fn [game value]
539 (if (not value)
540 (let [camera (.getCamera game)
541 cannon-ball
542 (sphere 0.7
543 :material "Common/MatDefs/Misc/Unshaded.j3md"
544 :texture "Textures/PokeCopper.jpg"
545 :position
546 (.add (.getLocation camera)
547 (.mult (.getDirection camera) (float 1)))
548 :mass 3)] ;200 0.05
549 (.setShadowMode cannon-ball RenderQueue$ShadowMode/CastAndReceive)
550 (.setLinearVelocity
551 (.getControl cannon-ball RigidBodyControl)
552 (.mult (.getDirection camera) (float 50))) ;50
553 (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 240
560 :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 64
567 (->
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 (dorun
578 (map
579 (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 (doto
594 (world
595 (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-camera
609 (fn [& _]))
610 (.start)))
611 #+end_src
613 #+begin_src clojure :results silent
614 (hello.brick-wall/brick-wall-game-run)
615 #+end_src
617 #+caption: the brick wall standing
618 [[./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 Games
624 #+srcname: other-games
625 #+begin_src clojure :results silent
626 (ns cortex.other-games
627 {: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.1
635 :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 (dorun
654 (map
655 (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 domino
676 ([position]
677 (domino position (Quaternion/IDENTITY)))
678 ([position rotation]
679 (doto (box domino-width domino-height domino-thickness
680 :position position :name "domino"
681 :material "Common/MatDefs/Misc/Unshaded.j3md"
682 :texture "Textures/Terrain/BrickWall/BrickWall.jpg"
683 :mass 1
684 :rotation rotation)
685 (.setShadowMode RenderQueue$ShadowMode/CastAndReceive)
686 )))
689 (defn domino-row []
690 (let [node (Node. "domino-row")]
691 (dorun
692 (map
693 (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 (dorun
708 (map
709 (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] (reduce
714 +
715 (map
716 (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 (list
724 (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 (doto
737 (world
738 (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-camera
760 (fn [& _]))
761 (.start)))
762 #+end_src
764 #+begin_src clojure :results silent
765 (cortex.other-games/domino-game-run)
766 #+end_src
768 #+caption: floating dominos
769 [[./images/dominos.jpg]]
771 *** Hello Loop
772 #+srcname: hello-loop
773 #+begin_src clojure :results silent
774 (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 1
782 :color ColorRGBA/Blue
783 :texture false
784 :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 root
792 {}
793 no-op
794 (fn [game tpf]
795 (.rotate cube 0.0 (* 2 tpf) 0.0)))))
796 #+end_src
798 *** Hello Collision
800 #+srcname: hello-collision
801 #+begin_src clojure :results silent
802 (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 (let
815 [scene-model
816 (doto
817 (.loadModel
818 (doto (asset-manager)
819 (.registerLocator
820 "/home/r/cortex/assets/zips/town.zip" ZipLocator))
821 "main.scene")
822 (.setLocalScale (float 2.0)))
823 collision-shape
824 (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 (doto
832 (CharacterControl.
833 (CapsuleCollisionShape. (float 1.5) (float 6)(float 1))
834 (float 0.05))
835 (.setJumpSpeed 20)
836 (.setFallSpeed 30)
837 (.setGravity 30) ;30
838 (.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 sky
858 (.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 (.add
864 (.getPhysicsSpace
865 (.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-fn
894 [game tpf]
895 (let [camera (.getCamera game)
896 cam-dir (.multLocal
897 (.clone
898 (.getDirection camera)) (float 0.6))
899 cam-left (.multLocal
900 (.clone
901 (.getLeft camera)) (float 0.4))
902 walk-direction (Vector3f. 0 0 0)]
904 (cond
905 @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 (.start
914 (world (environment)
915 (responses)
916 setup-fn
917 update-fn)))
918 #+end_src
920 *** Hello Terrain
921 #+srcname: hello-terrain
922 #+begin_src clojure :results silent
923 (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 (let
946 [mat_terrain
947 (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_terrain
965 (.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 (.loadTexture
976 (asset-manager)
977 ({:mountain "Textures/Terrain/splat/alphamap.png"
978 :fortress "Textures/Terrain/splat/alphamap2.png"} type))))
980 (doto terrain
981 (.setMaterial mat_terrain)
982 (.setLocalTranslation 0 -100 0)
983 (.setLocalScale 2 1 2))))
987 (defn run-terrain-game [type]
988 (.start
989 (world
990 (Node.)
991 {}
992 (partial setup-fn type)
993 no-op)))
994 #+end_src
998 #+srcname: hello-animation
999 #+begin_src clojure :results silent
1000 (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 stand
1009 [channel]
1010 (doto channel
1011 (.setAnim "stand" (float 0.5))
1012 (.setLoopMode LoopMode/DontLoop)
1013 (.setSpeed (float 1))))
1015 (defn anim-listener []
1016 (proxy [AnimEventListener] []
1017 (onAnimChange
1018 [control channel animation-name]
1019 (println-repl "RLM --- onAnimChange"))
1020 (onAnimCycleDone
1021 [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 sky
1029 (.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 channel
1039 (.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 (.start
1063 (world
1064 ninja
1065 (key-map channel)
1066 (partial setup-fn channel)
1067 no-op))))
1068 #+end_src
1070 *** Hello Materials
1071 #+srcname: material
1072 #+begin_src clojure :results silent
1073 (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 1
1082 :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 1
1089 :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 (doto
1097 (box 1 1 0.1
1098 :position Vector3f/ZERO
1099 :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 (doto
1110 (sphere 2
1111 :position (Vector3f. 0 2 -2)
1112 :name "Shiny rock"
1113 :material "Common/MatDefs/Light/Lighting.j3md"
1114 :texture false
1115 :physical? false)
1116 (-> (.getMesh)
1117 (doto
1118 (.setTextureMode Sphere$TextureMode/Projected)
1119 (TangentBinormalGenerator/generate)))
1120 (-> (.getMaterial)
1121 (doto
1122 (.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 (.start
1132 (world
1133 (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-op
1144 )))
1145 #+end_src
1149 * The Body
1150 ** Eyes
1152 Ultimately I want to make creatures with eyes. Each eye can be
1153 independely moved and should see its own version of the world
1154 depending on where it is.
1155 #+srcname: eyes
1156 #+begin_src clojure
1157 (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-processor
1183 "deals with converting FrameBuffers to BufferedImages so
1184 that the continuation function can be defined only in terms
1185 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 (initialize
1192 [renderManager viewPort]
1193 (let [cam (.getCamera viewPort)
1194 width (.getWidth cam)
1195 height (.getHeight cam)]
1196 (reset! renderer (.getRenderer renderManager))
1197 (reset! byte-buffer
1198 (BufferUtils/createByteBuffer
1199 (* width height 4)))
1200 (reset! image (BufferedImage. width height
1201 BufferedImage/TYPE_4BYTE_ABGR))))
1202 (isInitialized [] (not (nil? @byte-buffer)))
1203 (reshape [_ _ _])
1204 (preFrame [_])
1205 (postQueue [_])
1206 (postFrame
1207 [#^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-eye
1215 "Add an eye to the world, and call continuation on
1216 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 viewport
1223 (.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/invokeLater
1230 (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 (paintComponent
1243 [g]
1244 (proxy-super paintComponent g)
1245 (locking image
1246 (.drawImage g image 0 0
1247 (proxy [ImageObserver]
1248 []
1249 (imageUpdate
1250 []
1251 (proxy-super imageUpdate))))))))
1253 (defn movie-image []
1254 (let [setup
1255 (runonce
1256 (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 observer
1267 "place thy eye!"
1268 [world camera]
1269 (let [eye camera
1270 width (.getWidth eye)
1271 height (.getHeight eye)]
1272 (no-exceptions
1273 (add-eye
1274 world
1275 eye
1276 (movie-image)))))
1277 #+end_src
1279 #+srcname: test-vision
1280 #+begin_src clojure
1282 (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-exceptions
1327 (add-eye
1328 world
1329 eye
1330 (runonce visual))
1331 (add-eye
1332 world
1333 (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)))
1359 world)
1361 (defn test-world []
1362 (let [thing (box 1 1 1 :physical? false)]
1363 (world
1364 (doto (Node.)
1365 (.attachChild thing))
1366 {}
1367 setup-fn
1368 (fn [world tpf]
1369 (.rotate thing (* tpf 0.2) 0 0)
1370 ))))
1373 #+end_src
1376 #+results: eyes
1377 : #'body.eye/test-world
1379 Note the use of continuation passing style for connecting the eye to a
1380 function to process the output. The example code will create two
1381 videos of the same rotating cube from different angles, sutiable for
1382 stereoscopic vision.
1389 * COMMENT code generation
1390 #+begin_src clojure :tangle ../src/cortex/import.clj
1391 <<import>>
1392 #+end_src
1394 #+begin_src clojure :tangle ../src/hello/brick_wall.clj
1395 <<brick-wall-header>>
1396 <<brick-wall-body>>
1397 #+end_src
1399 #+begin_src clojure :tangle ../src/hello/hello_simple_app.clj
1400 <<hello-simple-app>>
1401 #+end_src
1403 #+begin_src clojure :tangle ../src/cortex/world.clj
1404 <<world-inputs>>
1405 <<world>>
1406 <<world-shapes>>
1407 <<world-view>>
1408 #+end_src
1410 #+begin_src clojure :tangle ../src/cortex/other_games.clj
1411 <<other-games>>
1412 #+end_src
1414 #+begin_src clojure :tangle ../src/hello/loop.clj
1415 <<hello-loop>>
1416 #+end_src
1418 #+begin_src clojure :tangle ../src/hello/collision.clj
1419 <<hello-collision>>
1420 #+end_src
1422 #+begin_src clojure :tangle ../src/hello/terrain.clj
1423 <<hello-terrain>>
1424 #+end_src
1426 #+begin_src clojure :tangle ../src/hello/animation.clj
1427 <<hello-animation>>
1428 #+end_src
1430 #+begin_src clojure :tangle ../src/hello/material.clj
1431 <<material>>
1432 #+end_src
1434 #+begin_src clojure :tangle ../src/body/eye.clj
1435 <<eyes>>
1436 #+end_src
1438 #+begin_src clojure :tangle ../src/test/vision.clj
1439 <<test-vision>>
1440 #+end_src