view org/body.org @ 202:d5c597a7aed4

writing prose for body.org
author Robert McIntyre <rlm@mit.edu>
date Wed, 08 Feb 2012 05:50:15 -0700
parents 026f69582022
children 0e5d5ee5a914
line wrap: on
line source
1 #+title: Building a Body
2 #+author: Robert McIntyre
3 #+email: rlm@mit.edu
4 #+description: Simulating a body (movement, touch, propioception) in jMonkeyEngine3.
5 #+SETUPFILE: ../../aurellem/org/setup.org
6 #+INCLUDE: ../../aurellem/org/level-0.org
9 * Design Constraints
11 I use [[www.blender.org/][blender]] to design bodies. The design of the bodies is
12 determined by the requirements of the AI that will use them. The
13 bodies must be easy for an AI to sense and control, and they must be
14 relatively simple for jMonkeyEngine to compute.
16 ** Bag of Bones
18 How to create such a body? One option I ultimately rejected is to use
19 blender's [[http://wiki.blender.org/index.php/Doc:2.6/Manual/Rigging/Armatures][armature]] system. The idea would have been to define a mesh
20 which describes the creature's entire body. To this you add an
21 (skeleton) which deforms this mesh. This technique is used extensively
22 to model humans and create realistic animations. It is hard to use for
23 my purposes because it is difficult to update the creature's Physics
24 Collision Mesh in tandem with its Geometric Mesh under the influence
25 of the armature. Withouth this the creature will not be able to grab
26 things in its environment, and it won't be able to tell where its
27 physical body is by using its eyes. Also, armatures do not specify
28 any rotational limits for a joint, making it hard to model elbows,
29 shoulders, etc.
31 ** EVE
33 Instead of using the human-like "deformable bag of bones" approach, I
34 decided to base my body plans on the robot EVE from the movie wall-E.
36 #+caption: EVE from the movie WALL-E. This body plan turns out to be much better suited to my purposes than a more human-like one.
37 [[../images/Eve.jpg]]
39 The main reason that I use eve-style bodies is so that there will be
40 correspondence between the AI's vision and the physical presence of
41 its body.
43 * Solidifying the Body
45 Here is a hand designed eve-style in blender.
48 #+attr_html: width="500"
49 [[../images/hand-screenshot0.png]]
51 If we load it directly into jMonkeyEngine, we get this:
53 #+name: test-0
54 #+begin_src clojure
55 (ns cortex.test.body
56 (:use (cortex world util body))
57 (:import (com.aurellem.capture Capture RatchetTimer)
58 (com.jme3.math Quaternion Vector3f)
59 java.io.File))
61 (def hand-path "Models/test-creature/hand.blend")
63 (defn hand [] (load-blender-model hand-path))
65 (defn setup [world]
66 (let [cam (.getCamera world)]
67 (println-repl cam)
68 (.setLocation
69 cam (Vector3f.
70 -6.9015837, 8.644911, 5.6043186))
71 (.setRotation
72 cam
73 (Quaternion.
74 0.14046453, 0.85894054, -0.34301838, 0.3533118)))
75 (light-up-everything world)
76 (.setTimer world (RatchetTimer. 60))
77 world)
79 (defn test-one []
80 (world (hand)
81 standard-debug-controls
82 (comp
83 #(Capture/captureVideo
84 % (File. "/home/r/proj/cortex/render/body/1"))
85 setup)
86 no-op))
87 #+end_src
90 #+begin_src clojure :results silent
91 (.start (cortex.test.body/test-one))
92 #+end_src
94 #+begin_html
95 <video controls="controls" width="755">
96 <source src="../video/ghost-hand.ogg" type="video/ogg"
97 preload="none" poster="../images/aurellem-1280x480.png" />
98 </video>
99 #+end_html
101 You will notice that the hand has no physical presence -- it's a
102 hologram through witch everything passes. Therefore, the first thing
103 to do is to make it solid. Blender has physics simulation on par with
104 jMonkeyEngine (they both use bullet as their physics backend), but it
105 can be difficult to translate between the two systems, so for now I
106 specify the mass of each object in blender and construct the physics
107 shape based on the mesh in jMonkeyEngine.
109 #+name: joints-1
110 #+begin_src clojure
111 (defn physical!
112 "Iterate through the nodes in creature and make them real physical
113 objects in the simulation."
114 [#^Node creature]
115 (dorun
116 (map
117 (fn [geom]
118 (let [physics-control
119 (RigidBodyControl.
120 (HullCollisionShape.
121 (.getMesh geom))
122 (if-let [mass (meta-data geom "mass")]
123 (do
124 (println-repl
125 "setting" (.getName geom) "mass to" (float mass))
126 (float mass))
127 (float 1)))]
128 (.addControl geom physics-control)))
129 (filter #(isa? (class %) Geometry )
130 (node-seq creature)))))
131 #+end_src
133 =(physical!)= iterates through a creature's node structure, creating
134 CollisionShapes for each geometry with the mass specified in that
135 geometry's meta-data.
137 #+begin_src clojure
138 (in-ns 'cortex.test.body)
140 (def normal-gravity
141 {"key-g" (fn [world _]
142 (set-gravity world (Vector3f. 0 -9.81 0)))})
144 (defn floor []
145 (box 10 3 10 :position (Vector3f. 0 -10 0)
146 :color ColorRGBA/Gray :mass 0))
148 (defn test-two []
149 (world (nodify
150 [(doto (hand)
151 (physical!))
152 (floor)])
153 (merge standard-debug-controls normal-gravity)
154 (comp
155 #(Capture/captureVideo
156 % (File. "/home/r/proj/cortex/render/body/2"))
157 #(do (set-gravity % Vector3f/ZERO) %)
158 setup)
159 no-op))
160 #+end_src
162 #+begin_html
163 <video controls="controls" width="755">
164 <source src="../video/crumbly-hand.ogg" type="video/ogg"
165 preload="none" poster="../images/aurellem-1280x480.png" />
166 </video>
167 #+end_html
169 Now that's some progress.
172 * Joints
174 Obviously, an AI is not going to be doing much just lying in pieces on
175 the floor. So, the next step to making a proper body is to connect
176 those pieces together with joints. jMonkeyEngine has a large array of
177 joints available via bullet, such as Point2Point, Cone, Hinge, and a
178 generic Six Degree of Freedom joint, with or without spring
179 restitution.
181 Although it should be possible to specify the joints using blender's
182 physics system, and then automatically import them with jMonkeyEngine,
183 the support isn't there yet, and there are a few problems with bullet
184 itself that need to be solved before it can happen.
186 So, I will use the same system for specifying joints as I will do for
187 some senses. Each joint is specified by an empty node whose parent
188 has the name "joints". Their orientation and meta-data determine what
189 joint is created.
191 [[../images/hand-screenshot1.png]]
196 #+name: joints-2
197 #+begin_src clojure
198 (defn joint-targets
199 "Return the two closest two objects to the joint object, ordered
200 from bottom to top according to the joint's rotation."
201 [#^Node parts #^Node joint]
202 (loop [radius (float 0.01)]
203 (let [results (CollisionResults.)]
204 (.collideWith
205 parts
206 (BoundingBox. (.getWorldTranslation joint)
207 radius radius radius)
208 results)
209 (let [targets
210 (distinct
211 (map #(.getGeometry %) results))]
212 (if (>= (count targets) 2)
213 (sort-by
214 #(let [v
215 (jme-to-blender
216 (.mult
217 (.inverse (.getWorldRotation joint))
218 (.subtract (.getWorldTranslation %)
219 (.getWorldTranslation joint))))]
220 (println-repl (.getName %) ":" v)
221 (.dot (Vector3f. 1 1 1)
222 v))
223 (take 2 targets))
224 (recur (float (* radius 2))))))))
226 (defmulti joint-dispatch
227 "Translate blender pseudo-joints into real JME joints."
228 (fn [constraints & _]
229 (:type constraints)))
231 (defmethod joint-dispatch :point
232 [constraints control-a control-b pivot-a pivot-b rotation]
233 (println-repl "creating POINT2POINT joint")
234 ;; bullet's point2point joints are BROKEN, so we must use the
235 ;; generic 6DOF joint instead of an actual Point2Point joint!
237 ;; should be able to do this:
238 (comment
239 (Point2PointJoint.
240 control-a
241 control-b
242 pivot-a
243 pivot-b))
245 ;; but instead we must do this:
246 (println-repl "substuting 6DOF joint for POINT2POINT joint!")
247 (doto
248 (SixDofJoint.
249 control-a
250 control-b
251 pivot-a
252 pivot-b
253 false)
254 (.setLinearLowerLimit Vector3f/ZERO)
255 (.setLinearUpperLimit Vector3f/ZERO)
256 ;;(.setAngularLowerLimit (Vector3f. 1 1 1))
257 ;;(.setAngularUpperLimit (Vector3f. 0 0 0))
259 ))
262 (defmethod joint-dispatch :hinge
263 [constraints control-a control-b pivot-a pivot-b rotation]
264 (println-repl "creating HINGE joint")
265 (let [axis
266 (if-let
267 [axis (:axis constraints)]
268 axis
269 Vector3f/UNIT_X)
270 [limit-1 limit-2] (:limit constraints)
271 hinge-axis
272 (.mult
273 rotation
274 (blender-to-jme axis))]
275 (doto
276 (HingeJoint.
277 control-a
278 control-b
279 pivot-a
280 pivot-b
281 hinge-axis
282 hinge-axis)
283 (.setLimit limit-1 limit-2))))
285 (defmethod joint-dispatch :cone
286 [constraints control-a control-b pivot-a pivot-b rotation]
287 (let [limit-xz (:limit-xz constraints)
288 limit-xy (:limit-xy constraints)
289 twist (:twist constraints)]
291 (println-repl "creating CONE joint")
292 (println-repl rotation)
293 (println-repl
294 "UNIT_X --> " (.mult rotation (Vector3f. 1 0 0)))
295 (println-repl
296 "UNIT_Y --> " (.mult rotation (Vector3f. 0 1 0)))
297 (println-repl
298 "UNIT_Z --> " (.mult rotation (Vector3f. 0 0 1)))
299 (doto
300 (ConeJoint.
301 control-a
302 control-b
303 pivot-a
304 pivot-b
305 rotation
306 rotation)
307 (.setLimit (float limit-xz)
308 (float limit-xy)
309 (float twist)))))
311 (defn connect
312 "Create a joint between 'obj-a and 'obj-b at the location of
313 'joint. The type of joint is determined by the metadata on 'joint.
315 Here are some examples:
316 {:type :point}
317 {:type :hinge :limit [0 (/ Math/PI 2)] :axis (Vector3f. 0 1 0)}
318 (:axis defaults to (Vector3f. 1 0 0) if not provided for hinge joints)
320 {:type :cone :limit-xz 0]
321 :limit-xy 0]
322 :twist 0]} (use XZY rotation mode in blender!)"
323 [#^Node obj-a #^Node obj-b #^Node joint]
324 (let [control-a (.getControl obj-a RigidBodyControl)
325 control-b (.getControl obj-b RigidBodyControl)
326 joint-center (.getWorldTranslation joint)
327 joint-rotation (.toRotationMatrix (.getWorldRotation joint))
328 pivot-a (world-to-local obj-a joint-center)
329 pivot-b (world-to-local obj-b joint-center)]
331 (if-let [constraints
332 (map-vals
333 eval
334 (read-string
335 (meta-data joint "joint")))]
336 ;; A side-effect of creating a joint registers
337 ;; it with both physics objects which in turn
338 ;; will register the joint with the physics system
339 ;; when the simulation is started.
340 (do
341 (println-repl "creating joint between"
342 (.getName obj-a) "and" (.getName obj-b))
343 (joint-dispatch constraints
344 control-a control-b
345 pivot-a pivot-b
346 joint-rotation))
347 (println-repl "could not find joint meta-data!"))))
349 (defvar
350 ^{:arglists '([creature])}
351 joints
352 (sense-nodes "joints")
353 "Return the children of the creature's \"joints\" node.")
355 (defn joints!
356 "Connect the solid parts of the creature with physical joints. The
357 joints are taken from the \"joints\" node in the creature."
358 [#^Node creature]
359 (dorun
360 (map
361 (fn [joint]
362 (let [[obj-a obj-b] (joint-targets creature joint)]
363 (connect obj-a obj-b joint)))
364 (joints creature))))
366 (defn body!
367 "Endow the creature with a physical body connected with joints. The
368 particulars of the joints and the masses of each pody part are
369 determined in blender."
370 [#^Node creature]
371 (physical! creature)
372 (joints! creature))
373 #+end_src
375 * Bookkeeping
377 #+name: body-0
378 #+begin_src clojure
379 (ns cortex.body
380 "Assemble a physical creature using the definitions found in a
381 specially prepared blender file. Creates rigid bodies and joints so
382 that a creature can have a physical presense in the simulation."
383 {:author "Robert McIntyre"}
384 (:use (cortex world util sense))
385 (:use clojure.contrib.def)
386 (:import
387 (com.jme3.math Vector3f Quaternion Vector2f Matrix3f)
388 (com.jme3.bullet.joints
389 SixDofJoint Point2PointJoint HingeJoint ConeJoint)
390 com.jme3.bullet.control.RigidBodyControl
391 com.jme3.collision.CollisionResults
392 com.jme3.bounding.BoundingBox
393 com.jme3.scene.Node
394 com.jme3.scene.Geometry
395 com.jme3.bullet.collision.shapes.HullCollisionShape))
396 #+end_src
398 * Source
400 * COMMENT Examples
402 #+name: test-body
403 #+begin_src clojure
404 (ns cortex.test.body
405 (:use (cortex world util body))
406 (:require cortex.silly)
407 (:import
408 com.jme3.math.Vector3f
409 com.jme3.math.ColorRGBA
410 com.jme3.bullet.joints.Point2PointJoint
411 com.jme3.bullet.control.RigidBodyControl
412 com.jme3.system.NanoTimer
413 com.jme3.math.Quaternion))
415 (defn worm-segments
416 "Create multiple evenly spaced box segments. They're fabulous!"
417 [segment-length num-segments interstitial-space radius]
418 (letfn [(nth-segment
419 [n]
420 (box segment-length radius radius :mass 0.1
421 :position
422 (Vector3f.
423 (* 2 n (+ interstitial-space segment-length)) 0 0)
424 :name (str "worm-segment" n)
425 :color (ColorRGBA/randomColor)))]
426 (map nth-segment (range num-segments))))
428 (defn connect-at-midpoint
429 "Connect two physics objects with a Point2Point joint constraint at
430 the point equidistant from both objects' centers."
431 [segmentA segmentB]
432 (let [centerA (.getWorldTranslation segmentA)
433 centerB (.getWorldTranslation segmentB)
434 midpoint (.mult (.add centerA centerB) (float 0.5))
435 pivotA (.subtract midpoint centerA)
436 pivotB (.subtract midpoint centerB)
438 ;; A side-effect of creating a joint registers
439 ;; it with both physics objects which in turn
440 ;; will register the joint with the physics system
441 ;; when the simulation is started.
442 joint (Point2PointJoint.
443 (.getControl segmentA RigidBodyControl)
444 (.getControl segmentB RigidBodyControl)
445 pivotA
446 pivotB)]
447 segmentB))
449 (defn eve-worm
450 "Create a worm-like body bound by invisible joint constraints."
451 []
452 (let [segments (worm-segments 0.2 5 0.1 0.1)]
453 (dorun (map (partial apply connect-at-midpoint)
454 (partition 2 1 segments)))
455 (nodify "worm" segments)))
457 (defn worm-pattern
458 "This is a simple, mindless motor control pattern that drives the
459 second segment of the worm's body at an offset angle with
460 sinusoidally varying strength."
461 [time]
462 (let [angle (* Math/PI (/ 9 20))
463 direction (Vector3f. 0 (Math/sin angle) (Math/cos angle))]
464 [Vector3f/ZERO
465 (.mult
466 direction
467 (float (* 2 (Math/sin (* Math/PI 2 (/ (rem time 300 ) 300))))))
468 Vector3f/ZERO
469 Vector3f/ZERO
470 Vector3f/ZERO]))
472 (defn test-motor-control
473 "Testing motor-control:
474 You should see a multi-segmented worm-like object fall onto the
475 table and begin writhing and moving."
476 []
477 (let [worm (eve-worm)
478 time (atom 0)
479 worm-motor-map (vector-motor-control worm)]
480 (world
481 (nodify [worm
482 (box 10 0.5 10 :position (Vector3f. 0 -5 0) :mass 0
483 :color ColorRGBA/Gray)])
484 standard-debug-controls
485 (fn [world]
486 (enable-debug world)
487 (light-up-everything world)
488 (comment
489 (com.aurellem.capture.Capture/captureVideo
490 world
491 (file-str "/home/r/proj/cortex/tmp/moving-worm")))
492 )
494 (fn [_ _]
495 (swap! time inc)
496 (Thread/sleep 20)
497 (dorun (worm-motor-map
498 (worm-pattern @time)))))))
502 (defn join-at-point [obj-a obj-b world-pivot]
503 (cortex.silly/joint-dispatch
504 {:type :point}
505 (.getControl obj-a RigidBodyControl)
506 (.getControl obj-b RigidBodyControl)
507 (cortex.silly/world-to-local obj-a world-pivot)
508 (cortex.silly/world-to-local obj-b world-pivot)
509 nil
510 ))
512 (import com.jme3.bullet.collision.PhysicsCollisionObject)
514 (defn blab-* []
515 (let [hand (box 0.5 0.2 0.2 :position (Vector3f. 0 0 0)
516 :mass 0 :color ColorRGBA/Green)
517 finger (box 0.5 0.2 0.2 :position (Vector3f. 2.4 0 0)
518 :mass 1 :color ColorRGBA/Red)
519 connection-point (Vector3f. 1.2 0 0)
520 root (nodify [hand finger])]
522 (join-at-point hand finger (Vector3f. 1.2 0 0))
524 (.setCollisionGroup
525 (.getControl hand RigidBodyControl)
526 PhysicsCollisionObject/COLLISION_GROUP_NONE)
527 (world
528 root
529 standard-debug-controls
530 (fn [world]
531 (enable-debug world)
532 (.setTimer world (com.aurellem.capture.RatchetTimer. 60))
533 (set-gravity world Vector3f/ZERO)
534 )
535 no-op)))
536 (comment
538 (defn proprioception-debug-window
539 []
540 (let [time (atom 0)]
541 (fn [prop-data]
542 (if (= 0 (rem (swap! time inc) 40))
543 (println-repl prop-data)))))
544 )
546 (comment
547 (dorun
548 (map
549 (comp
550 println-repl
551 (fn [[p y r]]
552 (format
553 "pitch: %1.2f\nyaw: %1.2f\nroll: %1.2f\n"
554 p y r)))
555 prop-data)))
560 (defn test-proprioception
561 "Testing proprioception:
562 You should see two foating bars, and a printout of pitch, yaw, and
563 roll. Pressing key-r/key-t should move the blue bar up and down and
564 change only the value of pitch. key-f/key-g moves it side to side
565 and changes yaw. key-v/key-b will spin the blue segment clockwise
566 and counterclockwise, and only affect roll."
567 []
568 (let [hand (box 0.2 1 0.2 :position (Vector3f. 0 0 0)
569 :mass 0 :color ColorRGBA/Green :name "hand")
570 finger (box 0.2 1 0.2 :position (Vector3f. 0 2.4 0)
571 :mass 1 :color ColorRGBA/Red :name "finger")
572 joint-node (box 0.1 0.05 0.05 :color ColorRGBA/Yellow
573 :position (Vector3f. 0 1.2 0)
574 :rotation (doto (Quaternion.)
575 (.fromAngleAxis
576 (/ Math/PI 2)
577 (Vector3f. 0 0 1)))
578 :physical? false)
579 joint (join-at-point hand finger (Vector3f. 0 1.2 0 ))
580 creature (nodify [hand finger joint-node])
581 finger-control (.getControl finger RigidBodyControl)
582 hand-control (.getControl hand RigidBodyControl)]
585 (let
586 ;; *******************************************
588 [floor (box 10 10 10 :position (Vector3f. 0 -15 0)
589 :mass 0 :color ColorRGBA/Gray)
591 root (nodify [creature floor])
592 prop (joint-proprioception creature joint-node)
593 prop-view (proprioception-debug-window)
595 controls
596 (merge standard-debug-controls
597 {"key-o"
598 (fn [_ _] (.setEnabled finger-control true))
599 "key-p"
600 (fn [_ _] (.setEnabled finger-control false))
601 "key-k"
602 (fn [_ _] (.setEnabled hand-control true))
603 "key-l"
604 (fn [_ _] (.setEnabled hand-control false))
605 "key-i"
606 (fn [world _] (set-gravity world (Vector3f. 0 0 0)))
607 "key-period"
608 (fn [world _]
609 (.setEnabled finger-control false)
610 (.setEnabled hand-control false)
611 (.rotate creature (doto (Quaternion.)
612 (.fromAngleAxis
613 (float (/ Math/PI 15))
614 (Vector3f. 0 0 -1))))
616 (.setEnabled finger-control true)
617 (.setEnabled hand-control true)
618 (set-gravity world (Vector3f. 0 0 0))
619 )
622 }
623 )
625 ]
626 (comment
627 (.setCollisionGroup
628 (.getControl hand RigidBodyControl)
629 PhysicsCollisionObject/COLLISION_GROUP_NONE)
630 )
631 (apply
632 world
633 (with-movement
634 hand
635 ["key-y" "key-u" "key-h" "key-j" "key-n" "key-m"]
636 [10 10 10 10 1 1]
637 (with-movement
638 finger
639 ["key-r" "key-t" "key-f" "key-g" "key-v" "key-b"]
640 [1 1 10 10 10 10]
641 [root
642 controls
643 (fn [world]
644 (.setTimer world (com.aurellem.capture.RatchetTimer. 60))
645 (set-gravity world (Vector3f. 0 0 0))
646 (light-up-everything world))
647 (fn [_ _] (prop-view (list (prop))))]))))))
649 #+end_src
651 #+results: test-body
652 : #'cortex.test.body/test-proprioception
655 * COMMENT code-limbo
656 #+begin_src clojure
657 ;;(.loadModel
658 ;; (doto (asset-manager)
659 ;; (.registerLoader BlenderModelLoader (into-array String ["blend"])))
660 ;; "Models/person/person.blend")
663 (defn load-blender-model
664 "Load a .blend file using an asset folder relative path."
665 [^String model]
666 (.loadModel
667 (doto (asset-manager)
668 (.registerLoader BlenderModelLoader (into-array String ["blend"])))
669 model))
672 (defn view-model [^String model]
673 (view
674 (.loadModel
675 (doto (asset-manager)
676 (.registerLoader BlenderModelLoader (into-array String ["blend"])))
677 model)))
679 (defn load-blender-scene [^String model]
680 (.loadModel
681 (doto (asset-manager)
682 (.registerLoader BlenderLoader (into-array String ["blend"])))
683 model))
685 (defn worm
686 []
687 (.loadModel (asset-manager) "Models/anim2/Cube.mesh.xml"))
689 (defn oto
690 []
691 (.loadModel (asset-manager) "Models/Oto/Oto.mesh.xml"))
693 (defn sinbad
694 []
695 (.loadModel (asset-manager) "Models/Sinbad/Sinbad.mesh.xml"))
697 (defn worm-blender
698 []
699 (first (seq (.getChildren (load-blender-model
700 "Models/anim2/simple-worm.blend")))))
702 (defn body
703 "given a node with a SkeletonControl, will produce a body sutiable
704 for AI control with movement and proprioception."
705 [node]
706 (let [skeleton-control (.getControl node SkeletonControl)
707 krc (KinematicRagdollControl.)]
708 (comment
709 (dorun
710 (map #(.addBoneName krc %)
711 ["mid2" "tail" "head" "mid1" "mid3" "mid4" "Dummy-Root" ""]
712 ;;"mid2" "mid3" "tail" "head"]
713 )))
714 (.addControl node krc)
715 (.setRagdollMode krc)
716 )
717 node
718 )
719 (defn show-skeleton [node]
720 (let [sd
722 (doto
723 (SkeletonDebugger. "aurellem-skel-debug"
724 (skel node))
725 (.setMaterial (green-x-ray)))]
726 (.attachChild node sd)
727 node))
731 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
733 ;; this could be a good way to give objects special properties like
734 ;; being eyes and the like
736 (.getUserData
737 (.getChild
738 (load-blender-model "Models/property/test.blend") 0)
739 "properties")
741 ;; the properties are saved along with the blender file.
742 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
747 (defn init-debug-skel-node
748 [f debug-node skeleton]
749 (let [bones
750 (map #(.getBone skeleton %)
751 (range (.getBoneCount skeleton)))]
752 (dorun (map #(.setUserControl % true) bones))
753 (dorun (map (fn [b]
754 (println (.getName b)
755 " -- " (f b)))
756 bones))
757 (dorun
758 (map #(.attachChild
759 debug-node
760 (doto
761 (sphere 0.1
762 :position (f %)
763 :physical? false)
764 (.setMaterial (green-x-ray))))
765 bones)))
766 debug-node)
768 (import jme3test.bullet.PhysicsTestHelper)
771 (defn test-zzz [the-worm world value]
772 (if (not value)
773 (let [skeleton (skel the-worm)]
774 (println-repl "enabling bones")
775 (dorun
776 (map
777 #(.setUserControl (.getBone skeleton %) true)
778 (range (.getBoneCount skeleton))))
781 (let [b (.getBone skeleton 2)]
782 (println-repl "moving " (.getName b))
783 (println-repl (.getLocalPosition b))
784 (.setUserTransforms b
785 Vector3f/UNIT_X
786 Quaternion/IDENTITY
787 ;;(doto (Quaternion.)
788 ;; (.fromAngles (/ Math/PI 2)
789 ;; 0
790 ;; 0
792 (Vector3f. 1 1 1))
793 )
795 (println-repl "hi! <3"))))
798 (defn test-ragdoll []
800 (let [the-worm
802 ;;(.loadModel (asset-manager) "Models/anim2/Cube.mesh.xml")
803 (doto (show-skeleton (worm-blender))
804 (.setLocalTranslation (Vector3f. 0 10 0))
805 ;;(worm)
806 ;;(oto)
807 ;;(sinbad)
808 )
809 ]
812 (.start
813 (world
814 (doto (Node.)
815 (.attachChild the-worm))
816 {"key-return" (fire-cannon-ball)
817 "key-space" (partial test-zzz the-worm)
818 }
819 (fn [world]
820 (light-up-everything world)
821 (PhysicsTestHelper/createPhysicsTestWorld
822 (.getRootNode world)
823 (asset-manager)
824 (.getPhysicsSpace
825 (.getState (.getStateManager world) BulletAppState)))
826 (set-gravity world Vector3f/ZERO)
827 ;;(.setTimer world (NanoTimer.))
828 ;;(org.lwjgl.input.Mouse/setGrabbed false)
829 )
830 no-op
831 )
834 )))
837 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
838 ;;; here is the ragdoll stuff
840 (def worm-mesh (.getMesh (.getChild (worm-blender) 0)))
841 (def mesh worm-mesh)
843 (.getFloatBuffer mesh VertexBuffer$Type/Position)
844 (.getFloatBuffer mesh VertexBuffer$Type/BoneWeight)
845 (.getData (.getBuffer mesh VertexBuffer$Type/BoneIndex))
848 (defn position [index]
849 (.get
850 (.getFloatBuffer worm-mesh VertexBuffer$Type/Position)
851 index))
853 (defn bones [index]
854 (.get
855 (.getData (.getBuffer mesh VertexBuffer$Type/BoneIndex))
856 index))
858 (defn bone-weights [index]
859 (.get
860 (.getFloatBuffer mesh VertexBuffer$Type/BoneWeight)
861 index))
865 (defn vertex-bones [vertex]
866 (vec (map (comp int bones) (range (* vertex 4) (+ (* vertex 4) 4)))))
868 (defn vertex-weights [vertex]
869 (vec (map (comp float bone-weights) (range (* vertex 4) (+ (* vertex 4) 4)))))
871 (defn vertex-position [index]
872 (let [offset (* index 3)]
873 (Vector3f. (position offset)
874 (position (inc offset))
875 (position (inc(inc offset))))))
877 (def vertex-info (juxt vertex-position vertex-bones vertex-weights))
879 (defn bone-control-color [index]
880 (get {[1 0 0 0] ColorRGBA/Red
881 [1 2 0 0] ColorRGBA/Magenta
882 [2 0 0 0] ColorRGBA/Blue}
883 (vertex-bones index)
884 ColorRGBA/White))
886 (defn influence-color [index bone-num]
887 (get
888 {(float 0) ColorRGBA/Blue
889 (float 0.5) ColorRGBA/Green
890 (float 1) ColorRGBA/Red}
891 ;; find the weight of the desired bone
892 ((zipmap (vertex-bones index)(vertex-weights index))
893 bone-num)
894 ColorRGBA/Blue))
896 (def worm-vertices (set (map vertex-info (range 60))))
899 (defn test-info []
900 (let [points (Node.)]
901 (dorun
902 (map #(.attachChild points %)
903 (map #(sphere 0.01
904 :position (vertex-position %)
905 :color (influence-color % 1)
906 :physical? false)
907 (range 60))))
908 (view points)))
911 (defrecord JointControl [joint physics-space]
912 PhysicsControl
913 (setPhysicsSpace [this space]
914 (dosync
915 (ref-set (:physics-space this) space))
916 (.addJoint space (:joint this)))
917 (update [this tpf])
918 (setSpatial [this spatial])
919 (render [this rm vp])
920 (getPhysicsSpace [this] (deref (:physics-space this)))
921 (isEnabled [this] true)
922 (setEnabled [this state]))
924 (defn add-joint
925 "Add a joint to a particular object. When the object is added to the
926 PhysicsSpace of a simulation, the joint will also be added"
927 [object joint]
928 (let [control (JointControl. joint (ref nil))]
929 (.addControl object control))
930 object)
933 (defn hinge-world
934 []
935 (let [sphere1 (sphere)
936 sphere2 (sphere 1 :position (Vector3f. 3 3 3))
937 joint (Point2PointJoint.
938 (.getControl sphere1 RigidBodyControl)
939 (.getControl sphere2 RigidBodyControl)
940 Vector3f/ZERO (Vector3f. 3 3 3))]
941 (add-joint sphere1 joint)
942 (doto (Node. "hinge-world")
943 (.attachChild sphere1)
944 (.attachChild sphere2))))
947 (defn test-joint []
948 (view (hinge-world)))
950 ;; (defn copier-gen []
951 ;; (let [count (atom 0)]
952 ;; (fn [in]
953 ;; (swap! count inc)
954 ;; (clojure.contrib.duck-streams/copy
955 ;; in (File. (str "/home/r/tmp/mao-test/clojure-images/"
956 ;; ;;/home/r/tmp/mao-test/clojure-images
957 ;; (format "%08d.png" @count)))))))
958 ;; (defn decrease-framerate []
959 ;; (map
960 ;; (copier-gen)
961 ;; (sort
962 ;; (map first
963 ;; (partition
964 ;; 4
965 ;; (filter #(re-matches #".*.png$" (.getCanonicalPath %))
966 ;; (file-seq
967 ;; (file-str
968 ;; "/home/r/media/anime/mao-temp/images"))))))))
972 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
974 (defn proprioception
975 "Create a proprioception map that reports the rotations of the
976 various limbs of the creature's body"
977 [creature]
978 [#^Node creature]
979 (let [
980 nodes (node-seq creature)
981 joints
982 (map
983 :joint
984 (filter
985 #(isa? (class %) JointControl)
986 (reduce
987 concat
988 (map (fn [node]
989 (map (fn [num] (.getControl node num))
990 (range (.getNumControls node))))
991 nodes))))]
992 (fn []
993 (reduce concat (map relative-positions (list (first joints)))))))
996 (defn skel [node]
997 (doto
998 (.getSkeleton
999 (.getControl node SkeletonControl))
1000 ;; this is necessary to force the skeleton to have accurate world
1001 ;; transforms before it is rendered to the screen.
1002 (.resetAndUpdate)))
1004 (defn green-x-ray []
1005 (doto (Material. (asset-manager)
1006 "Common/MatDefs/Misc/Unshaded.j3md")
1007 (.setColor "Color" ColorRGBA/Green)
1008 (-> (.getAdditionalRenderState)
1009 (.setDepthTest false))))
1011 (defn test-worm []
1012 (.start
1013 (world
1014 (doto (Node.)
1015 ;;(.attachChild (point-worm))
1016 (.attachChild (load-blender-model
1017 "Models/anim2/joint-worm.blend"))
1019 (.attachChild (box 10 1 10
1020 :position (Vector3f. 0 -2 0) :mass 0
1021 :color (ColorRGBA/Gray))))
1023 "key-space" (fire-cannon-ball)
1025 (fn [world]
1026 (enable-debug world)
1027 (light-up-everything world)
1028 ;;(.setTimer world (NanoTimer.))
1030 no-op)))
1034 ;; defunct movement stuff
1035 (defn torque-controls [control]
1036 (let [torques
1037 (concat
1038 (map #(Vector3f. 0 (Math/sin %) (Math/cos %))
1039 (range 0 (* Math/PI 2) (/ (* Math/PI 2) 20)))
1040 [Vector3f/UNIT_X])]
1041 (map (fn [torque-axis]
1042 (fn [torque]
1043 (.applyTorque
1044 control
1045 (.mult (.mult (.getPhysicsRotation control)
1046 torque-axis)
1047 (float
1048 (* (.getMass control) torque))))))
1049 torques)))
1051 (defn motor-map
1052 "Take a creature and generate a function that will enable fine
1053 grained control over all the creature's limbs."
1054 [#^Node creature]
1055 (let [controls (keep #(.getControl % RigidBodyControl)
1056 (node-seq creature))
1057 limb-controls (reduce concat (map torque-controls controls))
1058 body-control (partial map #(%1 %2) limb-controls)]
1059 body-control))
1061 (defn test-motor-map
1062 "see how torque works."
1063 []
1064 (let [finger (box 3 0.5 0.5 :position (Vector3f. 0 2 0)
1065 :mass 1 :color ColorRGBA/Green)
1066 motor-map (motor-map finger)]
1067 (world
1068 (nodify [finger
1069 (box 10 0.5 10 :position (Vector3f. 0 -5 0) :mass 0
1070 :color ColorRGBA/Gray)])
1071 standard-debug-controls
1072 (fn [world]
1073 (set-gravity world Vector3f/ZERO)
1074 (light-up-everything world)
1075 (.setTimer world (NanoTimer.)))
1076 (fn [_ _]
1077 (dorun (motor-map [0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
1078 0]))))))
1080 (defn joint-proprioception [#^Node parts #^Node joint]
1081 (let [[obj-a obj-b] (joint-targets parts joint)
1082 joint-rot (.getWorldRotation joint)
1083 pre-inv-a (.inverse (.getWorldRotation obj-a))
1084 x (.mult pre-inv-a (.mult joint-rot Vector3f/UNIT_X))
1085 y (.mult pre-inv-a (.mult joint-rot Vector3f/UNIT_Y))
1086 z (.mult pre-inv-a (.mult joint-rot Vector3f/UNIT_Z))
1088 x Vector3f/UNIT_Y
1089 y Vector3f/UNIT_Z
1090 z Vector3f/UNIT_X
1093 tmp-rot-a (.getWorldRotation obj-a)]
1094 (println-repl "x:" (.mult tmp-rot-a x))
1095 (println-repl "y:" (.mult tmp-rot-a y))
1096 (println-repl "z:" (.mult tmp-rot-a z))
1097 (println-repl "rot-a" (.getWorldRotation obj-a))
1098 (println-repl "rot-b" (.getWorldRotation obj-b))
1099 (println-repl "joint-rot" joint-rot)
1100 ;; this function will report proprioceptive information for the
1101 ;; joint.
1102 (fn []
1103 ;; x is the "twist" axis, y and z are the "bend" axes
1104 (let [rot-a (.getWorldRotation obj-a)
1105 ;;inv-a (.inverse rot-a)
1106 rot-b (.getWorldRotation obj-b)
1107 ;;relative (.mult rot-b inv-a)
1108 basis (doto (Matrix3f.)
1109 (.setColumn 0 (.mult rot-a x))
1110 (.setColumn 1 (.mult rot-a y))
1111 (.setColumn 2 (.mult rot-a z)))
1112 rotation-about-joint
1113 (doto (Quaternion.)
1114 (.fromRotationMatrix
1115 (.mult (.invert basis)
1116 (.toRotationMatrix rot-b))))
1117 [yaw roll pitch]
1118 (seq (.toAngles rotation-about-joint nil))]
1119 ;;return euler angles of the quaternion around the new basis
1120 [yaw roll pitch]))))
1122 #+end_src
1130 * COMMENT generate Source
1131 #+begin_src clojure :tangle ../src/cortex/body.clj
1132 <<joints>>
1133 #+end_src
1135 #+begin_src clojure :tangle ../src/cortex/test/body.clj
1136 <<test-0>>
1137 #+end_src