view org/test-creature.org @ 92:e70ec4bba96b

saving progress...
author Robert McIntyre <rlm@mit.edu>
date Mon, 09 Jan 2012 06:05:29 -0700
parents 2bcc7636cfea
children 7b739503836a
line wrap: on
line source
1 #+title: First attempt at a creature!
2 #+author: Robert McIntyre
3 #+email: rlm@mit.edu
4 #+description:
5 #+keywords: simulation, jMonkeyEngine3, clojure
6 #+SETUPFILE: ../../aurellem/org/setup.org
7 #+INCLUDE: ../../aurellem/org/level-0.org
9 * Intro
10 So far, I've made the following senses --
11 - Vision
12 - Hearing
13 - Touch
14 - Proprioception
16 And one effector:
17 - Movement
19 However, the code so far has only enabled these senses, but has not
20 actually implemented them. For example, there is still a lot of work
21 to be done for vision. I need to be able to create an /eyeball/ in
22 simulation that can be moved around and see the world from different
23 angles. I also need to determine weather to use log-polar or cartesian
24 for the visual input, and I need to determine how/wether to
25 disceritise the visual input.
27 I also want to be able to visualize both the sensors and the
28 effectors in pretty pictures. This semi-retarted creature will by my
29 first attempt at bringing everything together.
31 * The creature's body
33 Still going to do an eve-like body in blender, but due to problems
34 importing the joints, etc into jMonkeyEngine3, I',m going to do all
35 the connecting here in clojure code, using the names of the individual
36 components and trial and error. Later, I'll maybe make some sort of
37 creature-building modifications to blender that support whatever
38 discreitized senses I'm going to make.
40 #+name: body-1
41 #+begin_src clojure
42 (ns cortex.silly
43 "let's play!"
44 {:author "Robert McIntyre"})
46 ;; TODO remove this!
47 (require 'cortex.import)
48 (cortex.import/mega-import-jme3)
49 (use '(cortex world util body hearing touch vision))
51 (rlm.rlm-commands/help)
53 (declare joint-create)
55 (defn load-bullet []
56 (let [sim (world (Node.) {} no-op no-op)]
57 (.enqueue
58 sim
59 (fn []
60 (.stop sim)))
61 (.start sim)))
63 (defn load-blender-model
64 "Load a .blend file using an asset folder relative path."
65 [^String model]
66 (.loadModel
67 (doto (asset-manager)
68 (.registerLoader BlenderModelLoader (into-array String ["blend"])))
69 model))
71 (defn meta-data [blender-node key]
72 (if-let [data (.getUserData blender-node "properties")]
73 (.findValue data key)
74 nil))
76 (defn blender-to-jme
77 "Convert from Blender coordinates to JME coordinates"
78 [#^Vector3f in]
79 (Vector3f. (.getX in)
80 (.getZ in)
81 (- (.getY in))))
83 (defn jme-to-blender
84 "Convert from JME coordinates to Blender coordinates"
85 [#^Vector3f in]
86 (Vector3f. (.getX in)
87 (- (.getZ in))
88 (.getY in)))
90 (defn joint-targets
91 "Return the two closest two objects to the joint object, ordered
92 from bottom to top according to the joint's rotation."
93 [#^Node parts #^Node joint]
94 ;;(println (meta-data joint "joint"))
95 (.getWorldRotation joint)
96 (loop [radius (float 0.01)]
97 (let [results (CollisionResults.)]
98 (.collideWith
99 parts
100 (BoundingBox. (.getWorldTranslation joint)
101 radius radius radius)
102 results)
103 (let [targets
104 (distinct
105 (map #(.getGeometry %) results))]
106 (if (>= (count targets) 2)
107 (sort-by
108 #(let [v
109 (jme-to-blender
110 (.mult
111 (.inverse (.getWorldRotation joint))
112 (.subtract (.getWorldTranslation %)
113 (.getWorldTranslation joint))))]
114 (println-repl (.getName %) ":" v)
115 (.dot (Vector3f. 1 1 1)
116 v))
117 (take 2 targets))
118 (recur (float (* radius 2))))))))
120 (defn world-to-local
121 "Convert the world coordinates into coordinates relative to the
122 object (i.e. local coordinates), taking into account the rotation
123 of object."
124 [#^Spatial object world-coordinate]
125 (let [out (Vector3f.)]
126 (.worldToLocal object world-coordinate out) out))
128 (defmulti joint-dispatch
129 "Translate blender pseudo-joints into real JME joints."
130 (fn [constraints & _]
131 (:type constraints)))
133 (defmethod joint-dispatch :point
134 [constraints control-a control-b pivot-a pivot-b rotation]
135 (println-repl "creating POINT2POINT joint")
136 (Point2PointJoint.
137 control-a
138 control-b
139 pivot-a
140 pivot-b))
142 (defmethod joint-dispatch :hinge
143 [constraints control-a control-b pivot-a pivot-b rotation]
144 (println-repl "creating HINGE joint")
145 (let [axis
146 (if-let
147 [axis (:axis constraints)]
148 axis
149 Vector3f/UNIT_X)
150 [limit-1 limit-2] (:limit constraints)
151 hinge-axis
152 (.mult
153 rotation
154 (blender-to-jme axis))]
155 (doto
156 (HingeJoint.
157 control-a
158 control-b
159 pivot-a
160 pivot-b
161 hinge-axis
162 hinge-axis)
163 (.setLimit limit-1 limit-2))))
165 (defmethod joint-dispatch :cone
166 [constraints control-a control-b pivot-a pivot-b rotation]
167 (let [limit-xz (:limit-xz constraints)
168 limit-xy (:limit-xy constraints)
169 twist (:twist constraints)]
171 (println-repl "creating CONE joint")
172 (println-repl rotation)
173 (println-repl
174 "UNIT_X --> " (.mult rotation (Vector3f. 1 0 0)))
175 (println-repl
176 "UNIT_Y --> " (.mult rotation (Vector3f. 0 1 0)))
177 (println-repl
178 "UNIT_Z --> " (.mult rotation (Vector3f. 0 0 1)))
179 (doto
180 (ConeJoint.
181 control-a
182 control-b
183 pivot-a
184 pivot-b
185 rotation
186 rotation)
187 (.setLimit (float limit-xz)
188 (float limit-xy)
189 (float twist)))))
191 (defn connect
192 "here are some examples:
193 {:type :point}
194 {:type :hinge :limit [0 (/ Math/PI 2)] :axis (Vector3f. 0 1 0)}
195 (:axis defaults to (Vector3f. 1 0 0) if not provided for hinge joints)
197 {:type :cone :limit-xz 0]
198 :limit-xy 0]
199 :twist 0]} (use XZY rotation mode in blender!)"
200 [#^Node obj-a #^Node obj-b #^Node joint]
201 (let [control-a (.getControl obj-a RigidBodyControl)
202 control-b (.getControl obj-b RigidBodyControl)
203 joint-center (.getWorldTranslation joint)
204 joint-rotation (.toRotationMatrix (.getWorldRotation joint))
205 pivot-a (world-to-local obj-a joint-center)
206 pivot-b (world-to-local obj-b joint-center)]
208 (if-let [constraints
209 (map-vals
210 eval
211 (read-string
212 (meta-data joint "joint")))]
213 ;; A side-effect of creating a joint registers
214 ;; it with both physics objects which in turn
215 ;; will register the joint with the physics system
216 ;; when the simulation is started.
217 (do
218 (println-repl "creating joint between"
219 (.getName obj-a) "and" (.getName obj-b))
220 (joint-dispatch constraints
221 control-a control-b
222 pivot-a pivot-b
223 joint-rotation))
224 (println-repl "could not find joint meta-data!"))))
226 (defn assemble-creature [#^Node pieces joints]
227 (dorun
228 (map
229 (fn [geom]
230 (let [physics-control
231 (RigidBodyControl.
232 (HullCollisionShape.
233 (.getMesh geom))
234 (if-let [mass (meta-data geom "mass")]
235 (do
236 (println-repl
237 "setting" (.getName geom) "mass to" (float mass))
238 (float mass))
239 (float 1)))]
241 (.addControl geom physics-control)))
242 (filter #(isa? (class %) Geometry )
243 (node-seq pieces))))
245 (dorun
246 (map
247 (fn [joint]
248 (let [[obj-a obj-b]
249 (joint-targets pieces joint)]
250 (connect obj-a obj-b joint)))
251 joints))
252 pieces)
254 (defn blender-creature [blender-path]
255 (let [model (load-blender-model blender-path)
256 joints
257 (if-let [joint-node (.getChild model "joints")]
258 (seq (.getChildren joint-node))
259 (do (println-repl "could not find joints node")
260 []))]
261 (assemble-creature model joints)))
263 (def hand "Models/creature1/one.blend")
265 (def worm "Models/creature1/try-again.blend")
267 (def touch "Models/creature1/touch.blend")
269 (defn worm-model [] (load-blender-model worm))
271 (defn x-ray [#^ColorRGBA color]
272 (doto (Material. (asset-manager)
273 "Common/MatDefs/Misc/Unshaded.j3md")
274 (.setColor "Color" color)
275 (-> (.getAdditionalRenderState)
276 (.setDepthTest false))))
278 (defn test-creature [thing]
279 (let [x-axis
280 (box 1 0.01 0.01 :physical? false :color ColorRGBA/Red)
281 y-axis
282 (box 0.01 1 0.01 :physical? false :color ColorRGBA/Green)
283 z-axis
284 (box 0.01 0.01 1 :physical? false :color ColorRGBA/Blue)]
285 (world
286 (nodify [(blender-creature thing)
287 (box 10 2 10 :position (Vector3f. 0 -9 0)
288 :color ColorRGBA/Gray :mass 0)
289 x-axis y-axis z-axis
290 ])
291 standard-debug-controls
292 (fn [world]
293 (light-up-everything world)
294 (enable-debug world)
295 ;;(com.aurellem.capture.Capture/captureVideo
296 ;; world (file-str "/home/r/proj/ai-videos/hand"))
297 (.setTimer world (NanoTimer.))
298 ;;(set-gravity world (Vector3f. 0 0 0))
299 (speed-up world)
300 )
301 no-op
302 ;;(let [timer (atom 0)]
303 ;; (fn [_ _]
304 ;; (swap! timer inc)
305 ;; (if (= (rem @timer 60) 0)
306 ;; (println-repl (float (/ @timer 60))))))
307 )))
310 (defn colorful []
311 (.getChild (worm-model) "worm-21"))
313 (import jme3tools.converters.ImageToAwt)
315 (import ij.ImagePlus)
317 (defn triangle-indices
318 "Get the triangle vertex indices of a given triangle from a given
319 mesh."
320 [#^Mesh mesh triangle-index]
321 (let [indices (int-array 3)]
322 (.getTriangle mesh triangle-index indices)
323 (vec indices)))
325 (defn uv-coord
326 "Get the uv-coordinates of the vertex named by vertex-index"
327 [#^Mesh mesh vertex-index]
328 (let [UV-buffer
329 (.getData
330 (.getBuffer
331 mesh
332 VertexBuffer$Type/TexCoord))]
333 (Vector2f.
334 (.get UV-buffer (* vertex-index 2))
335 (.get UV-buffer (+ 1 (* vertex-index 2))))))
337 (defn touch-receptor-image
338 "Return the touch-sensor distribution image in ImagePlus format."
339 [#^Geometry obj]
340 (let
341 [mat (.getMaterial obj)
342 texture
343 (.getTextureValue
344 (.getTextureParam
345 mat
346 MaterialHelper/TEXTURE_TYPE_DIFFUSE))
347 im (.getImage texture)]
348 (ImagePlus.
349 "UV-map"
350 (ImageToAwt/convert im false false 0))))
353 (import ij.process.ImageProcessor)
354 (import java.awt.image.BufferedImage)
356 (defprotocol Frame
357 (frame [this]))
359 (extend-type BufferedImage
360 Frame
361 (frame [image]
362 (merge
363 (apply
364 hash-map
365 (interleave
366 (doall (for [x (range (.getWidth image)) y (range (.getHeight image))]
367 (vector x y)))
368 (doall (for [x (range (.getWidth image)) y (range (.getHeight image))]
369 (let [data (.getRGB image x y)]
370 (hash-map :r (bit-shift-right (bit-and 0xff0000 data) 16)
371 :g (bit-shift-right (bit-and 0x00ff00 data) 8)
372 :b (bit-and 0x0000ff data)))))))
373 {:width (.getWidth image) :height (.getHeight image)})))
376 (extend-type ImagePlus
377 Frame
378 (frame [image+]
379 (frame (.getBufferedImage image+))))
381 (def white -1)
383 (defn filter-pixels
384 "List the coordinates of all pixels matching pred."
385 {:author "Dylan Holmes"}
386 [pred #^ImageProcessor ip]
387 (let
388 [width (.getWidth ip)
389 height (.getHeight ip)]
390 ((fn accumulate [x y matches]
391 (cond
392 (>= y height) matches
393 (>= x width) (recur 0 (inc y) matches)
394 (pred (.getPixel ip x y))
395 (recur (inc x) y (conj matches (Vector2f. x y)))
396 :else (recur (inc x) y matches)))
397 0 0 [])))
399 (defn white-coordinates
400 "List the coordinates of all the white pixels in an image."
401 [#^ImageProcessor ip]
402 (filter-pixels #(= % white) ip))
404 (defn same-side? [p1 p2 ref p]
405 (<=
406 0
407 (.dot
408 (.cross (.subtract p2 p1) (.subtract p p1))
409 (.cross (.subtract p2 p1) (.subtract ref p1)))))
411 (defn inside-triangle?
412 [vert-1 vert-2 vert-3 p]
413 (and
414 (same-side? vert-1 vert-2 vert-3 p)
415 (same-side? vert-2 vert-3 vert-1 p)
416 (same-side? vert-3 vert-1 vert-2 p)))
419 (defn white? [color]
420 (and
421 (= (:r color) 255)
422 (= (:b color) 255)
423 (= (:g color) 255)))
426 ;; for each triangle in the mesh,
427 ;; get the normal to the triangle,
428 ;; look at the UV touch map, restricted to that triangle,
429 ;; get the positions of those touch sensors in geometry-relative
430 ;; coordinates.
431 (defn tactile-coords [#^Geometry obj]
432 (let [mesh (.getMesh obj)
433 num-triangles (.getTriangleCount mesh)
434 num-verticies (.getVertexCount mesh)
435 uv-coord (partial uv-coord mesh)
436 triangle-indices (partial triangle-indices mesh)
437 receptors (touch-receptor-image obj)
438 ]
439 (map
440 (fn [[tri-1 tri-2 tri-3]]
441 (let [width (.getWidth receptors)
442 height (.getHeight receptors)
443 uv-1 (uv-coord tri-1)
444 uv-2 (uv-coord tri-2)
445 uv-3 (uv-coord tri-3)
446 x-coords (map #(.getX %) [uv-1 uv-2 uv-3])
447 y-coords (map #(.getY %) [uv-1 uv-2 uv-3])
448 max-x (Math/ceil (* width (apply max x-coords)))
449 min-x (Math/floor (* width (apply min x-coords)))
450 max-y (Math/ceil (* height (apply max y-coords)))
451 min-y (Math/floor (* height (apply min y-coords)))
453 image-1 (Vector2f. (* width (.getX uv-1))
454 (* height (.getY uv-1)))
455 image-2 (Vector2f. (* width (.getX uv-2))
456 (* height (.getY uv-2)))
457 image-3 (Vector2f. (* width (.getX uv-3))
458 (* height (.getY uv-3)))
459 left-corner
460 (Vector2f. min-x min-y)
462 ]
464 (.setRoi receptors min-x min-y (- max-x min-x) (- max-y min-y))
465 (let [processor (.crop (.getProcessor receptors))
466 image (frame (.getBufferedImage processor))]
467 (with-meta
468 (filter-keys
469 (fn [[x y]]
470 (inside-triangle?
471 (.subtract image-1 left-corner)
472 (.subtract image-2 left-corner)
473 (.subtract image-3 left-corner)
474 (Vector2f. x y)))
477 (filter-vals white? image))
478 {:image
479 (comment
480 (.getBufferedImage
481 (doto processor
482 (.flipVertical))))
483 }
484 ))
485 )) (map triangle-indices (range num-triangles)))))
493 (defn all-names []
494 (concat
495 (re-split #"\n" (slurp (file-str
496 "/home/r/proj/names/dist.female.first")))
497 (re-split #"\n" (slurp (file-str
498 "/home/r/proj/names/dist.male.first")))
499 (re-split #"\n" (slurp (file-str
500 "/home/r/proj/names/dist.all.last")))))
510 (defrecord LulzLoader [])
511 (defprotocol Lulzable (load-lulz [this]))
512 (extend-type LulzLoader
513 Lulzable
514 (load-lulz [this] (println "the lulz have arrived!")))
517 (defn world-setup [joint]
518 (let [joint-position (Vector3f. 0 0 0)
519 joint-rotation
520 (.toRotationMatrix
521 (.mult
522 (doto (Quaternion.)
523 (.fromAngleAxis
524 (* 1 (/ Math/PI 4))
525 (Vector3f. -1 0 0)))
526 (doto (Quaternion.)
527 (.fromAngleAxis
528 (* 1 (/ Math/PI 2))
529 (Vector3f. 0 0 1)))))
530 top-position (.mult joint-rotation (Vector3f. 8 0 0))
532 origin (doto
533 (sphere 0.1 :physical? false :color ColorRGBA/Cyan
534 :position top-position))
535 top (doto
536 (sphere 0.1 :physical? false :color ColorRGBA/Yellow
537 :position top-position)
539 (.addControl
540 (RigidBodyControl.
541 (CapsuleCollisionShape. 0.5 1.5 1) (float 20))))
542 bottom (doto
543 (sphere 0.1 :physical? false :color ColorRGBA/DarkGray
544 :position (Vector3f. 0 0 0))
545 (.addControl
546 (RigidBodyControl.
547 (CapsuleCollisionShape. 0.5 1.5 1) (float 0))))
548 table (box 10 2 10 :position (Vector3f. 0 -20 0)
549 :color ColorRGBA/Gray :mass 0)
550 a (.getControl top RigidBodyControl)
551 b (.getControl bottom RigidBodyControl)]
553 (cond
554 (= joint :cone)
556 (doto (ConeJoint.
557 a b
558 (world-to-local top joint-position)
559 (world-to-local bottom joint-position)
560 joint-rotation
561 joint-rotation
562 )
565 (.setLimit (* (/ 10) Math/PI)
566 (* (/ 4) Math/PI)
567 0)))
568 [origin top bottom table]))
570 (defn test-joint [joint]
571 (let [[origin top bottom floor] (world-setup joint)
572 control (.getControl top RigidBodyControl)
573 move-up? (atom false)
574 move-down? (atom false)
575 move-left? (atom false)
576 move-right? (atom false)
577 roll-left? (atom false)
578 roll-right? (atom false)
579 timer (atom 0)]
581 (world
582 (nodify [top bottom floor origin])
583 (merge standard-debug-controls
584 {"key-r" (fn [_ pressed?] (reset! move-up? pressed?))
585 "key-t" (fn [_ pressed?] (reset! move-down? pressed?))
586 "key-f" (fn [_ pressed?] (reset! move-left? pressed?))
587 "key-g" (fn [_ pressed?] (reset! move-right? pressed?))
588 "key-v" (fn [_ pressed?] (reset! roll-left? pressed?))
589 "key-b" (fn [_ pressed?] (reset! roll-right? pressed?))})
591 (fn [world]
592 (light-up-everything world)
593 (enable-debug world)
594 (set-gravity world (Vector3f. 0 0 0))
595 )
597 (fn [world _]
598 (if (zero? (rem (swap! timer inc) 100))
599 (do
600 ;; (println-repl @timer)
601 (.attachChild (.getRootNode world)
602 (sphere 0.05 :color ColorRGBA/Yellow
603 :position (.getWorldTranslation top)
604 :physical? false))
605 (.attachChild (.getRootNode world)
606 (sphere 0.05 :color ColorRGBA/LightGray
607 :position (.getWorldTranslation bottom)
608 :physical? false))))
610 (if @move-up?
611 (.applyTorque control
612 (.mult (.getPhysicsRotation control)
613 (Vector3f. 0 0 10))))
614 (if @move-down?
615 (.applyTorque control
616 (.mult (.getPhysicsRotation control)
617 (Vector3f. 0 0 -10))))
618 (if @move-left?
619 (.applyTorque control
620 (.mult (.getPhysicsRotation control)
621 (Vector3f. 0 10 0))))
622 (if @move-right?
623 (.applyTorque control
624 (.mult (.getPhysicsRotation control)
625 (Vector3f. 0 -10 0))))
626 (if @roll-left?
627 (.applyTorque control
628 (.mult (.getPhysicsRotation control)
629 (Vector3f. -1 0 0))))
630 (if @roll-right?
631 (.applyTorque control
632 (.mult (.getPhysicsRotation control)
633 (Vector3f. 1 0 0))))))))
637 #+end_src
639 #+results: body-1
640 : #'cortex.silly/test-joint
643 * COMMENT purgatory
644 #+begin_src clojure
645 (defn bullet-trans []
646 (let [obj-a (sphere 0.5 :color ColorRGBA/Red
647 :position (Vector3f. -10 5 0))
648 obj-b (sphere 0.5 :color ColorRGBA/Blue
649 :position (Vector3f. -10 -5 0)
650 :mass 0)
651 control-a (.getControl obj-a RigidBodyControl)
652 control-b (.getControl obj-b RigidBodyControl)
653 swivel
654 (.toRotationMatrix
655 (doto (Quaternion.)
656 (.fromAngleAxis (/ Math/PI 2)
657 Vector3f/UNIT_X)))]
658 (doto
659 (ConeJoint.
660 control-a control-b
661 (Vector3f. 0 5 0)
662 (Vector3f. 0 -5 0)
663 swivel swivel)
664 (.setLimit (* 0.6 (/ Math/PI 4))
665 (/ Math/PI 4)
666 (* Math/PI 0.8)))
667 (world (nodify
668 [obj-a obj-b])
669 standard-debug-controls
670 enable-debug
671 no-op)))
674 (defn bullet-trans* []
675 (let [obj-a (box 1.5 0.5 0.5 :color ColorRGBA/Red
676 :position (Vector3f. 5 0 0)
677 :mass 90)
678 obj-b (sphere 0.5 :color ColorRGBA/Blue
679 :position (Vector3f. -5 0 0)
680 :mass 0)
681 control-a (.getControl obj-a RigidBodyControl)
682 control-b (.getControl obj-b RigidBodyControl)
683 move-up? (atom nil)
684 move-down? (atom nil)
685 move-left? (atom nil)
686 move-right? (atom nil)
687 roll-left? (atom nil)
688 roll-right? (atom nil)
689 force 100
690 swivel
691 (.toRotationMatrix
692 (doto (Quaternion.)
693 (.fromAngleAxis (/ Math/PI 2)
694 Vector3f/UNIT_X)))
695 x-move
696 (doto (Matrix3f.)
697 (.fromStartEndVectors Vector3f/UNIT_X
698 (.normalize (Vector3f. 1 1 0))))
700 timer (atom 0)]
701 (doto
702 (ConeJoint.
703 control-a control-b
704 (Vector3f. -8 0 0)
705 (Vector3f. 2 0 0)
706 ;;swivel swivel
707 ;;Matrix3f/IDENTITY Matrix3f/IDENTITY
708 x-move Matrix3f/IDENTITY
709 )
710 (.setCollisionBetweenLinkedBodys false)
711 (.setLimit (* 1 (/ Math/PI 4)) ;; twist
712 (* 1 (/ Math/PI 4)) ;; swing span in X-Y plane
713 (* 0 (/ Math/PI 4)))) ;; swing span in Y-Z plane
714 (world (nodify
715 [obj-a obj-b])
716 (merge standard-debug-controls
717 {"key-r" (fn [_ pressed?] (reset! move-up? pressed?))
718 "key-t" (fn [_ pressed?] (reset! move-down? pressed?))
719 "key-f" (fn [_ pressed?] (reset! move-left? pressed?))
720 "key-g" (fn [_ pressed?] (reset! move-right? pressed?))
721 "key-v" (fn [_ pressed?] (reset! roll-left? pressed?))
722 "key-b" (fn [_ pressed?] (reset! roll-right? pressed?))})
724 (fn [world]
725 (enable-debug world)
726 (set-gravity world Vector3f/ZERO)
727 )
729 (fn [world _]
731 (if @move-up?
732 (.applyForce control-a
733 (Vector3f. force 0 0)
734 (Vector3f. 0 0 0)))
735 (if @move-down?
736 (.applyForce control-a
737 (Vector3f. (- force) 0 0)
738 (Vector3f. 0 0 0)))
739 (if @move-left?
740 (.applyForce control-a
741 (Vector3f. 0 force 0)
742 (Vector3f. 0 0 0)))
743 (if @move-right?
744 (.applyForce control-a
745 (Vector3f. 0 (- force) 0)
746 (Vector3f. 0 0 0)))
748 (if @roll-left?
749 (.applyForce control-a
750 (Vector3f. 0 0 force)
751 (Vector3f. 0 0 0)))
752 (if @roll-right?
753 (.applyForce control-a
754 (Vector3f. 0 0 (- force))
755 (Vector3f. 0 0 0)))
757 (if (zero? (rem (swap! timer inc) 100))
758 (.attachChild
759 (.getRootNode world)
760 (sphere 0.05 :color ColorRGBA/Yellow
761 :physical? false :position
762 (.getWorldTranslation obj-a)))))
763 )
764 ))
768 #+end_src
771 * COMMENT generate source
772 #+begin_src clojure :tangle ../src/cortex/silly.clj
773 <<body-1>>
774 #+end_src