Mercurial > cortex
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 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.43 * Solidifying the Body45 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-054 #+begin_src clojure55 (ns cortex.test.body56 (: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 (.setLocation69 cam (Vector3f.70 -6.9015837, 8.644911, 5.6043186))71 (.setRotation72 cam73 (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-controls82 (comp83 #(Capture/captureVideo84 % (File. "/home/r/proj/cortex/render/body/1"))85 setup)86 no-op))87 #+end_src90 #+begin_src clojure :results silent91 (.start (cortex.test.body/test-one))92 #+end_src94 #+begin_html95 <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_html101 You will notice that the hand has no physical presence -- it's a102 hologram through witch everything passes. Therefore, the first thing103 to do is to make it solid. Blender has physics simulation on par with104 jMonkeyEngine (they both use bullet as their physics backend), but it105 can be difficult to translate between the two systems, so for now I106 specify the mass of each object in blender and construct the physics107 shape based on the mesh in jMonkeyEngine.109 #+name: joints-1110 #+begin_src clojure111 (defn physical!112 "Iterate through the nodes in creature and make them real physical113 objects in the simulation."114 [#^Node creature]115 (dorun116 (map117 (fn [geom]118 (let [physics-control119 (RigidBodyControl.120 (HullCollisionShape.121 (.getMesh geom))122 (if-let [mass (meta-data geom "mass")]123 (do124 (println-repl125 "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_src133 =(physical!)= iterates through a creature's node structure, creating134 CollisionShapes for each geometry with the mass specified in that135 geometry's meta-data.137 #+begin_src clojure138 (in-ns 'cortex.test.body)140 (def normal-gravity141 {"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 (nodify150 [(doto (hand)151 (physical!))152 (floor)])153 (merge standard-debug-controls normal-gravity)154 (comp155 #(Capture/captureVideo156 % (File. "/home/r/proj/cortex/render/body/2"))157 #(do (set-gravity % Vector3f/ZERO) %)158 setup)159 no-op))160 #+end_src162 #+begin_html163 <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_html169 Now that's some progress.172 * Joints174 Obviously, an AI is not going to be doing much just lying in pieces on175 the floor. So, the next step to making a proper body is to connect176 those pieces together with joints. jMonkeyEngine has a large array of177 joints available via bullet, such as Point2Point, Cone, Hinge, and a178 generic Six Degree of Freedom joint, with or without spring179 restitution.181 Although it should be possible to specify the joints using blender's182 physics system, and then automatically import them with jMonkeyEngine,183 the support isn't there yet, and there are a few problems with bullet184 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 for187 some senses. Each joint is specified by an empty node whose parent188 has the name "joints". Their orientation and meta-data determine what189 joint is created.191 [[../images/hand-screenshot1.png]]196 #+name: joints-2197 #+begin_src clojure198 (defn joint-targets199 "Return the two closest two objects to the joint object, ordered200 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 (.collideWith205 parts206 (BoundingBox. (.getWorldTranslation joint)207 radius radius radius)208 results)209 (let [targets210 (distinct211 (map #(.getGeometry %) results))]212 (if (>= (count targets) 2)213 (sort-by214 #(let [v215 (jme-to-blender216 (.mult217 (.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-dispatch227 "Translate blender pseudo-joints into real JME joints."228 (fn [constraints & _]229 (:type constraints)))231 (defmethod joint-dispatch :point232 [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 the235 ;; generic 6DOF joint instead of an actual Point2Point joint!237 ;; should be able to do this:238 (comment239 (Point2PointJoint.240 control-a241 control-b242 pivot-a243 pivot-b))245 ;; but instead we must do this:246 (println-repl "substuting 6DOF joint for POINT2POINT joint!")247 (doto248 (SixDofJoint.249 control-a250 control-b251 pivot-a252 pivot-b253 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 :hinge263 [constraints control-a control-b pivot-a pivot-b rotation]264 (println-repl "creating HINGE joint")265 (let [axis266 (if-let267 [axis (:axis constraints)]268 axis269 Vector3f/UNIT_X)270 [limit-1 limit-2] (:limit constraints)271 hinge-axis272 (.mult273 rotation274 (blender-to-jme axis))]275 (doto276 (HingeJoint.277 control-a278 control-b279 pivot-a280 pivot-b281 hinge-axis282 hinge-axis)283 (.setLimit limit-1 limit-2))))285 (defmethod joint-dispatch :cone286 [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-repl294 "UNIT_X --> " (.mult rotation (Vector3f. 1 0 0)))295 (println-repl296 "UNIT_Y --> " (.mult rotation (Vector3f. 0 1 0)))297 (println-repl298 "UNIT_Z --> " (.mult rotation (Vector3f. 0 0 1)))299 (doto300 (ConeJoint.301 control-a302 control-b303 pivot-a304 pivot-b305 rotation306 rotation)307 (.setLimit (float limit-xz)308 (float limit-xy)309 (float twist)))))311 (defn connect312 "Create a joint between 'obj-a and 'obj-b at the location of313 '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 [constraints332 (map-vals333 eval334 (read-string335 (meta-data joint "joint")))]336 ;; A side-effect of creating a joint registers337 ;; it with both physics objects which in turn338 ;; will register the joint with the physics system339 ;; when the simulation is started.340 (do341 (println-repl "creating joint between"342 (.getName obj-a) "and" (.getName obj-b))343 (joint-dispatch constraints344 control-a control-b345 pivot-a pivot-b346 joint-rotation))347 (println-repl "could not find joint meta-data!"))))349 (defvar350 ^{:arglists '([creature])}351 joints352 (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. The357 joints are taken from the \"joints\" node in the creature."358 [#^Node creature]359 (dorun360 (map361 (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. The368 particulars of the joints and the masses of each pody part are369 determined in blender."370 [#^Node creature]371 (physical! creature)372 (joints! creature))373 #+end_src375 * Bookkeeping377 #+name: body-0378 #+begin_src clojure379 (ns cortex.body380 "Assemble a physical creature using the definitions found in a381 specially prepared blender file. Creates rigid bodies and joints so382 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 (:import387 (com.jme3.math Vector3f Quaternion Vector2f Matrix3f)388 (com.jme3.bullet.joints389 SixDofJoint Point2PointJoint HingeJoint ConeJoint)390 com.jme3.bullet.control.RigidBodyControl391 com.jme3.collision.CollisionResults392 com.jme3.bounding.BoundingBox393 com.jme3.scene.Node394 com.jme3.scene.Geometry395 com.jme3.bullet.collision.shapes.HullCollisionShape))396 #+end_src398 * Source400 * COMMENT Examples402 #+name: test-body403 #+begin_src clojure404 (ns cortex.test.body405 (:use (cortex world util body))406 (:require cortex.silly)407 (:import408 com.jme3.math.Vector3f409 com.jme3.math.ColorRGBA410 com.jme3.bullet.joints.Point2PointJoint411 com.jme3.bullet.control.RigidBodyControl412 com.jme3.system.NanoTimer413 com.jme3.math.Quaternion))415 (defn worm-segments416 "Create multiple evenly spaced box segments. They're fabulous!"417 [segment-length num-segments interstitial-space radius]418 (letfn [(nth-segment419 [n]420 (box segment-length radius radius :mass 0.1421 :position422 (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-midpoint429 "Connect two physics objects with a Point2Point joint constraint at430 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 registers439 ;; it with both physics objects which in turn440 ;; will register the joint with the physics system441 ;; when the simulation is started.442 joint (Point2PointJoint.443 (.getControl segmentA RigidBodyControl)444 (.getControl segmentB RigidBodyControl)445 pivotA446 pivotB)]447 segmentB))449 (defn eve-worm450 "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-pattern458 "This is a simple, mindless motor control pattern that drives the459 second segment of the worm's body at an offset angle with460 sinusoidally varying strength."461 [time]462 (let [angle (* Math/PI (/ 9 20))463 direction (Vector3f. 0 (Math/sin angle) (Math/cos angle))]464 [Vector3f/ZERO465 (.mult466 direction467 (float (* 2 (Math/sin (* Math/PI 2 (/ (rem time 300 ) 300))))))468 Vector3f/ZERO469 Vector3f/ZERO470 Vector3f/ZERO]))472 (defn test-motor-control473 "Testing motor-control:474 You should see a multi-segmented worm-like object fall onto the475 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 (world481 (nodify [worm482 (box 10 0.5 10 :position (Vector3f. 0 -5 0) :mass 0483 :color ColorRGBA/Gray)])484 standard-debug-controls485 (fn [world]486 (enable-debug world)487 (light-up-everything world)488 (comment489 (com.aurellem.capture.Capture/captureVideo490 world491 (file-str "/home/r/proj/cortex/tmp/moving-worm")))492 )494 (fn [_ _]495 (swap! time inc)496 (Thread/sleep 20)497 (dorun (worm-motor-map498 (worm-pattern @time)))))))502 (defn join-at-point [obj-a obj-b world-pivot]503 (cortex.silly/joint-dispatch504 {: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 nil510 ))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 (.setCollisionGroup525 (.getControl hand RigidBodyControl)526 PhysicsCollisionObject/COLLISION_GROUP_NONE)527 (world528 root529 standard-debug-controls530 (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 (comment538 (defn proprioception-debug-window539 []540 (let [time (atom 0)]541 (fn [prop-data]542 (if (= 0 (rem (swap! time inc) 40))543 (println-repl prop-data)))))544 )546 (comment547 (dorun548 (map549 (comp550 println-repl551 (fn [[p y r]]552 (format553 "pitch: %1.2f\nyaw: %1.2f\nroll: %1.2f\n"554 p y r)))555 prop-data)))560 (defn test-proprioception561 "Testing proprioception:562 You should see two foating bars, and a printout of pitch, yaw, and563 roll. Pressing key-r/key-t should move the blue bar up and down and564 change only the value of pitch. key-f/key-g moves it side to side565 and changes yaw. key-v/key-b will spin the blue segment clockwise566 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/Yellow573 :position (Vector3f. 0 1.2 0)574 :rotation (doto (Quaternion.)575 (.fromAngleAxis576 (/ 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 (let586 ;; *******************************************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 controls596 (merge standard-debug-controls597 {"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 (.fromAngleAxis613 (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 (comment627 (.setCollisionGroup628 (.getControl hand RigidBodyControl)629 PhysicsCollisionObject/COLLISION_GROUP_NONE)630 )631 (apply632 world633 (with-movement634 hand635 ["key-y" "key-u" "key-h" "key-j" "key-n" "key-m"]636 [10 10 10 10 1 1]637 (with-movement638 finger639 ["key-r" "key-t" "key-f" "key-g" "key-v" "key-b"]640 [1 1 10 10 10 10]641 [root642 controls643 (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_src651 #+results: test-body652 : #'cortex.test.body/test-proprioception655 * COMMENT code-limbo656 #+begin_src clojure657 ;;(.loadModel658 ;; (doto (asset-manager)659 ;; (.registerLoader BlenderModelLoader (into-array String ["blend"])))660 ;; "Models/person/person.blend")663 (defn load-blender-model664 "Load a .blend file using an asset folder relative path."665 [^String model]666 (.loadModel667 (doto (asset-manager)668 (.registerLoader BlenderModelLoader (into-array String ["blend"])))669 model))672 (defn view-model [^String model]673 (view674 (.loadModel675 (doto (asset-manager)676 (.registerLoader BlenderModelLoader (into-array String ["blend"])))677 model)))679 (defn load-blender-scene [^String model]680 (.loadModel681 (doto (asset-manager)682 (.registerLoader BlenderLoader (into-array String ["blend"])))683 model))685 (defn worm686 []687 (.loadModel (asset-manager) "Models/anim2/Cube.mesh.xml"))689 (defn oto690 []691 (.loadModel (asset-manager) "Models/Oto/Oto.mesh.xml"))693 (defn sinbad694 []695 (.loadModel (asset-manager) "Models/Sinbad/Sinbad.mesh.xml"))697 (defn worm-blender698 []699 (first (seq (.getChildren (load-blender-model700 "Models/anim2/simple-worm.blend")))))702 (defn body703 "given a node with a SkeletonControl, will produce a body sutiable704 for AI control with movement and proprioception."705 [node]706 (let [skeleton-control (.getControl node SkeletonControl)707 krc (KinematicRagdollControl.)]708 (comment709 (dorun710 (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 node718 )719 (defn show-skeleton [node]720 (let [sd722 (doto723 (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 like734 ;; being eyes and the like736 (.getUserData737 (.getChild738 (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-node748 [f debug-node skeleton]749 (let [bones750 (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 (dorun758 (map #(.attachChild759 debug-node760 (doto761 (sphere 0.1762 :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 (dorun776 (map777 #(.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 b785 Vector3f/UNIT_X786 Quaternion/IDENTITY787 ;;(doto (Quaternion.)788 ;; (.fromAngles (/ Math/PI 2)789 ;; 0790 ;; 0792 (Vector3f. 1 1 1))793 )795 (println-repl "hi! <3"))))798 (defn test-ragdoll []800 (let [the-worm802 ;;(.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 (.start813 (world814 (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/createPhysicsTestWorld822 (.getRootNode world)823 (asset-manager)824 (.getPhysicsSpace825 (.getState (.getStateManager world) BulletAppState)))826 (set-gravity world Vector3f/ZERO)827 ;;(.setTimer world (NanoTimer.))828 ;;(org.lwjgl.input.Mouse/setGrabbed false)829 )830 no-op831 )834 )))837 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;838 ;;; here is the ragdoll stuff840 (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 (.get850 (.getFloatBuffer worm-mesh VertexBuffer$Type/Position)851 index))853 (defn bones [index]854 (.get855 (.getData (.getBuffer mesh VertexBuffer$Type/BoneIndex))856 index))858 (defn bone-weights [index]859 (.get860 (.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/Red881 [1 2 0 0] ColorRGBA/Magenta882 [2 0 0 0] ColorRGBA/Blue}883 (vertex-bones index)884 ColorRGBA/White))886 (defn influence-color [index bone-num]887 (get888 {(float 0) ColorRGBA/Blue889 (float 0.5) ColorRGBA/Green890 (float 1) ColorRGBA/Red}891 ;; find the weight of the desired bone892 ((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 (dorun902 (map #(.attachChild points %)903 (map #(sphere 0.01904 :position (vertex-position %)905 :color (influence-color % 1)906 :physical? false)907 (range 60))))908 (view points)))911 (defrecord JointControl [joint physics-space]912 PhysicsControl913 (setPhysicsSpace [this space]914 (dosync915 (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-joint925 "Add a joint to a particular object. When the object is added to the926 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-world934 []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/copy955 ;; in (File. (str "/home/r/tmp/mao-test/clojure-images/"956 ;; ;;/home/r/tmp/mao-test/clojure-images957 ;; (format "%08d.png" @count)))))))958 ;; (defn decrease-framerate []959 ;; (map960 ;; (copier-gen)961 ;; (sort962 ;; (map first963 ;; (partition964 ;; 4965 ;; (filter #(re-matches #".*.png$" (.getCanonicalPath %))966 ;; (file-seq967 ;; (file-str968 ;; "/home/r/media/anime/mao-temp/images"))))))))972 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;974 (defn proprioception975 "Create a proprioception map that reports the rotations of the976 various limbs of the creature's body"977 [creature]978 [#^Node creature]979 (let [980 nodes (node-seq creature)981 joints982 (map983 :joint984 (filter985 #(isa? (class %) JointControl)986 (reduce987 concat988 (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 (doto998 (.getSkeleton999 (.getControl node SkeletonControl))1000 ;; this is necessary to force the skeleton to have accurate world1001 ;; 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 (.start1013 (world1014 (doto (Node.)1015 ;;(.attachChild (point-worm))1016 (.attachChild (load-blender-model1017 "Models/anim2/joint-worm.blend"))1019 (.attachChild (box 10 1 101020 :position (Vector3f. 0 -2 0) :mass 01021 :color (ColorRGBA/Gray))))1022 {1023 "key-space" (fire-cannon-ball)1024 }1025 (fn [world]1026 (enable-debug world)1027 (light-up-everything world)1028 ;;(.setTimer world (NanoTimer.))1029 )1030 no-op)))1034 ;; defunct movement stuff1035 (defn torque-controls [control]1036 (let [torques1037 (concat1038 (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 (.applyTorque1044 control1045 (.mult (.mult (.getPhysicsRotation control)1046 torque-axis)1047 (float1048 (* (.getMass control) torque))))))1049 torques)))1051 (defn motor-map1052 "Take a creature and generate a function that will enable fine1053 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-map1062 "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 (world1068 (nodify [finger1069 (box 10 0.5 10 :position (Vector3f. 0 -5 0) :mass 01070 :color ColorRGBA/Gray)])1071 standard-debug-controls1072 (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 01078 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_Y1089 y Vector3f/UNIT_Z1090 z Vector3f/UNIT_X1093 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 the1101 ;; joint.1102 (fn []1103 ;; x is the "twist" axis, y and z are the "bend" axes1104 (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-joint1113 (doto (Quaternion.)1114 (.fromRotationMatrix1115 (.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 basis1120 [yaw roll pitch]))))1122 #+end_src1130 * COMMENT generate Source1131 #+begin_src clojure :tangle ../src/cortex/body.clj1132 <<joints>>1133 #+end_src1135 #+begin_src clojure :tangle ../src/cortex/test/body.clj1136 <<test-0>>1137 #+end_src