changeset 466:da311eefbb09

finish body -- needs more work, but whatever.
author Robert McIntyre <rlm@mit.edu>
date Fri, 28 Mar 2014 13:16:37 -0400
parents e4104ce9105c
children ade64947d2bf
files org/body.org thesis/cortex.org thesis/images/physical-hand.png
diffstat 3 files changed, 248 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
     1.1 --- a/org/body.org	Fri Mar 28 11:08:32 2014 -0400
     1.2 +++ b/org/body.org	Fri Mar 28 13:16:37 2014 -0400
     1.3 @@ -228,7 +228,7 @@
     1.4  functions must do three things to translate these into real joints:
     1.5  
     1.6   - Find the children of the "joints" node.
     1.7 - - Determine the two spatials the joint it meant to connect.
     1.8 + - Determine the two spatials the joint is meant to connect.
     1.9   - Create the joint based on the meta-data of the empty node.
    1.10  
    1.11  ** Finding the Joints 
     2.1 --- a/thesis/cortex.org	Fri Mar 28 11:08:32 2014 -0400
     2.2 +++ b/thesis/cortex.org	Fri Mar 28 13:16:37 2014 -0400
     2.3 @@ -21,7 +21,7 @@
     2.4    #+caption: 
     2.5    #+name: name
     2.6    #+ATTR_LaTeX: :width 10cm
     2.7 -  [[./images/Eve.jpg]]
     2.8 +  [[./images/aurellem-gray.png]]
     2.9  
    2.10  
    2.11  
    2.12 @@ -530,16 +530,18 @@
    2.13     While trying to find a good compromise for body-design, one option
    2.14     I ultimately rejected is to use blender's [[http://wiki.blender.org/index.php/Doc:2.6/Manual/Rigging/Armatures][armature]] system. The idea
    2.15     would have been to define a mesh which describes the creature's
    2.16 -   entire body. To this you add an skeleton which deforms this
    2.17 -   mesh. This technique is used extensively to model humans and create
    2.18 -   realistic animations. It is hard to use for my purposes because it
    2.19 -   is difficult to update the creature's Physics Collision Mesh in
    2.20 -   tandem with its Geometric Mesh under the influence of the
    2.21 -   armature. Without this the creature will not be able to grab things
    2.22 -   in its environment, and it won't be able to tell where its physical
    2.23 -   body is by using its eyes. Also, armatures do not specify any
    2.24 -   rotational limits for a joint, making it hard to model elbows,
    2.25 -   shoulders, etc.
    2.26 +   entire body. To this you add a skeleton which deforms this mesh
    2.27 +   (called rigging). This technique is used extensively to model
    2.28 +   humans and create realistic animations. It is not a good technique
    2.29 +   for physical simulation, because deformable surfaces are hard to
    2.30 +   model. Humans work like a squishy bag with some hard bones to give
    2.31 +   it shape. The bones are easy to simulate physically, but they
    2.32 +   interact with thr world though the skin, which is contiguous, but
    2.33 +   does not have a constant shape. In order to simulate skin you need
    2.34 +   some way to continuously update the physical model of the skin
    2.35 +   along with the movement of the bones. Given that bullet is
    2.36 +   optimized for rigid, solid objects, this leads to unmanagable
    2.37 +   computation and incorrect simulation. 
    2.38  
    2.39     Instead of using the human-like ``deformable bag of bones''
    2.40     approach, I decided to base my body plans on multiple solid objects
    2.41 @@ -586,14 +588,248 @@
    2.42     #+caption: node (highlighted in yellow) and its children which each
    2.43     #+caption: represent a joint in the hand. Each joint node has metadata
    2.44     #+caption: specifying what sort of joint it is.
    2.45 +   #+name: blender-hand
    2.46     #+ATTR_LaTeX: :width 10cm
    2.47     [[./images/hand-screenshot1.png]]
    2.48  
    2.49     
    2.50 +   =CORTEX= creates a creature in two steps: first, it traverses the
    2.51 +   nodes in the blender file and creates physical representations for
    2.52 +   any of them that have mass defined.
    2.53 +
    2.54 +   #+caption: Program for iterating through the nodes in a blender file
    2.55 +   #+caption: and generating physical jMonkeyEngine3 objects with mass
    2.56 +   #+caption: and a matching physics shape.
    2.57 +   #+name: name
    2.58 +   #+begin_listing clojure
    2.59 +   #+begin_src clojure
    2.60 +(defn physical!
    2.61 +  "Iterate through the nodes in creature and make them real physical
    2.62 +   objects in the simulation."
    2.63 +  [#^Node creature]
    2.64 +  (dorun
    2.65 +   (map
    2.66 +    (fn [geom]
    2.67 +      (let [physics-control
    2.68 +            (RigidBodyControl.
    2.69 +             (HullCollisionShape.
    2.70 +              (.getMesh geom))
    2.71 +             (if-let [mass (meta-data geom "mass")]
    2.72 +               (float mass) (float 1)))]
    2.73 +        (.addControl geom physics-control)))
    2.74 +    (filter #(isa? (class %) Geometry )
    2.75 +            (node-seq creature)))))
    2.76 +   #+end_src
    2.77 +   #+end_listing
    2.78     
    2.79 +   The next step to making a proper body is to connect those pieces
    2.80 +   together with joints. jMonkeyEngine has a large array of joints
    2.81 +   available via =bullet=, such as Point2Point, Cone, Hinge, and a
    2.82 +   generic Six Degree of Freedom joint, with or without spring
    2.83 +   restitution. =CORTEX='s procedure for binding the creature together
    2.84 +   with joints is as follows:
    2.85  
    2.86 +   - Find the children of the "joints" node.
    2.87 +   - Determine the two spatials the joint is meant to connect.
    2.88 +   - Create the joint based on the meta-data of the empty node.
    2.89 +
    2.90 +   The higher order function =sense-nodes= from =cortex.sense=
    2.91 +   simplifies finding the joints based on their parent ``joints''
    2.92 +   node.
    2.93 +
    2.94 +   #+caption: Retrieving the children empty nodes from a single 
    2.95 +   #+caption: named empty node is a common pattern in =CORTEX=
    2.96 +   #+caption: further instances of this technique for the senses 
    2.97 +   #+caption: will be omitted
    2.98 +   #+name: get-empty-nodes
    2.99 +   #+begin_listing clojure
   2.100 +   #+begin_src clojure
   2.101 +(defn sense-nodes
   2.102 +  "For some senses there is a special empty blender node whose
   2.103 +   children are considered markers for an instance of that sense. This
   2.104 +   function generates functions to find those children, given the name
   2.105 +   of the special parent node."
   2.106 +  [parent-name]
   2.107 +  (fn [#^Node creature]
   2.108 +    (if-let [sense-node (.getChild creature parent-name)]
   2.109 +      (seq (.getChildren sense-node)) [])))
   2.110 +
   2.111 +(def
   2.112 +  ^{:doc "Return the children of the creature's \"joints\" node."
   2.113 +    :arglists '([creature])}
   2.114 +  joints
   2.115 +  (sense-nodes "joints"))
   2.116 +   #+end_src
   2.117 +   #+end_listing
   2.118 +
   2.119 +   To find a joint's targets targets, =CORTEX= creates a small cube,
   2.120 +   centered around the empty-node, and grows the cube exponentially
   2.121 +   until it intersects two /physical/ objects. The objects are ordered
   2.122 +   according to the joint's rotation, with the first one being the
   2.123 +   object that has more negative coordinates in the joint's reference
   2.124 +   frame. Since the objects must be physical, the empty-node itself
   2.125 +   escapes detection. Because the objects must be physical,
   2.126 +   =joint-targets= must be called /after/ =physical!= is called.
   2.127     
   2.128 +   #+caption: Program to find the targets of a joint node by 
   2.129 +   #+caption: exponentiallly growth of a search cube.
   2.130 +   #+name: joint-targets
   2.131 +   #+begin_listing clojure
   2.132 +   #+begin_src clojure
   2.133 +(defn joint-targets
   2.134 +  "Return the two closest two objects to the joint object, ordered
   2.135 +  from bottom to top according to the joint's rotation."
   2.136 +  [#^Node parts #^Node joint]
   2.137 +  (loop [radius (float 0.01)]
   2.138 +    (let [results (CollisionResults.)]
   2.139 +      (.collideWith
   2.140 +       parts
   2.141 +       (BoundingBox. (.getWorldTranslation joint)
   2.142 +                     radius radius radius) results)
   2.143 +      (let [targets
   2.144 +            (distinct
   2.145 +             (map  #(.getGeometry %) results))]
   2.146 +        (if (>= (count targets) 2)
   2.147 +          (sort-by
   2.148 +           #(let [joint-ref-frame-position
   2.149 +                  (jme-to-blender
   2.150 +                   (.mult
   2.151 +                    (.inverse (.getWorldRotation joint))
   2.152 +                    (.subtract (.getWorldTranslation %)
   2.153 +                               (.getWorldTranslation joint))))]
   2.154 +              (.dot (Vector3f. 1 1 1) joint-ref-frame-position))                  
   2.155 +           (take 2 targets))
   2.156 +          (recur (float (* radius 2))))))))
   2.157 +   #+end_src
   2.158 +   #+end_listing
   2.159     
   2.160 +   Once =CORTEX= finds all joints and targets, it creates them using a
   2.161 +   simple dispatch on the metadata of the joint node. 
   2.162 +
   2.163 +   #+caption: Program to dispatch on blender metadata and create joints
   2.164 +   #+caption: sutiable for physical simulation.
   2.165 +   #+name: joint-dispatch
   2.166 +   #+begin_listing clojure
   2.167 +   #+begin_src clojure
   2.168 +(defmulti joint-dispatch
   2.169 +  "Translate blender pseudo-joints into real JME joints."
   2.170 +  (fn [constraints & _] 
   2.171 +    (:type constraints)))
   2.172 +
   2.173 +(defmethod joint-dispatch :point
   2.174 +  [constraints control-a control-b pivot-a pivot-b rotation]
   2.175 +  (doto (SixDofJoint. control-a control-b pivot-a pivot-b false)
   2.176 +    (.setLinearLowerLimit Vector3f/ZERO)
   2.177 +    (.setLinearUpperLimit Vector3f/ZERO)))
   2.178 +
   2.179 +(defmethod joint-dispatch :hinge
   2.180 +  [constraints control-a control-b pivot-a pivot-b rotation]
   2.181 +  (let [axis (if-let [axis (:axis constraints)] axis Vector3f/UNIT_X)
   2.182 +        [limit-1 limit-2] (:limit constraints)
   2.183 +        hinge-axis (.mult rotation (blender-to-jme axis))]
   2.184 +    (doto (HingeJoint. control-a control-b pivot-a pivot-b 
   2.185 +                       hinge-axis hinge-axis)
   2.186 +      (.setLimit limit-1 limit-2))))
   2.187 +
   2.188 +(defmethod joint-dispatch :cone
   2.189 +  [constraints control-a control-b pivot-a pivot-b rotation]
   2.190 +  (let [limit-xz (:limit-xz constraints)
   2.191 +        limit-xy (:limit-xy constraints)
   2.192 +        twist    (:twist constraints)]
   2.193 +    (doto (ConeJoint. control-a control-b pivot-a pivot-b
   2.194 +                      rotation rotation)
   2.195 +      (.setLimit (float limit-xz) (float limit-xy)
   2.196 +                 (float twist)))))
   2.197 +   #+end_src
   2.198 +   #+end_listing
   2.199 +
   2.200 +   All that is left for joints it to combine the above pieces into a
   2.201 +   something that can operate on the collection of nodes that a
   2.202 +   blender file represents.
   2.203 +
   2.204 +   #+caption: Program to completely create a joint given information 
   2.205 +   #+caption: from a blender file.
   2.206 +   #+name: connect
   2.207 +   #+begin_listing clojure
   2.208 +   #+begin_src clojure
   2.209 +(defn connect
   2.210 +  "Create a joint between 'obj-a and 'obj-b at the location of
   2.211 +  'joint. The type of joint is determined by the metadata on 'joint.
   2.212 +
   2.213 +   Here are some examples:
   2.214 +   {:type :point}
   2.215 +   {:type :hinge  :limit [0 (/ Math/PI 2)] :axis (Vector3f. 0 1 0)}
   2.216 +   (:axis defaults to (Vector3f. 1 0 0) if not provided for hinge joints)
   2.217 +
   2.218 +   {:type :cone :limit-xz 0]
   2.219 +                :limit-xy 0]
   2.220 +                :twist 0]}   (use XZY rotation mode in blender!)"
   2.221 +  [#^Node obj-a #^Node obj-b #^Node joint]
   2.222 +  (let [control-a (.getControl obj-a RigidBodyControl)
   2.223 +        control-b (.getControl obj-b RigidBodyControl)
   2.224 +        joint-center (.getWorldTranslation joint)
   2.225 +        joint-rotation (.toRotationMatrix (.getWorldRotation joint))
   2.226 +        pivot-a (world-to-local obj-a joint-center)
   2.227 +        pivot-b (world-to-local obj-b joint-center)]
   2.228 +    (if-let
   2.229 +        [constraints (map-vals eval (read-string (meta-data joint "joint")))]
   2.230 +      ;; A side-effect of creating a joint registers
   2.231 +      ;; it with both physics objects which in turn
   2.232 +      ;; will register the joint with the physics system
   2.233 +      ;; when the simulation is started.
   2.234 +        (joint-dispatch constraints
   2.235 +                        control-a control-b
   2.236 +                        pivot-a pivot-b
   2.237 +                        joint-rotation))))
   2.238 +   #+end_src
   2.239 +   #+end_listing
   2.240 +
   2.241 +   In general, whenever =CORTEX= exposes a sense (or in this case
   2.242 +   physicality), it provides a function of the type =sense!=, which
   2.243 +   takes in a collection of nodes and augments it to support that
   2.244 +   sense. The function returns any controlls necessary to use that
   2.245 +   sense. In this case =body!= cerates a physical body and returns no
   2.246 +   control functions.
   2.247 +
   2.248 +   #+caption: Program to give joints to a creature.
   2.249 +   #+name: name
   2.250 +   #+begin_listing clojure
   2.251 +   #+begin_src clojure
   2.252 +(defn joints!
   2.253 +  "Connect the solid parts of the creature with physical joints. The
   2.254 +   joints are taken from the \"joints\" node in the creature."
   2.255 +  [#^Node creature]
   2.256 +  (dorun
   2.257 +   (map
   2.258 +    (fn [joint]
   2.259 +      (let [[obj-a obj-b] (joint-targets creature joint)]
   2.260 +        (connect obj-a obj-b joint)))
   2.261 +    (joints creature))))
   2.262 +(defn body!
   2.263 +  "Endow the creature with a physical body connected with joints.  The
   2.264 +   particulars of the joints and the masses of each body part are
   2.265 +   determined in blender."
   2.266 +  [#^Node creature]
   2.267 +  (physical! creature)
   2.268 +  (joints! creature))
   2.269 +   #+end_src
   2.270 +   #+end_listing
   2.271 +
   2.272 +   All of the code you have just seen amounts to only 130 lines, yet
   2.273 +   because it builds on top of Blender and jMonkeyEngine3, those few
   2.274 +   lines pack quite a punch!
   2.275 +
   2.276 +   The hand from figure \ref{blender-hand}, which was modeled after my
   2.277 +   own right hand, can now be given joints and simulated as a
   2.278 +   creature.
   2.279 +   
   2.280 +   #+caption: With the ability to create physical creatures from blender,
   2.281 +   #+caption: =CORTEX= gets one step closer to a full creature simulation
   2.282 +   #+caption: environment.
   2.283 +   #+name: name
   2.284 +   #+ATTR_LaTeX: :width 15cm
   2.285 +   [[./images/physical-hand.png]]
   2.286 +
   2.287  
   2.288  ** Eyes reuse standard video game components
   2.289  
     3.1 Binary file thesis/images/physical-hand.png has changed