Mercurial > cortex
view org/body.org @ 203:0e5d5ee5a914
first draft of body.org complete
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Wed, 08 Feb 2012 08:53:12 -0700 |
parents | d5c597a7aed4 |
children | 162b24a82712 |
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 The main reason that I use eve-style bodies is so that there will be40 correspondence between the AI's vision and the physical presence of41 its body. Each individual section is simulated by a separate rigid42 body that corresponds exactly with its visual representation and does43 not change. Sections are connected by invisible joints that are well44 supported in jMonkyeEngine. Bullet, the physics backend for45 jMonkeyEngine, can efficiently simulate hundreds of rigid bodies46 connected by joints. Sections do not have to stay as one piece47 forever; they can be dynamically replaced with multiple sections to48 simulate splitting in two. This could be used to simulate retractable49 claws or EVE's hands, which could coalece into one object in the50 movie.52 * Solidifying the Body54 Here is a hand designed eve-style in blender.56 #+attr_html: width="755"57 [[../images/hand-screenshot0.png]]59 If we load it directly into jMonkeyEngine, we get this:61 #+name: test-062 #+begin_src clojure63 (ns cortex.test.body64 (:use (cortex world util body))65 (:import (com.aurellem.capture Capture RatchetTimer)66 (com.jme3.math Quaternion Vector3f)67 java.io.File))69 (def hand-path "Models/test-creature/hand.blend")71 (defn hand [] (load-blender-model hand-path))73 (defn setup [world]74 (let [cam (.getCamera world)]75 (println-repl cam)76 (.setLocation77 cam (Vector3f.78 -6.9015837, 8.644911, 5.6043186))79 (.setRotation80 cam81 (Quaternion.82 0.14046453, 0.85894054, -0.34301838, 0.3533118)))83 (light-up-everything world)84 (.setTimer world (RatchetTimer. 60))85 world)87 (defn test-one []88 (world (hand)89 standard-debug-controls90 (comp91 #(Capture/captureVideo92 % (File. "/home/r/proj/cortex/render/body/1"))93 setup)94 no-op))95 #+end_src98 #+begin_src clojure :results silent99 (.start (cortex.test.body/test-one))100 #+end_src102 #+begin_html103 <div class="figure">104 <center>105 <video controls="controls" width="640">106 <source src="../video/ghost-hand.ogg" type="video/ogg"107 preload="none" poster="../images/aurellem-1280x480.png" />108 </video>109 </center>110 <p>The hand model directly loaded from blender. It has no physical111 presense in the simulation. </p>112 </div>113 #+end_html115 You will notice that the hand has no physical presence -- it's a116 hologram through witch everything passes. Therefore, the first thing117 to do is to make it solid. Blender has physics simulation on par with118 jMonkeyEngine (they both use bullet as their physics backend), but it119 can be difficult to translate between the two systems, so for now I120 specify the mass of each object in blender and construct the physics121 shape based on the mesh in jMonkeyEngine.123 #+name: body-1124 #+begin_src clojure125 (defn physical!126 "Iterate through the nodes in creature and make them real physical127 objects in the simulation."128 [#^Node creature]129 (dorun130 (map131 (fn [geom]132 (let [physics-control133 (RigidBodyControl.134 (HullCollisionShape.135 (.getMesh geom))136 (if-let [mass (meta-data geom "mass")]137 (do138 (println-repl139 "setting" (.getName geom) "mass to" (float mass))140 (float mass))141 (float 1)))]142 (.addControl geom physics-control)))143 (filter #(isa? (class %) Geometry )144 (node-seq creature)))))145 #+end_src147 =(physical!)= iterates through a creature's node structure, creating148 CollisionShapes for each geometry with the mass specified in that149 geometry's meta-data.151 #+name: test-1152 #+begin_src clojure153 (in-ns 'cortex.test.body)155 (def normal-gravity156 {"key-g" (fn [world _]157 (set-gravity world (Vector3f. 0 -9.81 0)))})159 (defn floor []160 (box 10 3 10 :position (Vector3f. 0 -10 0)161 :color ColorRGBA/Gray :mass 0))163 (defn test-two []164 (world (nodify165 [(doto (hand)166 (physical!))167 (floor)])168 (merge standard-debug-controls normal-gravity)169 (comp170 #(Capture/captureVideo171 % (File. "/home/r/proj/cortex/render/body/2"))172 #(do (set-gravity % Vector3f/ZERO) %)173 setup)174 no-op))175 #+end_src177 #+begin_html178 <div class="figure">179 <center>180 <video controls="controls" width="640">181 <source src="../video/crumbly-hand.ogg" type="video/ogg"182 preload="none" poster="../images/aurellem-1280x480.png" />183 </video>184 </center>185 <p>The hand now has a physical presence, but there is nothing to hold186 it together.</p>187 </div>188 #+end_html190 Now that's some progress.193 * Joints195 Obviously, an AI is not going to be doing much just lying in pieces on196 the floor. So, the next step to making a proper body is to connect197 those pieces together with joints. jMonkeyEngine has a large array of198 joints available via bullet, such as Point2Point, Cone, Hinge, and a199 generic Six Degree of Freedom joint, with or without spring200 restitution.202 Although it should be possible to specify the joints using blender's203 physics system, and then automatically import them with jMonkeyEngine,204 the support isn't there yet, and there are a few problems with bullet205 itself that need to be solved before it can happen.207 So, I will use the same system for specifying joints as I will do for208 some senses. Each joint is specified by an empty node whose parent209 has the name "joints". Their orientation and meta-data determine what210 joint is created.212 #+attr_html: width="755"213 #+caption: joints hack in blender. Each empty node here will be transformed into a joint in jMonkeyEngine214 [[../images/hand-screenshot1.png]]216 The empty node in the upper right, highlighted in yellow, is the217 parent node of all the emptys which represent joints. The following218 functions must do three things to translate these into real joints:220 - Find the children of the "joints" node.221 - Determine the two spatials the joint it meant to connect.222 - Create the joint based on the meta-data of the empty node.224 ** Finding the Joints225 #+name: joints-2226 #+begin_src clojure227 (defvar228 ^{:arglists '([creature])}229 joints230 (sense-nodes "joints")231 "Return the children of the creature's \"joints\" node.")232 #+end_src234 The higher order function =(sense-nodes)= from cortex.sense makes our235 first task very easy.237 ** Joint Targets and Orientation239 This technique for finding a joint's targets is very similiar to240 =(cortex.sense/closest-node)=. A small cube, centered around the241 empty-node, grows exponentially until it intersects two /physical/242 objects. The objects are ordered according to the joint's rotation,243 with the first one being the object that has more negative coordinates244 in the joint's reference frame. Since the objects must be physical,245 the empty-node itself escapes detection. Because the objects must be246 physical, =(joint-targets)= must be called /after/ =(physical!)= is247 called.249 #+name: joints-3250 #+begin_src clojure251 (defn joint-targets252 "Return the two closest two objects to the joint object, ordered253 from bottom to top according to the joint's rotation."254 [#^Node parts #^Node joint]255 (loop [radius (float 0.01)]256 (let [results (CollisionResults.)]257 (.collideWith258 parts259 (BoundingBox. (.getWorldTranslation joint)260 radius radius radius)261 results)262 (let [targets263 (distinct264 (map #(.getGeometry %) results))]265 (if (>= (count targets) 2)266 (sort-by267 #(let [v268 (jme-to-blender269 (.mult270 (.inverse (.getWorldRotation joint))271 (.subtract (.getWorldTranslation %)272 (.getWorldTranslation joint))))]273 (println-repl (.getName %) ":" v)274 (.dot (Vector3f. 1 1 1)275 v))276 (take 2 targets))277 (recur (float (* radius 2))))))))278 #+end_src280 ** Generating Joints282 This long chunk of code iterates through all the different ways of283 specifying joints using blender meta-data and converts each one to the284 appropriate jMonkyeEngine joint.286 #+name: joints-4287 #+begin_src clojure288 (defmulti joint-dispatch289 "Translate blender pseudo-joints into real JME joints."290 (fn [constraints & _]291 (:type constraints)))293 (defmethod joint-dispatch :point294 [constraints control-a control-b pivot-a pivot-b rotation]295 (println-repl "creating POINT2POINT joint")296 ;; bullet's point2point joints are BROKEN, so we must use the297 ;; generic 6DOF joint instead of an actual Point2Point joint!299 ;; should be able to do this:300 (comment301 (Point2PointJoint.302 control-a303 control-b304 pivot-a305 pivot-b))307 ;; but instead we must do this:308 (println-repl "substuting 6DOF joint for POINT2POINT joint!")309 (doto310 (SixDofJoint.311 control-a312 control-b313 pivot-a314 pivot-b315 false)316 (.setLinearLowerLimit Vector3f/ZERO)317 (.setLinearUpperLimit Vector3f/ZERO)))319 (defmethod joint-dispatch :hinge320 [constraints control-a control-b pivot-a pivot-b rotation]321 (println-repl "creating HINGE joint")322 (let [axis323 (if-let324 [axis (:axis constraints)]325 axis326 Vector3f/UNIT_X)327 [limit-1 limit-2] (:limit constraints)328 hinge-axis329 (.mult330 rotation331 (blender-to-jme axis))]332 (doto333 (HingeJoint.334 control-a335 control-b336 pivot-a337 pivot-b338 hinge-axis339 hinge-axis)340 (.setLimit limit-1 limit-2))))342 (defmethod joint-dispatch :cone343 [constraints control-a control-b pivot-a pivot-b rotation]344 (let [limit-xz (:limit-xz constraints)345 limit-xy (:limit-xy constraints)346 twist (:twist constraints)]348 (println-repl "creating CONE joint")349 (println-repl rotation)350 (println-repl351 "UNIT_X --> " (.mult rotation (Vector3f. 1 0 0)))352 (println-repl353 "UNIT_Y --> " (.mult rotation (Vector3f. 0 1 0)))354 (println-repl355 "UNIT_Z --> " (.mult rotation (Vector3f. 0 0 1)))356 (doto357 (ConeJoint.358 control-a359 control-b360 pivot-a361 pivot-b362 rotation363 rotation)364 (.setLimit (float limit-xz)365 (float limit-xy)366 (float twist)))))368 (defn connect369 "Create a joint between 'obj-a and 'obj-b at the location of370 'joint. The type of joint is determined by the metadata on 'joint.372 Here are some examples:373 {:type :point}374 {:type :hinge :limit [0 (/ Math/PI 2)] :axis (Vector3f. 0 1 0)}375 (:axis defaults to (Vector3f. 1 0 0) if not provided for hinge joints)377 {:type :cone :limit-xz 0]378 :limit-xy 0]379 :twist 0]} (use XZY rotation mode in blender!)"380 [#^Node obj-a #^Node obj-b #^Node joint]381 (let [control-a (.getControl obj-a RigidBodyControl)382 control-b (.getControl obj-b RigidBodyControl)383 joint-center (.getWorldTranslation joint)384 joint-rotation (.toRotationMatrix (.getWorldRotation joint))385 pivot-a (world-to-local obj-a joint-center)386 pivot-b (world-to-local obj-b joint-center)]388 (if-let [constraints389 (map-vals390 eval391 (read-string392 (meta-data joint "joint")))]393 ;; A side-effect of creating a joint registers394 ;; it with both physics objects which in turn395 ;; will register the joint with the physics system396 ;; when the simulation is started.397 (do398 (println-repl "creating joint between"399 (.getName obj-a) "and" (.getName obj-b))400 (joint-dispatch constraints401 control-a control-b402 pivot-a pivot-b403 joint-rotation))404 (println-repl "could not find joint meta-data!"))))405 #+end_src407 Creating joints is now a matter applying =(connect)= to each joint408 node.410 #+begin_src clojure411 (defn joints!412 "Connect the solid parts of the creature with physical joints. The413 joints are taken from the \"joints\" node in the creature."414 [#^Node creature]415 (dorun416 (map417 (fn [joint]418 (let [[obj-a obj-b] (joint-targets creature joint)]419 (connect obj-a obj-b joint)))420 (joints creature))))421 #+end_src424 ** Round 3426 Now we can test the hand in all its glory.428 #+begin_src clojure429 (in-ns 'cortex.test.body)431 (def debug-control432 {"key-h" (fn [world val]433 (if val (enable-debug world)))435 "key-u" (fn [world _] (set-gravity world Vector3f/ZERO))436 })438 (defn test-three []439 (world (nodify440 [(doto (hand)441 (physical!)442 (joints!) )443 (floor)])444 (merge standard-debug-controls debug-control445 normal-gravity)446 (comp447 #(Capture/captureVideo448 % (File. "/home/r/proj/cortex/render/body/3"))449 #(do (set-gravity % Vector3f/ZERO) %)450 setup)451 no-op))452 #+end_src454 =(physical!)= makes the hand solid, then =(joints!)= connects each455 piece together.458 #+begin_html459 <div class="figure">460 <center>461 <video controls="controls" width="640">462 <source src="../video/full-hand.ogg" type="video/ogg"463 preload="none" poster="../images/aurellem-1280x480.png" />464 </video>465 </center>466 <p>Now the hand is physical and has joints.</p>467 </div>468 #+end_html470 The joints are visualized as green connections between each segment471 for debug purposes. You can see that they correspond to the empty472 nodes in the blender file.474 * Wrap-Up!476 It is convienent to combine =(physical!)= and =(joints!)= into one477 function that completely creates the creature's physical body.479 #+name: joints-4480 #+begin_src clojure481 (defn body!482 "Endow the creature with a physical body connected with joints. The483 particulars of the joints and the masses of each pody part are484 determined in blender."485 [#^Node creature]486 (physical! creature)487 (joints! creature))488 #+end_src490 * Bookkeeping492 Header; here for completeness.494 #+name: body-0495 #+begin_src clojure496 (ns cortex.body497 "Assemble a physical creature using the definitions found in a498 specially prepared blender file. Creates rigid bodies and joints so499 that a creature can have a physical presense in the simulation."500 {:author "Robert McIntyre"}501 (:use (cortex world util sense))502 (:use clojure.contrib.def)503 (:import504 (com.jme3.math Vector3f Quaternion Vector2f Matrix3f)505 (com.jme3.bullet.joints506 SixDofJoint Point2PointJoint HingeJoint ConeJoint)507 com.jme3.bullet.control.RigidBodyControl508 com.jme3.collision.CollisionResults509 com.jme3.bounding.BoundingBox510 com.jme3.scene.Node511 com.jme3.scene.Geometry512 com.jme3.bullet.collision.shapes.HullCollisionShape))513 #+end_src515 * Source517 Dylan -- I'll fill these in later518 - cortex.body519 - cortex.test.body520 - blender files522 * COMMENT Examples524 #+name: test-body525 #+begin_src clojure526 (ns cortex.test.body527 (:use (cortex world util body))528 (:require cortex.silly)529 (:import530 com.jme3.math.Vector3f531 com.jme3.math.ColorRGBA532 com.jme3.bullet.joints.Point2PointJoint533 com.jme3.bullet.control.RigidBodyControl534 com.jme3.system.NanoTimer535 com.jme3.math.Quaternion))537 (defn worm-segments538 "Create multiple evenly spaced box segments. They're fabulous!"539 [segment-length num-segments interstitial-space radius]540 (letfn [(nth-segment541 [n]542 (box segment-length radius radius :mass 0.1543 :position544 (Vector3f.545 (* 2 n (+ interstitial-space segment-length)) 0 0)546 :name (str "worm-segment" n)547 :color (ColorRGBA/randomColor)))]548 (map nth-segment (range num-segments))))550 (defn connect-at-midpoint551 "Connect two physics objects with a Point2Point joint constraint at552 the point equidistant from both objects' centers."553 [segmentA segmentB]554 (let [centerA (.getWorldTranslation segmentA)555 centerB (.getWorldTranslation segmentB)556 midpoint (.mult (.add centerA centerB) (float 0.5))557 pivotA (.subtract midpoint centerA)558 pivotB (.subtract midpoint centerB)560 ;; A side-effect of creating a joint registers561 ;; it with both physics objects which in turn562 ;; will register the joint with the physics system563 ;; when the simulation is started.564 joint (Point2PointJoint.565 (.getControl segmentA RigidBodyControl)566 (.getControl segmentB RigidBodyControl)567 pivotA568 pivotB)]569 segmentB))571 (defn eve-worm572 "Create a worm-like body bound by invisible joint constraints."573 []574 (let [segments (worm-segments 0.2 5 0.1 0.1)]575 (dorun (map (partial apply connect-at-midpoint)576 (partition 2 1 segments)))577 (nodify "worm" segments)))579 (defn worm-pattern580 "This is a simple, mindless motor control pattern that drives the581 second segment of the worm's body at an offset angle with582 sinusoidally varying strength."583 [time]584 (let [angle (* Math/PI (/ 9 20))585 direction (Vector3f. 0 (Math/sin angle) (Math/cos angle))]586 [Vector3f/ZERO587 (.mult588 direction589 (float (* 2 (Math/sin (* Math/PI 2 (/ (rem time 300 ) 300))))))590 Vector3f/ZERO591 Vector3f/ZERO592 Vector3f/ZERO]))594 (defn test-motor-control595 "Testing motor-control:596 You should see a multi-segmented worm-like object fall onto the597 table and begin writhing and moving."598 []599 (let [worm (eve-worm)600 time (atom 0)601 worm-motor-map (vector-motor-control worm)]602 (world603 (nodify [worm604 (box 10 0.5 10 :position (Vector3f. 0 -5 0) :mass 0605 :color ColorRGBA/Gray)])606 standard-debug-controls607 (fn [world]608 (enable-debug world)609 (light-up-everything world)610 (comment611 (com.aurellem.capture.Capture/captureVideo612 world613 (file-str "/home/r/proj/cortex/tmp/moving-worm")))614 )616 (fn [_ _]617 (swap! time inc)618 (Thread/sleep 20)619 (dorun (worm-motor-map620 (worm-pattern @time)))))))624 (defn join-at-point [obj-a obj-b world-pivot]625 (cortex.silly/joint-dispatch626 {:type :point}627 (.getControl obj-a RigidBodyControl)628 (.getControl obj-b RigidBodyControl)629 (cortex.silly/world-to-local obj-a world-pivot)630 (cortex.silly/world-to-local obj-b world-pivot)631 nil632 ))634 (import com.jme3.bullet.collision.PhysicsCollisionObject)636 (defn blab-* []637 (let [hand (box 0.5 0.2 0.2 :position (Vector3f. 0 0 0)638 :mass 0 :color ColorRGBA/Green)639 finger (box 0.5 0.2 0.2 :position (Vector3f. 2.4 0 0)640 :mass 1 :color ColorRGBA/Red)641 connection-point (Vector3f. 1.2 0 0)642 root (nodify [hand finger])]644 (join-at-point hand finger (Vector3f. 1.2 0 0))646 (.setCollisionGroup647 (.getControl hand RigidBodyControl)648 PhysicsCollisionObject/COLLISION_GROUP_NONE)649 (world650 root651 standard-debug-controls652 (fn [world]653 (enable-debug world)654 (.setTimer world (com.aurellem.capture.RatchetTimer. 60))655 (set-gravity world Vector3f/ZERO)656 )657 no-op)))658 (comment660 (defn proprioception-debug-window661 []662 (let [time (atom 0)]663 (fn [prop-data]664 (if (= 0 (rem (swap! time inc) 40))665 (println-repl prop-data)))))666 )668 (comment669 (dorun670 (map671 (comp672 println-repl673 (fn [[p y r]]674 (format675 "pitch: %1.2f\nyaw: %1.2f\nroll: %1.2f\n"676 p y r)))677 prop-data)))682 (defn test-proprioception683 "Testing proprioception:684 You should see two foating bars, and a printout of pitch, yaw, and685 roll. Pressing key-r/key-t should move the blue bar up and down and686 change only the value of pitch. key-f/key-g moves it side to side687 and changes yaw. key-v/key-b will spin the blue segment clockwise688 and counterclockwise, and only affect roll."689 []690 (let [hand (box 0.2 1 0.2 :position (Vector3f. 0 0 0)691 :mass 0 :color ColorRGBA/Green :name "hand")692 finger (box 0.2 1 0.2 :position (Vector3f. 0 2.4 0)693 :mass 1 :color ColorRGBA/Red :name "finger")694 joint-node (box 0.1 0.05 0.05 :color ColorRGBA/Yellow695 :position (Vector3f. 0 1.2 0)696 :rotation (doto (Quaternion.)697 (.fromAngleAxis698 (/ Math/PI 2)699 (Vector3f. 0 0 1)))700 :physical? false)701 joint (join-at-point hand finger (Vector3f. 0 1.2 0 ))702 creature (nodify [hand finger joint-node])703 finger-control (.getControl finger RigidBodyControl)704 hand-control (.getControl hand RigidBodyControl)]707 (let708 ;; *******************************************710 [floor (box 10 10 10 :position (Vector3f. 0 -15 0)711 :mass 0 :color ColorRGBA/Gray)713 root (nodify [creature floor])714 prop (joint-proprioception creature joint-node)715 prop-view (proprioception-debug-window)717 controls718 (merge standard-debug-controls719 {"key-o"720 (fn [_ _] (.setEnabled finger-control true))721 "key-p"722 (fn [_ _] (.setEnabled finger-control false))723 "key-k"724 (fn [_ _] (.setEnabled hand-control true))725 "key-l"726 (fn [_ _] (.setEnabled hand-control false))727 "key-i"728 (fn [world _] (set-gravity world (Vector3f. 0 0 0)))729 "key-period"730 (fn [world _]731 (.setEnabled finger-control false)732 (.setEnabled hand-control false)733 (.rotate creature (doto (Quaternion.)734 (.fromAngleAxis735 (float (/ Math/PI 15))736 (Vector3f. 0 0 -1))))738 (.setEnabled finger-control true)739 (.setEnabled hand-control true)740 (set-gravity world (Vector3f. 0 0 0))741 )744 }745 )747 ]748 (comment749 (.setCollisionGroup750 (.getControl hand RigidBodyControl)751 PhysicsCollisionObject/COLLISION_GROUP_NONE)752 )753 (apply754 world755 (with-movement756 hand757 ["key-y" "key-u" "key-h" "key-j" "key-n" "key-m"]758 [10 10 10 10 1 1]759 (with-movement760 finger761 ["key-r" "key-t" "key-f" "key-g" "key-v" "key-b"]762 [1 1 10 10 10 10]763 [root764 controls765 (fn [world]766 (.setTimer world (com.aurellem.capture.RatchetTimer. 60))767 (set-gravity world (Vector3f. 0 0 0))768 (light-up-everything world))769 (fn [_ _] (prop-view (list (prop))))]))))))771 #+end_src773 #+results: test-body774 : #'cortex.test.body/test-proprioception777 * COMMENT code-limbo778 #+begin_src clojure779 ;;(.loadModel780 ;; (doto (asset-manager)781 ;; (.registerLoader BlenderModelLoader (into-array String ["blend"])))782 ;; "Models/person/person.blend")785 (defn load-blender-model786 "Load a .blend file using an asset folder relative path."787 [^String model]788 (.loadModel789 (doto (asset-manager)790 (.registerLoader BlenderModelLoader (into-array String ["blend"])))791 model))794 (defn view-model [^String model]795 (view796 (.loadModel797 (doto (asset-manager)798 (.registerLoader BlenderModelLoader (into-array String ["blend"])))799 model)))801 (defn load-blender-scene [^String model]802 (.loadModel803 (doto (asset-manager)804 (.registerLoader BlenderLoader (into-array String ["blend"])))805 model))807 (defn worm808 []809 (.loadModel (asset-manager) "Models/anim2/Cube.mesh.xml"))811 (defn oto812 []813 (.loadModel (asset-manager) "Models/Oto/Oto.mesh.xml"))815 (defn sinbad816 []817 (.loadModel (asset-manager) "Models/Sinbad/Sinbad.mesh.xml"))819 (defn worm-blender820 []821 (first (seq (.getChildren (load-blender-model822 "Models/anim2/simple-worm.blend")))))824 (defn body825 "given a node with a SkeletonControl, will produce a body sutiable826 for AI control with movement and proprioception."827 [node]828 (let [skeleton-control (.getControl node SkeletonControl)829 krc (KinematicRagdollControl.)]830 (comment831 (dorun832 (map #(.addBoneName krc %)833 ["mid2" "tail" "head" "mid1" "mid3" "mid4" "Dummy-Root" ""]834 ;;"mid2" "mid3" "tail" "head"]835 )))836 (.addControl node krc)837 (.setRagdollMode krc)838 )839 node840 )841 (defn show-skeleton [node]842 (let [sd844 (doto845 (SkeletonDebugger. "aurellem-skel-debug"846 (skel node))847 (.setMaterial (green-x-ray)))]848 (.attachChild node sd)849 node))853 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;855 ;; this could be a good way to give objects special properties like856 ;; being eyes and the like858 (.getUserData859 (.getChild860 (load-blender-model "Models/property/test.blend") 0)861 "properties")863 ;; the properties are saved along with the blender file.864 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;869 (defn init-debug-skel-node870 [f debug-node skeleton]871 (let [bones872 (map #(.getBone skeleton %)873 (range (.getBoneCount skeleton)))]874 (dorun (map #(.setUserControl % true) bones))875 (dorun (map (fn [b]876 (println (.getName b)877 " -- " (f b)))878 bones))879 (dorun880 (map #(.attachChild881 debug-node882 (doto883 (sphere 0.1884 :position (f %)885 :physical? false)886 (.setMaterial (green-x-ray))))887 bones)))888 debug-node)890 (import jme3test.bullet.PhysicsTestHelper)893 (defn test-zzz [the-worm world value]894 (if (not value)895 (let [skeleton (skel the-worm)]896 (println-repl "enabling bones")897 (dorun898 (map899 #(.setUserControl (.getBone skeleton %) true)900 (range (.getBoneCount skeleton))))903 (let [b (.getBone skeleton 2)]904 (println-repl "moving " (.getName b))905 (println-repl (.getLocalPosition b))906 (.setUserTransforms b907 Vector3f/UNIT_X908 Quaternion/IDENTITY909 ;;(doto (Quaternion.)910 ;; (.fromAngles (/ Math/PI 2)911 ;; 0912 ;; 0914 (Vector3f. 1 1 1))915 )917 (println-repl "hi! <3"))))920 (defn test-ragdoll []922 (let [the-worm924 ;;(.loadModel (asset-manager) "Models/anim2/Cube.mesh.xml")925 (doto (show-skeleton (worm-blender))926 (.setLocalTranslation (Vector3f. 0 10 0))927 ;;(worm)928 ;;(oto)929 ;;(sinbad)930 )931 ]934 (.start935 (world936 (doto (Node.)937 (.attachChild the-worm))938 {"key-return" (fire-cannon-ball)939 "key-space" (partial test-zzz the-worm)940 }941 (fn [world]942 (light-up-everything world)943 (PhysicsTestHelper/createPhysicsTestWorld944 (.getRootNode world)945 (asset-manager)946 (.getPhysicsSpace947 (.getState (.getStateManager world) BulletAppState)))948 (set-gravity world Vector3f/ZERO)949 ;;(.setTimer world (NanoTimer.))950 ;;(org.lwjgl.input.Mouse/setGrabbed false)951 )952 no-op953 )956 )))959 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;960 ;;; here is the ragdoll stuff962 (def worm-mesh (.getMesh (.getChild (worm-blender) 0)))963 (def mesh worm-mesh)965 (.getFloatBuffer mesh VertexBuffer$Type/Position)966 (.getFloatBuffer mesh VertexBuffer$Type/BoneWeight)967 (.getData (.getBuffer mesh VertexBuffer$Type/BoneIndex))970 (defn position [index]971 (.get972 (.getFloatBuffer worm-mesh VertexBuffer$Type/Position)973 index))975 (defn bones [index]976 (.get977 (.getData (.getBuffer mesh VertexBuffer$Type/BoneIndex))978 index))980 (defn bone-weights [index]981 (.get982 (.getFloatBuffer mesh VertexBuffer$Type/BoneWeight)983 index))987 (defn vertex-bones [vertex]988 (vec (map (comp int bones) (range (* vertex 4) (+ (* vertex 4) 4)))))990 (defn vertex-weights [vertex]991 (vec (map (comp float bone-weights) (range (* vertex 4) (+ (* vertex 4) 4)))))993 (defn vertex-position [index]994 (let [offset (* index 3)]995 (Vector3f. (position offset)996 (position (inc offset))997 (position (inc(inc offset))))))999 (def vertex-info (juxt vertex-position vertex-bones vertex-weights))1001 (defn bone-control-color [index]1002 (get {[1 0 0 0] ColorRGBA/Red1003 [1 2 0 0] ColorRGBA/Magenta1004 [2 0 0 0] ColorRGBA/Blue}1005 (vertex-bones index)1006 ColorRGBA/White))1008 (defn influence-color [index bone-num]1009 (get1010 {(float 0) ColorRGBA/Blue1011 (float 0.5) ColorRGBA/Green1012 (float 1) ColorRGBA/Red}1013 ;; find the weight of the desired bone1014 ((zipmap (vertex-bones index)(vertex-weights index))1015 bone-num)1016 ColorRGBA/Blue))1018 (def worm-vertices (set (map vertex-info (range 60))))1021 (defn test-info []1022 (let [points (Node.)]1023 (dorun1024 (map #(.attachChild points %)1025 (map #(sphere 0.011026 :position (vertex-position %)1027 :color (influence-color % 1)1028 :physical? false)1029 (range 60))))1030 (view points)))1033 (defrecord JointControl [joint physics-space]1034 PhysicsControl1035 (setPhysicsSpace [this space]1036 (dosync1037 (ref-set (:physics-space this) space))1038 (.addJoint space (:joint this)))1039 (update [this tpf])1040 (setSpatial [this spatial])1041 (render [this rm vp])1042 (getPhysicsSpace [this] (deref (:physics-space this)))1043 (isEnabled [this] true)1044 (setEnabled [this state]))1046 (defn add-joint1047 "Add a joint to a particular object. When the object is added to the1048 PhysicsSpace of a simulation, the joint will also be added"1049 [object joint]1050 (let [control (JointControl. joint (ref nil))]1051 (.addControl object control))1052 object)1055 (defn hinge-world1056 []1057 (let [sphere1 (sphere)1058 sphere2 (sphere 1 :position (Vector3f. 3 3 3))1059 joint (Point2PointJoint.1060 (.getControl sphere1 RigidBodyControl)1061 (.getControl sphere2 RigidBodyControl)1062 Vector3f/ZERO (Vector3f. 3 3 3))]1063 (add-joint sphere1 joint)1064 (doto (Node. "hinge-world")1065 (.attachChild sphere1)1066 (.attachChild sphere2))))1069 (defn test-joint []1070 (view (hinge-world)))1072 ;; (defn copier-gen []1073 ;; (let [count (atom 0)]1074 ;; (fn [in]1075 ;; (swap! count inc)1076 ;; (clojure.contrib.duck-streams/copy1077 ;; in (File. (str "/home/r/tmp/mao-test/clojure-images/"1078 ;; ;;/home/r/tmp/mao-test/clojure-images1079 ;; (format "%08d.png" @count)))))))1080 ;; (defn decrease-framerate []1081 ;; (map1082 ;; (copier-gen)1083 ;; (sort1084 ;; (map first1085 ;; (partition1086 ;; 41087 ;; (filter #(re-matches #".*.png$" (.getCanonicalPath %))1088 ;; (file-seq1089 ;; (file-str1090 ;; "/home/r/media/anime/mao-temp/images"))))))))1094 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1096 (defn proprioception1097 "Create a proprioception map that reports the rotations of the1098 various limbs of the creature's body"1099 [creature]1100 [#^Node creature]1101 (let [1102 nodes (node-seq creature)1103 joints1104 (map1105 :joint1106 (filter1107 #(isa? (class %) JointControl)1108 (reduce1109 concat1110 (map (fn [node]1111 (map (fn [num] (.getControl node num))1112 (range (.getNumControls node))))1113 nodes))))]1114 (fn []1115 (reduce concat (map relative-positions (list (first joints)))))))1118 (defn skel [node]1119 (doto1120 (.getSkeleton1121 (.getControl node SkeletonControl))1122 ;; this is necessary to force the skeleton to have accurate world1123 ;; transforms before it is rendered to the screen.1124 (.resetAndUpdate)))1126 (defn green-x-ray []1127 (doto (Material. (asset-manager)1128 "Common/MatDefs/Misc/Unshaded.j3md")1129 (.setColor "Color" ColorRGBA/Green)1130 (-> (.getAdditionalRenderState)1131 (.setDepthTest false))))1133 (defn test-worm []1134 (.start1135 (world1136 (doto (Node.)1137 ;;(.attachChild (point-worm))1138 (.attachChild (load-blender-model1139 "Models/anim2/joint-worm.blend"))1141 (.attachChild (box 10 1 101142 :position (Vector3f. 0 -2 0) :mass 01143 :color (ColorRGBA/Gray))))1144 {1145 "key-space" (fire-cannon-ball)1146 }1147 (fn [world]1148 (enable-debug world)1149 (light-up-everything world)1150 ;;(.setTimer world (NanoTimer.))1151 )1152 no-op)))1156 ;; defunct movement stuff1157 (defn torque-controls [control]1158 (let [torques1159 (concat1160 (map #(Vector3f. 0 (Math/sin %) (Math/cos %))1161 (range 0 (* Math/PI 2) (/ (* Math/PI 2) 20)))1162 [Vector3f/UNIT_X])]1163 (map (fn [torque-axis]1164 (fn [torque]1165 (.applyTorque1166 control1167 (.mult (.mult (.getPhysicsRotation control)1168 torque-axis)1169 (float1170 (* (.getMass control) torque))))))1171 torques)))1173 (defn motor-map1174 "Take a creature and generate a function that will enable fine1175 grained control over all the creature's limbs."1176 [#^Node creature]1177 (let [controls (keep #(.getControl % RigidBodyControl)1178 (node-seq creature))1179 limb-controls (reduce concat (map torque-controls controls))1180 body-control (partial map #(%1 %2) limb-controls)]1181 body-control))1183 (defn test-motor-map1184 "see how torque works."1185 []1186 (let [finger (box 3 0.5 0.5 :position (Vector3f. 0 2 0)1187 :mass 1 :color ColorRGBA/Green)1188 motor-map (motor-map finger)]1189 (world1190 (nodify [finger1191 (box 10 0.5 10 :position (Vector3f. 0 -5 0) :mass 01192 :color ColorRGBA/Gray)])1193 standard-debug-controls1194 (fn [world]1195 (set-gravity world Vector3f/ZERO)1196 (light-up-everything world)1197 (.setTimer world (NanoTimer.)))1198 (fn [_ _]1199 (dorun (motor-map [0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 01200 0]))))))1202 (defn joint-proprioception [#^Node parts #^Node joint]1203 (let [[obj-a obj-b] (joint-targets parts joint)1204 joint-rot (.getWorldRotation joint)1205 pre-inv-a (.inverse (.getWorldRotation obj-a))1206 x (.mult pre-inv-a (.mult joint-rot Vector3f/UNIT_X))1207 y (.mult pre-inv-a (.mult joint-rot Vector3f/UNIT_Y))1208 z (.mult pre-inv-a (.mult joint-rot Vector3f/UNIT_Z))1210 x Vector3f/UNIT_Y1211 y Vector3f/UNIT_Z1212 z Vector3f/UNIT_X1215 tmp-rot-a (.getWorldRotation obj-a)]1216 (println-repl "x:" (.mult tmp-rot-a x))1217 (println-repl "y:" (.mult tmp-rot-a y))1218 (println-repl "z:" (.mult tmp-rot-a z))1219 (println-repl "rot-a" (.getWorldRotation obj-a))1220 (println-repl "rot-b" (.getWorldRotation obj-b))1221 (println-repl "joint-rot" joint-rot)1222 ;; this function will report proprioceptive information for the1223 ;; joint.1224 (fn []1225 ;; x is the "twist" axis, y and z are the "bend" axes1226 (let [rot-a (.getWorldRotation obj-a)1227 ;;inv-a (.inverse rot-a)1228 rot-b (.getWorldRotation obj-b)1229 ;;relative (.mult rot-b inv-a)1230 basis (doto (Matrix3f.)1231 (.setColumn 0 (.mult rot-a x))1232 (.setColumn 1 (.mult rot-a y))1233 (.setColumn 2 (.mult rot-a z)))1234 rotation-about-joint1235 (doto (Quaternion.)1236 (.fromRotationMatrix1237 (.mult (.invert basis)1238 (.toRotationMatrix rot-b))))1239 [yaw roll pitch]1240 (seq (.toAngles rotation-about-joint nil))]1241 ;;return euler angles of the quaternion around the new basis1242 [yaw roll pitch]))))1244 #+end_src1252 * COMMENT generate Source1253 #+begin_src clojure :tangle ../src/cortex/body.clj1254 <<joints>>1255 #+end_src1257 #+begin_src clojure :tangle ../src/cortex/test/body.clj1258 <<test-0>>1259 #+end_src