Mercurial > cortex
comparison thesis/cortex.org @ 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 |
comparison
equal
deleted
inserted
replaced
465:e4104ce9105c | 466:da311eefbb09 |
---|---|
19 #+caption: | 19 #+caption: |
20 #+caption: | 20 #+caption: |
21 #+caption: | 21 #+caption: |
22 #+name: name | 22 #+name: name |
23 #+ATTR_LaTeX: :width 10cm | 23 #+ATTR_LaTeX: :width 10cm |
24 [[./images/Eve.jpg]] | 24 [[./images/aurellem-gray.png]] |
25 | 25 |
26 | 26 |
27 | 27 |
28 * COMMENT Empathy and Embodiment as problem solving strategies | 28 * COMMENT Empathy and Embodiment as problem solving strategies |
29 | 29 |
528 engine, and must also be easy to rig with =CORTEX='s senses. | 528 engine, and must also be easy to rig with =CORTEX='s senses. |
529 | 529 |
530 While trying to find a good compromise for body-design, one option | 530 While trying to find a good compromise for body-design, one option |
531 I ultimately rejected is to use blender's [[http://wiki.blender.org/index.php/Doc:2.6/Manual/Rigging/Armatures][armature]] system. The idea | 531 I ultimately rejected is to use blender's [[http://wiki.blender.org/index.php/Doc:2.6/Manual/Rigging/Armatures][armature]] system. The idea |
532 would have been to define a mesh which describes the creature's | 532 would have been to define a mesh which describes the creature's |
533 entire body. To this you add an skeleton which deforms this | 533 entire body. To this you add a skeleton which deforms this mesh |
534 mesh. This technique is used extensively to model humans and create | 534 (called rigging). This technique is used extensively to model |
535 realistic animations. It is hard to use for my purposes because it | 535 humans and create realistic animations. It is not a good technique |
536 is difficult to update the creature's Physics Collision Mesh in | 536 for physical simulation, because deformable surfaces are hard to |
537 tandem with its Geometric Mesh under the influence of the | 537 model. Humans work like a squishy bag with some hard bones to give |
538 armature. Without this the creature will not be able to grab things | 538 it shape. The bones are easy to simulate physically, but they |
539 in its environment, and it won't be able to tell where its physical | 539 interact with thr world though the skin, which is contiguous, but |
540 body is by using its eyes. Also, armatures do not specify any | 540 does not have a constant shape. In order to simulate skin you need |
541 rotational limits for a joint, making it hard to model elbows, | 541 some way to continuously update the physical model of the skin |
542 shoulders, etc. | 542 along with the movement of the bones. Given that bullet is |
543 optimized for rigid, solid objects, this leads to unmanagable | |
544 computation and incorrect simulation. | |
543 | 545 |
544 Instead of using the human-like ``deformable bag of bones'' | 546 Instead of using the human-like ``deformable bag of bones'' |
545 approach, I decided to base my body plans on multiple solid objects | 547 approach, I decided to base my body plans on multiple solid objects |
546 that are connected by joints, inspired by the robot =EVE= from the | 548 that are connected by joints, inspired by the robot =EVE= from the |
547 movie WALL-E. | 549 movie WALL-E. |
584 | 586 |
585 #+caption: View of the hand model in Blender showing the main ``joints'' | 587 #+caption: View of the hand model in Blender showing the main ``joints'' |
586 #+caption: node (highlighted in yellow) and its children which each | 588 #+caption: node (highlighted in yellow) and its children which each |
587 #+caption: represent a joint in the hand. Each joint node has metadata | 589 #+caption: represent a joint in the hand. Each joint node has metadata |
588 #+caption: specifying what sort of joint it is. | 590 #+caption: specifying what sort of joint it is. |
591 #+name: blender-hand | |
589 #+ATTR_LaTeX: :width 10cm | 592 #+ATTR_LaTeX: :width 10cm |
590 [[./images/hand-screenshot1.png]] | 593 [[./images/hand-screenshot1.png]] |
591 | 594 |
592 | 595 |
593 | 596 =CORTEX= creates a creature in two steps: first, it traverses the |
594 | 597 nodes in the blender file and creates physical representations for |
595 | 598 any of them that have mass defined. |
596 | 599 |
600 #+caption: Program for iterating through the nodes in a blender file | |
601 #+caption: and generating physical jMonkeyEngine3 objects with mass | |
602 #+caption: and a matching physics shape. | |
603 #+name: name | |
604 #+begin_listing clojure | |
605 #+begin_src clojure | |
606 (defn physical! | |
607 "Iterate through the nodes in creature and make them real physical | |
608 objects in the simulation." | |
609 [#^Node creature] | |
610 (dorun | |
611 (map | |
612 (fn [geom] | |
613 (let [physics-control | |
614 (RigidBodyControl. | |
615 (HullCollisionShape. | |
616 (.getMesh geom)) | |
617 (if-let [mass (meta-data geom "mass")] | |
618 (float mass) (float 1)))] | |
619 (.addControl geom physics-control))) | |
620 (filter #(isa? (class %) Geometry ) | |
621 (node-seq creature))))) | |
622 #+end_src | |
623 #+end_listing | |
624 | |
625 The next step to making a proper body is to connect those pieces | |
626 together with joints. jMonkeyEngine has a large array of joints | |
627 available via =bullet=, such as Point2Point, Cone, Hinge, and a | |
628 generic Six Degree of Freedom joint, with or without spring | |
629 restitution. =CORTEX='s procedure for binding the creature together | |
630 with joints is as follows: | |
631 | |
632 - Find the children of the "joints" node. | |
633 - Determine the two spatials the joint is meant to connect. | |
634 - Create the joint based on the meta-data of the empty node. | |
635 | |
636 The higher order function =sense-nodes= from =cortex.sense= | |
637 simplifies finding the joints based on their parent ``joints'' | |
638 node. | |
639 | |
640 #+caption: Retrieving the children empty nodes from a single | |
641 #+caption: named empty node is a common pattern in =CORTEX= | |
642 #+caption: further instances of this technique for the senses | |
643 #+caption: will be omitted | |
644 #+name: get-empty-nodes | |
645 #+begin_listing clojure | |
646 #+begin_src clojure | |
647 (defn sense-nodes | |
648 "For some senses there is a special empty blender node whose | |
649 children are considered markers for an instance of that sense. This | |
650 function generates functions to find those children, given the name | |
651 of the special parent node." | |
652 [parent-name] | |
653 (fn [#^Node creature] | |
654 (if-let [sense-node (.getChild creature parent-name)] | |
655 (seq (.getChildren sense-node)) []))) | |
656 | |
657 (def | |
658 ^{:doc "Return the children of the creature's \"joints\" node." | |
659 :arglists '([creature])} | |
660 joints | |
661 (sense-nodes "joints")) | |
662 #+end_src | |
663 #+end_listing | |
664 | |
665 To find a joint's targets targets, =CORTEX= creates a small cube, | |
666 centered around the empty-node, and grows the cube exponentially | |
667 until it intersects two /physical/ objects. The objects are ordered | |
668 according to the joint's rotation, with the first one being the | |
669 object that has more negative coordinates in the joint's reference | |
670 frame. Since the objects must be physical, the empty-node itself | |
671 escapes detection. Because the objects must be physical, | |
672 =joint-targets= must be called /after/ =physical!= is called. | |
673 | |
674 #+caption: Program to find the targets of a joint node by | |
675 #+caption: exponentiallly growth of a search cube. | |
676 #+name: joint-targets | |
677 #+begin_listing clojure | |
678 #+begin_src clojure | |
679 (defn joint-targets | |
680 "Return the two closest two objects to the joint object, ordered | |
681 from bottom to top according to the joint's rotation." | |
682 [#^Node parts #^Node joint] | |
683 (loop [radius (float 0.01)] | |
684 (let [results (CollisionResults.)] | |
685 (.collideWith | |
686 parts | |
687 (BoundingBox. (.getWorldTranslation joint) | |
688 radius radius radius) results) | |
689 (let [targets | |
690 (distinct | |
691 (map #(.getGeometry %) results))] | |
692 (if (>= (count targets) 2) | |
693 (sort-by | |
694 #(let [joint-ref-frame-position | |
695 (jme-to-blender | |
696 (.mult | |
697 (.inverse (.getWorldRotation joint)) | |
698 (.subtract (.getWorldTranslation %) | |
699 (.getWorldTranslation joint))))] | |
700 (.dot (Vector3f. 1 1 1) joint-ref-frame-position)) | |
701 (take 2 targets)) | |
702 (recur (float (* radius 2)))))))) | |
703 #+end_src | |
704 #+end_listing | |
705 | |
706 Once =CORTEX= finds all joints and targets, it creates them using a | |
707 simple dispatch on the metadata of the joint node. | |
708 | |
709 #+caption: Program to dispatch on blender metadata and create joints | |
710 #+caption: sutiable for physical simulation. | |
711 #+name: joint-dispatch | |
712 #+begin_listing clojure | |
713 #+begin_src clojure | |
714 (defmulti joint-dispatch | |
715 "Translate blender pseudo-joints into real JME joints." | |
716 (fn [constraints & _] | |
717 (:type constraints))) | |
718 | |
719 (defmethod joint-dispatch :point | |
720 [constraints control-a control-b pivot-a pivot-b rotation] | |
721 (doto (SixDofJoint. control-a control-b pivot-a pivot-b false) | |
722 (.setLinearLowerLimit Vector3f/ZERO) | |
723 (.setLinearUpperLimit Vector3f/ZERO))) | |
724 | |
725 (defmethod joint-dispatch :hinge | |
726 [constraints control-a control-b pivot-a pivot-b rotation] | |
727 (let [axis (if-let [axis (:axis constraints)] axis Vector3f/UNIT_X) | |
728 [limit-1 limit-2] (:limit constraints) | |
729 hinge-axis (.mult rotation (blender-to-jme axis))] | |
730 (doto (HingeJoint. control-a control-b pivot-a pivot-b | |
731 hinge-axis hinge-axis) | |
732 (.setLimit limit-1 limit-2)))) | |
733 | |
734 (defmethod joint-dispatch :cone | |
735 [constraints control-a control-b pivot-a pivot-b rotation] | |
736 (let [limit-xz (:limit-xz constraints) | |
737 limit-xy (:limit-xy constraints) | |
738 twist (:twist constraints)] | |
739 (doto (ConeJoint. control-a control-b pivot-a pivot-b | |
740 rotation rotation) | |
741 (.setLimit (float limit-xz) (float limit-xy) | |
742 (float twist))))) | |
743 #+end_src | |
744 #+end_listing | |
745 | |
746 All that is left for joints it to combine the above pieces into a | |
747 something that can operate on the collection of nodes that a | |
748 blender file represents. | |
749 | |
750 #+caption: Program to completely create a joint given information | |
751 #+caption: from a blender file. | |
752 #+name: connect | |
753 #+begin_listing clojure | |
754 #+begin_src clojure | |
755 (defn connect | |
756 "Create a joint between 'obj-a and 'obj-b at the location of | |
757 'joint. The type of joint is determined by the metadata on 'joint. | |
758 | |
759 Here are some examples: | |
760 {:type :point} | |
761 {:type :hinge :limit [0 (/ Math/PI 2)] :axis (Vector3f. 0 1 0)} | |
762 (:axis defaults to (Vector3f. 1 0 0) if not provided for hinge joints) | |
763 | |
764 {:type :cone :limit-xz 0] | |
765 :limit-xy 0] | |
766 :twist 0]} (use XZY rotation mode in blender!)" | |
767 [#^Node obj-a #^Node obj-b #^Node joint] | |
768 (let [control-a (.getControl obj-a RigidBodyControl) | |
769 control-b (.getControl obj-b RigidBodyControl) | |
770 joint-center (.getWorldTranslation joint) | |
771 joint-rotation (.toRotationMatrix (.getWorldRotation joint)) | |
772 pivot-a (world-to-local obj-a joint-center) | |
773 pivot-b (world-to-local obj-b joint-center)] | |
774 (if-let | |
775 [constraints (map-vals eval (read-string (meta-data joint "joint")))] | |
776 ;; A side-effect of creating a joint registers | |
777 ;; it with both physics objects which in turn | |
778 ;; will register the joint with the physics system | |
779 ;; when the simulation is started. | |
780 (joint-dispatch constraints | |
781 control-a control-b | |
782 pivot-a pivot-b | |
783 joint-rotation)))) | |
784 #+end_src | |
785 #+end_listing | |
786 | |
787 In general, whenever =CORTEX= exposes a sense (or in this case | |
788 physicality), it provides a function of the type =sense!=, which | |
789 takes in a collection of nodes and augments it to support that | |
790 sense. The function returns any controlls necessary to use that | |
791 sense. In this case =body!= cerates a physical body and returns no | |
792 control functions. | |
793 | |
794 #+caption: Program to give joints to a creature. | |
795 #+name: name | |
796 #+begin_listing clojure | |
797 #+begin_src clojure | |
798 (defn joints! | |
799 "Connect the solid parts of the creature with physical joints. The | |
800 joints are taken from the \"joints\" node in the creature." | |
801 [#^Node creature] | |
802 (dorun | |
803 (map | |
804 (fn [joint] | |
805 (let [[obj-a obj-b] (joint-targets creature joint)] | |
806 (connect obj-a obj-b joint))) | |
807 (joints creature)))) | |
808 (defn body! | |
809 "Endow the creature with a physical body connected with joints. The | |
810 particulars of the joints and the masses of each body part are | |
811 determined in blender." | |
812 [#^Node creature] | |
813 (physical! creature) | |
814 (joints! creature)) | |
815 #+end_src | |
816 #+end_listing | |
817 | |
818 All of the code you have just seen amounts to only 130 lines, yet | |
819 because it builds on top of Blender and jMonkeyEngine3, those few | |
820 lines pack quite a punch! | |
821 | |
822 The hand from figure \ref{blender-hand}, which was modeled after my | |
823 own right hand, can now be given joints and simulated as a | |
824 creature. | |
825 | |
826 #+caption: With the ability to create physical creatures from blender, | |
827 #+caption: =CORTEX= gets one step closer to a full creature simulation | |
828 #+caption: environment. | |
829 #+name: name | |
830 #+ATTR_LaTeX: :width 15cm | |
831 [[./images/physical-hand.png]] | |
832 | |
597 | 833 |
598 ** Eyes reuse standard video game components | 834 ** Eyes reuse standard video game components |
599 | 835 |
600 ** Hearing is hard; =CORTEX= does it right | 836 ** Hearing is hard; =CORTEX= does it right |
601 | 837 |