Mercurial > cortex
view org/body.org @ 204:162b24a82712
corrections from conv. with Dylan
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Wed, 08 Feb 2012 09:10:23 -0700 |
parents | 0e5d5ee5a914 |
children | d3a2abfac405 |
line wrap: on
line source
1 #+title: Building a Body2 #+author: Robert McIntyre3 #+email: rlm@mit.edu4 #+description: Simulating a body (movement, touch, propioception) in jMonkeyEngine3.5 #+SETUPFILE: ../../aurellem/org/setup.org6 #+INCLUDE: ../../aurellem/org/level-0.org9 * Design Constraints11 I use [[www.blender.org/][blender]] to design bodies. The design of the bodies is12 determined by the requirements of the AI that will use them. The13 bodies must be easy for an AI to sense and control, and they must be14 relatively simple for jMonkeyEngine to compute.16 ** Bag of Bones18 How to create such a body? One option I ultimately rejected is to use19 blender's [[http://wiki.blender.org/index.php/Doc:2.6/Manual/Rigging/Armatures][armature]] system. The idea would have been to define a mesh20 which describes the creature's entire body. To this you add an21 (skeleton) which deforms this mesh. This technique is used extensively22 to model humans and create realistic animations. It is hard to use for23 my purposes because it is difficult to update the creature's Physics24 Collision Mesh in tandem with its Geometric Mesh under the influence25 of the armature. Withouth this the creature will not be able to grab26 things in its environment, and it won't be able to tell where its27 physical body is by using its eyes. Also, armatures do not specify28 any rotational limits for a joint, making it hard to model elbows,29 shoulders, etc.31 ** EVE33 Instead of using the human-like "deformable bag of bones" approach, I34 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 EVE's body is composed of several rigid components that are held40 together by invisible joint constraints. This is what I mean by41 "eve-like". The main reason that I use eve-style bodies is so that42 there will be correspondence between the AI's vision and the physical43 presence of its body. Each individual section is simulated by a44 separate rigid body that corresponds exactly with its visual45 representation and does not change. Sections are connected by46 invisible joints that are well supported in jMonkyeEngine. Bullet, the47 physics backend for jMonkeyEngine, can efficiently simulate hundreds48 of rigid bodies connected by joints. Sections do not have to stay as49 one piece forever; they can be dynamically replaced with multiple50 sections to simulate splitting in two. This could be used to simulate51 retractable claws or EVE's hands, which could coalece into one object52 in the movie.54 * Solidifying the Body56 Here is a hand designed eve-style in blender.58 #+attr_html: width="755"59 [[../images/hand-screenshot0.png]]61 If we load it directly into jMonkeyEngine, we get this:63 #+name: test-064 #+begin_src clojure65 (ns cortex.test.body66 (:use (cortex world util body))67 (:import (com.aurellem.capture Capture RatchetTimer)68 (com.jme3.math Quaternion Vector3f)69 java.io.File))71 (def hand-path "Models/test-creature/hand.blend")73 (defn hand [] (load-blender-model hand-path))75 (defn setup [world]76 (let [cam (.getCamera world)]77 (println-repl cam)78 (.setLocation79 cam (Vector3f.80 -6.9015837, 8.644911, 5.6043186))81 (.setRotation82 cam83 (Quaternion.84 0.14046453, 0.85894054, -0.34301838, 0.3533118)))85 (light-up-everything world)86 (.setTimer world (RatchetTimer. 60))87 world)89 (defn test-one []90 (world (hand)91 standard-debug-controls92 (comp93 #(Capture/captureVideo94 % (File. "/home/r/proj/cortex/render/body/1"))95 setup)96 no-op))97 #+end_src100 #+begin_src clojure :results silent101 (.start (cortex.test.body/test-one))102 #+end_src104 #+begin_html105 <div class="figure">106 <center>107 <video controls="controls" width="640">108 <source src="../video/ghost-hand.ogg" type="video/ogg"109 preload="none" poster="../images/aurellem-1280x480.png" />110 </video>111 </center>112 <p>The hand model directly loaded from blender. It has no physical113 presense in the simulation. </p>114 </div>115 #+end_html117 You will notice that the hand has no physical presence -- it's a118 hologram through which everything passes. Therefore, the first thing119 to do is to make it solid. Blender has physics simulation on par with120 jMonkeyEngine (they both use bullet as their physics backend), but it121 can be difficult to translate between the two systems, so for now I122 specify the mass of each object in blender and construct the physics123 shape based on the mesh in jMonkeyEngine.125 #+name: body-1126 #+begin_src clojure127 (defn physical!128 "Iterate through the nodes in creature and make them real physical129 objects in the simulation."130 [#^Node creature]131 (dorun132 (map133 (fn [geom]134 (let [physics-control135 (RigidBodyControl.136 (HullCollisionShape.137 (.getMesh geom))138 (if-let [mass (meta-data geom "mass")]139 (do140 (println-repl141 "setting" (.getName geom) "mass to" (float mass))142 (float mass))143 (float 1)))]144 (.addControl geom physics-control)))145 (filter #(isa? (class %) Geometry )146 (node-seq creature)))))147 #+end_src149 =(physical!)= iterates through a creature's node structure, creating150 CollisionShapes for each geometry with the mass specified in that151 geometry's meta-data.153 #+name: test-1154 #+begin_src clojure155 (in-ns 'cortex.test.body)157 (def normal-gravity158 {"key-g" (fn [world _]159 (set-gravity world (Vector3f. 0 -9.81 0)))})161 (defn floor []162 (box 10 3 10 :position (Vector3f. 0 -10 0)163 :color ColorRGBA/Gray :mass 0))165 (defn test-two []166 (world (nodify167 [(doto (hand)168 (physical!))169 (floor)])170 (merge standard-debug-controls normal-gravity)171 (comp172 #(Capture/captureVideo173 % (File. "/home/r/proj/cortex/render/body/2"))174 #(do (set-gravity % Vector3f/ZERO) %)175 setup)176 no-op))177 #+end_src179 #+begin_html180 <div class="figure">181 <center>182 <video controls="controls" width="640">183 <source src="../video/crumbly-hand.ogg" type="video/ogg"184 preload="none" poster="../images/aurellem-1280x480.png" />185 </video>186 </center>187 <p>The hand now has a physical presence, but there is nothing to hold188 it together.</p>189 </div>190 #+end_html192 Now that's some progress.195 * Joints197 Obviously, an AI is not going to be doing much just lying in pieces on198 the floor. So, the next step to making a proper body is to connect199 those pieces together with joints. jMonkeyEngine has a large array of200 joints available via bullet, such as Point2Point, Cone, Hinge, and a201 generic Six Degree of Freedom joint, with or without spring202 restitution.204 Although it should be possible to specify the joints using blender's205 physics system, and then automatically import them with jMonkeyEngine,206 the support isn't there yet, and there are a few problems with bullet207 itself that need to be solved before it can happen.209 So, I will use the same system for specifying joints as I will do for210 some senses. Each joint is specified by an empty node whose parent211 has the name "joints". Their orientation and meta-data determine what212 joint is created.214 #+attr_html: width="755"215 #+caption: joints hack in blender. Each empty node here will be transformed into a joint in jMonkeyEngine216 [[../images/hand-screenshot1.png]]218 The empty node in the upper right, highlighted in yellow, is the219 parent node of all the emptys which represent joints. The following220 functions must do three things to translate these into real joints:222 - Find the children of the "joints" node.223 - Determine the two spatials the joint it meant to connect.224 - Create the joint based on the meta-data of the empty node.226 ** Finding the Joints227 #+name: joints-2228 #+begin_src clojure229 (defvar230 ^{:arglists '([creature])}231 joints232 (sense-nodes "joints")233 "Return the children of the creature's \"joints\" node.")234 #+end_src236 The higher order function =(sense-nodes)= from cortex.sense makes our237 first task very easy.239 ** Joint Targets and Orientation241 This technique for finding a joint's targets is very similiar to242 =(cortex.sense/closest-node)=. A small cube, centered around the243 empty-node, grows exponentially until it intersects two /physical/244 objects. The objects are ordered according to the joint's rotation,245 with the first one being the object that has more negative coordinates246 in the joint's reference frame. Since the objects must be physical,247 the empty-node itself escapes detection. Because the objects must be248 physical, =(joint-targets)= must be called /after/ =(physical!)= is249 called.251 #+name: joints-3252 #+begin_src clojure253 (defn joint-targets254 "Return the two closest two objects to the joint object, ordered255 from bottom to top according to the joint's rotation."256 [#^Node parts #^Node joint]257 (loop [radius (float 0.01)]258 (let [results (CollisionResults.)]259 (.collideWith260 parts261 (BoundingBox. (.getWorldTranslation joint)262 radius radius radius)263 results)264 (let [targets265 (distinct266 (map #(.getGeometry %) results))]267 (if (>= (count targets) 2)268 (sort-by269 #(let [v270 (jme-to-blender271 (.mult272 (.inverse (.getWorldRotation joint))273 (.subtract (.getWorldTranslation %)274 (.getWorldTranslation joint))))]275 (println-repl (.getName %) ":" v)276 (.dot (Vector3f. 1 1 1)277 v))278 (take 2 targets))279 (recur (float (* radius 2))))))))280 #+end_src282 ** Generating Joints284 This long chunk of code iterates through all the different ways of285 specifying joints using blender meta-data and converts each one to the286 appropriate jMonkyeEngine joint.288 #+name: joints-4289 #+begin_src clojure290 (defmulti joint-dispatch291 "Translate blender pseudo-joints into real JME joints."292 (fn [constraints & _]293 (:type constraints)))295 (defmethod joint-dispatch :point296 [constraints control-a control-b pivot-a pivot-b rotation]297 (println-repl "creating POINT2POINT joint")298 ;; bullet's point2point joints are BROKEN, so we must use the299 ;; generic 6DOF joint instead of an actual Point2Point joint!301 ;; should be able to do this:302 (comment303 (Point2PointJoint.304 control-a305 control-b306 pivot-a307 pivot-b))309 ;; but instead we must do this:310 (println-repl "substuting 6DOF joint for POINT2POINT joint!")311 (doto312 (SixDofJoint.313 control-a314 control-b315 pivot-a316 pivot-b317 false)318 (.setLinearLowerLimit Vector3f/ZERO)319 (.setLinearUpperLimit Vector3f/ZERO)))321 (defmethod joint-dispatch :hinge322 [constraints control-a control-b pivot-a pivot-b rotation]323 (println-repl "creating HINGE joint")324 (let [axis325 (if-let326 [axis (:axis constraints)]327 axis328 Vector3f/UNIT_X)329 [limit-1 limit-2] (:limit constraints)330 hinge-axis331 (.mult332 rotation333 (blender-to-jme axis))]334 (doto335 (HingeJoint.336 control-a337 control-b338 pivot-a339 pivot-b340 hinge-axis341 hinge-axis)342 (.setLimit limit-1 limit-2))))344 (defmethod joint-dispatch :cone345 [constraints control-a control-b pivot-a pivot-b rotation]346 (let [limit-xz (:limit-xz constraints)347 limit-xy (:limit-xy constraints)348 twist (:twist constraints)]350 (println-repl "creating CONE joint")351 (println-repl rotation)352 (println-repl353 "UNIT_X --> " (.mult rotation (Vector3f. 1 0 0)))354 (println-repl355 "UNIT_Y --> " (.mult rotation (Vector3f. 0 1 0)))356 (println-repl357 "UNIT_Z --> " (.mult rotation (Vector3f. 0 0 1)))358 (doto359 (ConeJoint.360 control-a361 control-b362 pivot-a363 pivot-b364 rotation365 rotation)366 (.setLimit (float limit-xz)367 (float limit-xy)368 (float twist)))))370 (defn connect371 "Create a joint between 'obj-a and 'obj-b at the location of372 'joint. The type of joint is determined by the metadata on 'joint.374 Here are some examples:375 {:type :point}376 {:type :hinge :limit [0 (/ Math/PI 2)] :axis (Vector3f. 0 1 0)}377 (:axis defaults to (Vector3f. 1 0 0) if not provided for hinge joints)379 {:type :cone :limit-xz 0]380 :limit-xy 0]381 :twist 0]} (use XZY rotation mode in blender!)"382 [#^Node obj-a #^Node obj-b #^Node joint]383 (let [control-a (.getControl obj-a RigidBodyControl)384 control-b (.getControl obj-b RigidBodyControl)385 joint-center (.getWorldTranslation joint)386 joint-rotation (.toRotationMatrix (.getWorldRotation joint))387 pivot-a (world-to-local obj-a joint-center)388 pivot-b (world-to-local obj-b joint-center)]390 (if-let [constraints391 (map-vals392 eval393 (read-string394 (meta-data joint "joint")))]395 ;; A side-effect of creating a joint registers396 ;; it with both physics objects which in turn397 ;; will register the joint with the physics system398 ;; when the simulation is started.399 (do400 (println-repl "creating joint between"401 (.getName obj-a) "and" (.getName obj-b))402 (joint-dispatch constraints403 control-a control-b404 pivot-a pivot-b405 joint-rotation))406 (println-repl "could not find joint meta-data!"))))407 #+end_src409 Creating joints is now a matter applying =(connect)= to each joint410 node.412 #+begin_src clojure413 (defn joints!414 "Connect the solid parts of the creature with physical joints. The415 joints are taken from the \"joints\" node in the creature."416 [#^Node creature]417 (dorun418 (map419 (fn [joint]420 (let [[obj-a obj-b] (joint-targets creature joint)]421 (connect obj-a obj-b joint)))422 (joints creature))))423 #+end_src426 ** Round 3428 Now we can test the hand in all its glory.430 #+begin_src clojure431 (in-ns 'cortex.test.body)433 (def debug-control434 {"key-h" (fn [world val]435 (if val (enable-debug world)))437 "key-u" (fn [world _] (set-gravity world Vector3f/ZERO))438 })440 (defn test-three []441 (world (nodify442 [(doto (hand)443 (physical!)444 (joints!) )445 (floor)])446 (merge standard-debug-controls debug-control447 normal-gravity)448 (comp449 #(Capture/captureVideo450 % (File. "/home/r/proj/cortex/render/body/3"))451 #(do (set-gravity % Vector3f/ZERO) %)452 setup)453 no-op))454 #+end_src456 =(physical!)= makes the hand solid, then =(joints!)= connects each457 piece together.460 #+begin_html461 <div class="figure">462 <center>463 <video controls="controls" width="640">464 <source src="../video/full-hand.ogg" type="video/ogg"465 preload="none" poster="../images/aurellem-1280x480.png" />466 </video>467 </center>468 <p>Now the hand is physical and has joints.</p>469 </div>470 #+end_html472 The joints are visualized as green connections between each segment473 for debug purposes. You can see that they correspond to the empty474 nodes in the blender file.476 * Wrap-Up!478 It is convienent to combine =(physical!)= and =(joints!)= into one479 function that completely creates the creature's physical body.481 #+name: joints-4482 #+begin_src clojure483 (defn body!484 "Endow the creature with a physical body connected with joints. The485 particulars of the joints and the masses of each pody part are486 determined in blender."487 [#^Node creature]488 (physical! creature)489 (joints! creature))490 #+end_src492 * Bookkeeping494 Header; here for completeness.496 #+name: body-0497 #+begin_src clojure498 (ns cortex.body499 "Assemble a physical creature using the definitions found in a500 specially prepared blender file. Creates rigid bodies and joints so501 that a creature can have a physical presense in the simulation."502 {:author "Robert McIntyre"}503 (:use (cortex world util sense))504 (:use clojure.contrib.def)505 (:import506 (com.jme3.math Vector3f Quaternion Vector2f Matrix3f)507 (com.jme3.bullet.joints508 SixDofJoint Point2PointJoint HingeJoint ConeJoint)509 com.jme3.bullet.control.RigidBodyControl510 com.jme3.collision.CollisionResults511 com.jme3.bounding.BoundingBox512 com.jme3.scene.Node513 com.jme3.scene.Geometry514 com.jme3.bullet.collision.shapes.HullCollisionShape))515 #+end_src517 * Source519 Dylan -- I'll fill these in later520 - cortex.body521 - cortex.test.body522 - blender files524 * COMMENT Examples526 #+name: test-body527 #+begin_src clojure528 (ns cortex.test.body529 (:use (cortex world util body))530 (:require cortex.silly)531 (:import532 com.jme3.math.Vector3f533 com.jme3.math.ColorRGBA534 com.jme3.bullet.joints.Point2PointJoint535 com.jme3.bullet.control.RigidBodyControl536 com.jme3.system.NanoTimer537 com.jme3.math.Quaternion))539 (defn worm-segments540 "Create multiple evenly spaced box segments. They're fabulous!"541 [segment-length num-segments interstitial-space radius]542 (letfn [(nth-segment543 [n]544 (box segment-length radius radius :mass 0.1545 :position546 (Vector3f.547 (* 2 n (+ interstitial-space segment-length)) 0 0)548 :name (str "worm-segment" n)549 :color (ColorRGBA/randomColor)))]550 (map nth-segment (range num-segments))))552 (defn connect-at-midpoint553 "Connect two physics objects with a Point2Point joint constraint at554 the point equidistant from both objects' centers."555 [segmentA segmentB]556 (let [centerA (.getWorldTranslation segmentA)557 centerB (.getWorldTranslation segmentB)558 midpoint (.mult (.add centerA centerB) (float 0.5))559 pivotA (.subtract midpoint centerA)560 pivotB (.subtract midpoint centerB)562 ;; A side-effect of creating a joint registers563 ;; it with both physics objects which in turn564 ;; will register the joint with the physics system565 ;; when the simulation is started.566 joint (Point2PointJoint.567 (.getControl segmentA RigidBodyControl)568 (.getControl segmentB RigidBodyControl)569 pivotA570 pivotB)]571 segmentB))573 (defn eve-worm574 "Create a worm-like body bound by invisible joint constraints."575 []576 (let [segments (worm-segments 0.2 5 0.1 0.1)]577 (dorun (map (partial apply connect-at-midpoint)578 (partition 2 1 segments)))579 (nodify "worm" segments)))581 (defn worm-pattern582 "This is a simple, mindless motor control pattern that drives the583 second segment of the worm's body at an offset angle with584 sinusoidally varying strength."585 [time]586 (let [angle (* Math/PI (/ 9 20))587 direction (Vector3f. 0 (Math/sin angle) (Math/cos angle))]588 [Vector3f/ZERO589 (.mult590 direction591 (float (* 2 (Math/sin (* Math/PI 2 (/ (rem time 300 ) 300))))))592 Vector3f/ZERO593 Vector3f/ZERO594 Vector3f/ZERO]))596 (defn test-motor-control597 "Testing motor-control:598 You should see a multi-segmented worm-like object fall onto the599 table and begin writhing and moving."600 []601 (let [worm (eve-worm)602 time (atom 0)603 worm-motor-map (vector-motor-control worm)]604 (world605 (nodify [worm606 (box 10 0.5 10 :position (Vector3f. 0 -5 0) :mass 0607 :color ColorRGBA/Gray)])608 standard-debug-controls609 (fn [world]610 (enable-debug world)611 (light-up-everything world)612 (comment613 (com.aurellem.capture.Capture/captureVideo614 world615 (file-str "/home/r/proj/cortex/tmp/moving-worm")))616 )618 (fn [_ _]619 (swap! time inc)620 (Thread/sleep 20)621 (dorun (worm-motor-map622 (worm-pattern @time)))))))626 (defn join-at-point [obj-a obj-b world-pivot]627 (cortex.silly/joint-dispatch628 {:type :point}629 (.getControl obj-a RigidBodyControl)630 (.getControl obj-b RigidBodyControl)631 (cortex.silly/world-to-local obj-a world-pivot)632 (cortex.silly/world-to-local obj-b world-pivot)633 nil634 ))636 (import com.jme3.bullet.collision.PhysicsCollisionObject)638 (defn blab-* []639 (let [hand (box 0.5 0.2 0.2 :position (Vector3f. 0 0 0)640 :mass 0 :color ColorRGBA/Green)641 finger (box 0.5 0.2 0.2 :position (Vector3f. 2.4 0 0)642 :mass 1 :color ColorRGBA/Red)643 connection-point (Vector3f. 1.2 0 0)644 root (nodify [hand finger])]646 (join-at-point hand finger (Vector3f. 1.2 0 0))648 (.setCollisionGroup649 (.getControl hand RigidBodyControl)650 PhysicsCollisionObject/COLLISION_GROUP_NONE)651 (world652 root653 standard-debug-controls654 (fn [world]655 (enable-debug world)656 (.setTimer world (com.aurellem.capture.RatchetTimer. 60))657 (set-gravity world Vector3f/ZERO)658 )659 no-op)))660 (comment662 (defn proprioception-debug-window663 []664 (let [time (atom 0)]665 (fn [prop-data]666 (if (= 0 (rem (swap! time inc) 40))667 (println-repl prop-data)))))668 )670 (comment671 (dorun672 (map673 (comp674 println-repl675 (fn [[p y r]]676 (format677 "pitch: %1.2f\nyaw: %1.2f\nroll: %1.2f\n"678 p y r)))679 prop-data)))684 (defn test-proprioception685 "Testing proprioception:686 You should see two foating bars, and a printout of pitch, yaw, and687 roll. Pressing key-r/key-t should move the blue bar up and down and688 change only the value of pitch. key-f/key-g moves it side to side689 and changes yaw. key-v/key-b will spin the blue segment clockwise690 and counterclockwise, and only affect roll."691 []692 (let [hand (box 0.2 1 0.2 :position (Vector3f. 0 0 0)693 :mass 0 :color ColorRGBA/Green :name "hand")694 finger (box 0.2 1 0.2 :position (Vector3f. 0 2.4 0)695 :mass 1 :color ColorRGBA/Red :name "finger")696 joint-node (box 0.1 0.05 0.05 :color ColorRGBA/Yellow697 :position (Vector3f. 0 1.2 0)698 :rotation (doto (Quaternion.)699 (.fromAngleAxis700 (/ Math/PI 2)701 (Vector3f. 0 0 1)))702 :physical? false)703 joint (join-at-point hand finger (Vector3f. 0 1.2 0 ))704 creature (nodify [hand finger joint-node])705 finger-control (.getControl finger RigidBodyControl)706 hand-control (.getControl hand RigidBodyControl)]709 (let710 ;; *******************************************712 [floor (box 10 10 10 :position (Vector3f. 0 -15 0)713 :mass 0 :color ColorRGBA/Gray)715 root (nodify [creature floor])716 prop (joint-proprioception creature joint-node)717 prop-view (proprioception-debug-window)719 controls720 (merge standard-debug-controls721 {"key-o"722 (fn [_ _] (.setEnabled finger-control true))723 "key-p"724 (fn [_ _] (.setEnabled finger-control false))725 "key-k"726 (fn [_ _] (.setEnabled hand-control true))727 "key-l"728 (fn [_ _] (.setEnabled hand-control false))729 "key-i"730 (fn [world _] (set-gravity world (Vector3f. 0 0 0)))731 "key-period"732 (fn [world _]733 (.setEnabled finger-control false)734 (.setEnabled hand-control false)735 (.rotate creature (doto (Quaternion.)736 (.fromAngleAxis737 (float (/ Math/PI 15))738 (Vector3f. 0 0 -1))))740 (.setEnabled finger-control true)741 (.setEnabled hand-control true)742 (set-gravity world (Vector3f. 0 0 0))743 )746 }747 )749 ]750 (comment751 (.setCollisionGroup752 (.getControl hand RigidBodyControl)753 PhysicsCollisionObject/COLLISION_GROUP_NONE)754 )755 (apply756 world757 (with-movement758 hand759 ["key-y" "key-u" "key-h" "key-j" "key-n" "key-m"]760 [10 10 10 10 1 1]761 (with-movement762 finger763 ["key-r" "key-t" "key-f" "key-g" "key-v" "key-b"]764 [1 1 10 10 10 10]765 [root766 controls767 (fn [world]768 (.setTimer world (com.aurellem.capture.RatchetTimer. 60))769 (set-gravity world (Vector3f. 0 0 0))770 (light-up-everything world))771 (fn [_ _] (prop-view (list (prop))))]))))))773 #+end_src775 #+results: test-body776 : #'cortex.test.body/test-proprioception779 * COMMENT code-limbo780 #+begin_src clojure781 ;;(.loadModel782 ;; (doto (asset-manager)783 ;; (.registerLoader BlenderModelLoader (into-array String ["blend"])))784 ;; "Models/person/person.blend")787 (defn load-blender-model788 "Load a .blend file using an asset folder relative path."789 [^String model]790 (.loadModel791 (doto (asset-manager)792 (.registerLoader BlenderModelLoader (into-array String ["blend"])))793 model))796 (defn view-model [^String model]797 (view798 (.loadModel799 (doto (asset-manager)800 (.registerLoader BlenderModelLoader (into-array String ["blend"])))801 model)))803 (defn load-blender-scene [^String model]804 (.loadModel805 (doto (asset-manager)806 (.registerLoader BlenderLoader (into-array String ["blend"])))807 model))809 (defn worm810 []811 (.loadModel (asset-manager) "Models/anim2/Cube.mesh.xml"))813 (defn oto814 []815 (.loadModel (asset-manager) "Models/Oto/Oto.mesh.xml"))817 (defn sinbad818 []819 (.loadModel (asset-manager) "Models/Sinbad/Sinbad.mesh.xml"))821 (defn worm-blender822 []823 (first (seq (.getChildren (load-blender-model824 "Models/anim2/simple-worm.blend")))))826 (defn body827 "given a node with a SkeletonControl, will produce a body sutiable828 for AI control with movement and proprioception."829 [node]830 (let [skeleton-control (.getControl node SkeletonControl)831 krc (KinematicRagdollControl.)]832 (comment833 (dorun834 (map #(.addBoneName krc %)835 ["mid2" "tail" "head" "mid1" "mid3" "mid4" "Dummy-Root" ""]836 ;;"mid2" "mid3" "tail" "head"]837 )))838 (.addControl node krc)839 (.setRagdollMode krc)840 )841 node842 )843 (defn show-skeleton [node]844 (let [sd846 (doto847 (SkeletonDebugger. "aurellem-skel-debug"848 (skel node))849 (.setMaterial (green-x-ray)))]850 (.attachChild node sd)851 node))855 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;857 ;; this could be a good way to give objects special properties like858 ;; being eyes and the like860 (.getUserData861 (.getChild862 (load-blender-model "Models/property/test.blend") 0)863 "properties")865 ;; the properties are saved along with the blender file.866 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;871 (defn init-debug-skel-node872 [f debug-node skeleton]873 (let [bones874 (map #(.getBone skeleton %)875 (range (.getBoneCount skeleton)))]876 (dorun (map #(.setUserControl % true) bones))877 (dorun (map (fn [b]878 (println (.getName b)879 " -- " (f b)))880 bones))881 (dorun882 (map #(.attachChild883 debug-node884 (doto885 (sphere 0.1886 :position (f %)887 :physical? false)888 (.setMaterial (green-x-ray))))889 bones)))890 debug-node)892 (import jme3test.bullet.PhysicsTestHelper)895 (defn test-zzz [the-worm world value]896 (if (not value)897 (let [skeleton (skel the-worm)]898 (println-repl "enabling bones")899 (dorun900 (map901 #(.setUserControl (.getBone skeleton %) true)902 (range (.getBoneCount skeleton))))905 (let [b (.getBone skeleton 2)]906 (println-repl "moving " (.getName b))907 (println-repl (.getLocalPosition b))908 (.setUserTransforms b909 Vector3f/UNIT_X910 Quaternion/IDENTITY911 ;;(doto (Quaternion.)912 ;; (.fromAngles (/ Math/PI 2)913 ;; 0914 ;; 0916 (Vector3f. 1 1 1))917 )919 (println-repl "hi! <3"))))922 (defn test-ragdoll []924 (let [the-worm926 ;;(.loadModel (asset-manager) "Models/anim2/Cube.mesh.xml")927 (doto (show-skeleton (worm-blender))928 (.setLocalTranslation (Vector3f. 0 10 0))929 ;;(worm)930 ;;(oto)931 ;;(sinbad)932 )933 ]936 (.start937 (world938 (doto (Node.)939 (.attachChild the-worm))940 {"key-return" (fire-cannon-ball)941 "key-space" (partial test-zzz the-worm)942 }943 (fn [world]944 (light-up-everything world)945 (PhysicsTestHelper/createPhysicsTestWorld946 (.getRootNode world)947 (asset-manager)948 (.getPhysicsSpace949 (.getState (.getStateManager world) BulletAppState)))950 (set-gravity world Vector3f/ZERO)951 ;;(.setTimer world (NanoTimer.))952 ;;(org.lwjgl.input.Mouse/setGrabbed false)953 )954 no-op955 )958 )))961 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;962 ;;; here is the ragdoll stuff964 (def worm-mesh (.getMesh (.getChild (worm-blender) 0)))965 (def mesh worm-mesh)967 (.getFloatBuffer mesh VertexBuffer$Type/Position)968 (.getFloatBuffer mesh VertexBuffer$Type/BoneWeight)969 (.getData (.getBuffer mesh VertexBuffer$Type/BoneIndex))972 (defn position [index]973 (.get974 (.getFloatBuffer worm-mesh VertexBuffer$Type/Position)975 index))977 (defn bones [index]978 (.get979 (.getData (.getBuffer mesh VertexBuffer$Type/BoneIndex))980 index))982 (defn bone-weights [index]983 (.get984 (.getFloatBuffer mesh VertexBuffer$Type/BoneWeight)985 index))989 (defn vertex-bones [vertex]990 (vec (map (comp int bones) (range (* vertex 4) (+ (* vertex 4) 4)))))992 (defn vertex-weights [vertex]993 (vec (map (comp float bone-weights) (range (* vertex 4) (+ (* vertex 4) 4)))))995 (defn vertex-position [index]996 (let [offset (* index 3)]997 (Vector3f. (position offset)998 (position (inc offset))999 (position (inc(inc offset))))))1001 (def vertex-info (juxt vertex-position vertex-bones vertex-weights))1003 (defn bone-control-color [index]1004 (get {[1 0 0 0] ColorRGBA/Red1005 [1 2 0 0] ColorRGBA/Magenta1006 [2 0 0 0] ColorRGBA/Blue}1007 (vertex-bones index)1008 ColorRGBA/White))1010 (defn influence-color [index bone-num]1011 (get1012 {(float 0) ColorRGBA/Blue1013 (float 0.5) ColorRGBA/Green1014 (float 1) ColorRGBA/Red}1015 ;; find the weight of the desired bone1016 ((zipmap (vertex-bones index)(vertex-weights index))1017 bone-num)1018 ColorRGBA/Blue))1020 (def worm-vertices (set (map vertex-info (range 60))))1023 (defn test-info []1024 (let [points (Node.)]1025 (dorun1026 (map #(.attachChild points %)1027 (map #(sphere 0.011028 :position (vertex-position %)1029 :color (influence-color % 1)1030 :physical? false)1031 (range 60))))1032 (view points)))1035 (defrecord JointControl [joint physics-space]1036 PhysicsControl1037 (setPhysicsSpace [this space]1038 (dosync1039 (ref-set (:physics-space this) space))1040 (.addJoint space (:joint this)))1041 (update [this tpf])1042 (setSpatial [this spatial])1043 (render [this rm vp])1044 (getPhysicsSpace [this] (deref (:physics-space this)))1045 (isEnabled [this] true)1046 (setEnabled [this state]))1048 (defn add-joint1049 "Add a joint to a particular object. When the object is added to the1050 PhysicsSpace of a simulation, the joint will also be added"1051 [object joint]1052 (let [control (JointControl. joint (ref nil))]1053 (.addControl object control))1054 object)1057 (defn hinge-world1058 []1059 (let [sphere1 (sphere)1060 sphere2 (sphere 1 :position (Vector3f. 3 3 3))1061 joint (Point2PointJoint.1062 (.getControl sphere1 RigidBodyControl)1063 (.getControl sphere2 RigidBodyControl)1064 Vector3f/ZERO (Vector3f. 3 3 3))]1065 (add-joint sphere1 joint)1066 (doto (Node. "hinge-world")1067 (.attachChild sphere1)1068 (.attachChild sphere2))))1071 (defn test-joint []1072 (view (hinge-world)))1074 ;; (defn copier-gen []1075 ;; (let [count (atom 0)]1076 ;; (fn [in]1077 ;; (swap! count inc)1078 ;; (clojure.contrib.duck-streams/copy1079 ;; in (File. (str "/home/r/tmp/mao-test/clojure-images/"1080 ;; ;;/home/r/tmp/mao-test/clojure-images1081 ;; (format "%08d.png" @count)))))))1082 ;; (defn decrease-framerate []1083 ;; (map1084 ;; (copier-gen)1085 ;; (sort1086 ;; (map first1087 ;; (partition1088 ;; 41089 ;; (filter #(re-matches #".*.png$" (.getCanonicalPath %))1090 ;; (file-seq1091 ;; (file-str1092 ;; "/home/r/media/anime/mao-temp/images"))))))))1096 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1098 (defn proprioception1099 "Create a proprioception map that reports the rotations of the1100 various limbs of the creature's body"1101 [creature]1102 [#^Node creature]1103 (let [1104 nodes (node-seq creature)1105 joints1106 (map1107 :joint1108 (filter1109 #(isa? (class %) JointControl)1110 (reduce1111 concat1112 (map (fn [node]1113 (map (fn [num] (.getControl node num))1114 (range (.getNumControls node))))1115 nodes))))]1116 (fn []1117 (reduce concat (map relative-positions (list (first joints)))))))1120 (defn skel [node]1121 (doto1122 (.getSkeleton1123 (.getControl node SkeletonControl))1124 ;; this is necessary to force the skeleton to have accurate world1125 ;; transforms before it is rendered to the screen.1126 (.resetAndUpdate)))1128 (defn green-x-ray []1129 (doto (Material. (asset-manager)1130 "Common/MatDefs/Misc/Unshaded.j3md")1131 (.setColor "Color" ColorRGBA/Green)1132 (-> (.getAdditionalRenderState)1133 (.setDepthTest false))))1135 (defn test-worm []1136 (.start1137 (world1138 (doto (Node.)1139 ;;(.attachChild (point-worm))1140 (.attachChild (load-blender-model1141 "Models/anim2/joint-worm.blend"))1143 (.attachChild (box 10 1 101144 :position (Vector3f. 0 -2 0) :mass 01145 :color (ColorRGBA/Gray))))1146 {1147 "key-space" (fire-cannon-ball)1148 }1149 (fn [world]1150 (enable-debug world)1151 (light-up-everything world)1152 ;;(.setTimer world (NanoTimer.))1153 )1154 no-op)))1158 ;; defunct movement stuff1159 (defn torque-controls [control]1160 (let [torques1161 (concat1162 (map #(Vector3f. 0 (Math/sin %) (Math/cos %))1163 (range 0 (* Math/PI 2) (/ (* Math/PI 2) 20)))1164 [Vector3f/UNIT_X])]1165 (map (fn [torque-axis]1166 (fn [torque]1167 (.applyTorque1168 control1169 (.mult (.mult (.getPhysicsRotation control)1170 torque-axis)1171 (float1172 (* (.getMass control) torque))))))1173 torques)))1175 (defn motor-map1176 "Take a creature and generate a function that will enable fine1177 grained control over all the creature's limbs."1178 [#^Node creature]1179 (let [controls (keep #(.getControl % RigidBodyControl)1180 (node-seq creature))1181 limb-controls (reduce concat (map torque-controls controls))1182 body-control (partial map #(%1 %2) limb-controls)]1183 body-control))1185 (defn test-motor-map1186 "see how torque works."1187 []1188 (let [finger (box 3 0.5 0.5 :position (Vector3f. 0 2 0)1189 :mass 1 :color ColorRGBA/Green)1190 motor-map (motor-map finger)]1191 (world1192 (nodify [finger1193 (box 10 0.5 10 :position (Vector3f. 0 -5 0) :mass 01194 :color ColorRGBA/Gray)])1195 standard-debug-controls1196 (fn [world]1197 (set-gravity world Vector3f/ZERO)1198 (light-up-everything world)1199 (.setTimer world (NanoTimer.)))1200 (fn [_ _]1201 (dorun (motor-map [0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 01202 0]))))))1204 (defn joint-proprioception [#^Node parts #^Node joint]1205 (let [[obj-a obj-b] (joint-targets parts joint)1206 joint-rot (.getWorldRotation joint)1207 pre-inv-a (.inverse (.getWorldRotation obj-a))1208 x (.mult pre-inv-a (.mult joint-rot Vector3f/UNIT_X))1209 y (.mult pre-inv-a (.mult joint-rot Vector3f/UNIT_Y))1210 z (.mult pre-inv-a (.mult joint-rot Vector3f/UNIT_Z))1212 x Vector3f/UNIT_Y1213 y Vector3f/UNIT_Z1214 z Vector3f/UNIT_X1217 tmp-rot-a (.getWorldRotation obj-a)]1218 (println-repl "x:" (.mult tmp-rot-a x))1219 (println-repl "y:" (.mult tmp-rot-a y))1220 (println-repl "z:" (.mult tmp-rot-a z))1221 (println-repl "rot-a" (.getWorldRotation obj-a))1222 (println-repl "rot-b" (.getWorldRotation obj-b))1223 (println-repl "joint-rot" joint-rot)1224 ;; this function will report proprioceptive information for the1225 ;; joint.1226 (fn []1227 ;; x is the "twist" axis, y and z are the "bend" axes1228 (let [rot-a (.getWorldRotation obj-a)1229 ;;inv-a (.inverse rot-a)1230 rot-b (.getWorldRotation obj-b)1231 ;;relative (.mult rot-b inv-a)1232 basis (doto (Matrix3f.)1233 (.setColumn 0 (.mult rot-a x))1234 (.setColumn 1 (.mult rot-a y))1235 (.setColumn 2 (.mult rot-a z)))1236 rotation-about-joint1237 (doto (Quaternion.)1238 (.fromRotationMatrix1239 (.mult (.invert basis)1240 (.toRotationMatrix rot-b))))1241 [yaw roll pitch]1242 (seq (.toAngles rotation-about-joint nil))]1243 ;;return euler angles of the quaternion around the new basis1244 [yaw roll pitch]))))1246 #+end_src1254 * COMMENT generate Source1255 #+begin_src clojure :tangle ../src/cortex/body.clj1256 <<joints>>1257 #+end_src1259 #+begin_src clojure :tangle ../src/cortex/test/body.clj1260 <<test-0>>1261 #+end_src