From 6310e1b24c0a59860635d3bb13f38b537444b431 Mon Sep 17 00:00:00 2001
From: Rainer Kartmann <rainer.kartmann@student.kit.edu>
Date: Tue, 13 Nov 2018 11:30:43 +0100
Subject: [PATCH] Refactored pose/axis update (now forward instead of
 backwards)

---
 .../examples/MjcfConverter/MjcfConverter.cpp  |   4 +-
 .../examples/MjcfConverter/MjcfConverter.h    |   7 +-
 VirtualRobot/examples/MjcfConverter/main.cpp  |   1 -
 .../MjcfConverter/mjcf/MjcfDocument.cpp       |  16 --
 .../MjcfConverter/mjcf/MjcfDocument.h         |  20 +-
 .../mjcf/MjcfMasslessBodySanitizer.cpp        | 182 +++++++++++-------
 .../mjcf/MjcfMasslessBodySanitizer.h          |  11 +-
 .../examples/MjcfConverter/mjcf/utils.cpp     |  20 ++
 .../examples/MjcfConverter/mjcf/utils.h       |  30 ++-
 9 files changed, 175 insertions(+), 116 deletions(-)

diff --git a/VirtualRobot/examples/MjcfConverter/MjcfConverter.cpp b/VirtualRobot/examples/MjcfConverter/MjcfConverter.cpp
index 3d18e8cfc..c9d7237a0 100644
--- a/VirtualRobot/examples/MjcfConverter/MjcfConverter.cpp
+++ b/VirtualRobot/examples/MjcfConverter/MjcfConverter.cpp
@@ -3,8 +3,8 @@
 #include <VirtualRobot/RobotNodeSet.h>
 #include <VirtualRobot/XML/RobotIO.h>
 
-#include "utils.h"
-#include "xml_visitors.h"
+#include "mjcf/utils.h"
+#include "mjcf/xml_visitors.h"
 
 
 
diff --git a/VirtualRobot/examples/MjcfConverter/MjcfConverter.h b/VirtualRobot/examples/MjcfConverter/MjcfConverter.h
index 0bd82a8ed..17a5dc73d 100644
--- a/VirtualRobot/examples/MjcfConverter/MjcfConverter.h
+++ b/VirtualRobot/examples/MjcfConverter/MjcfConverter.h
@@ -5,10 +5,9 @@
 #include <VirtualRobot/Robot.h>
 
 
-#include "exceptions.h"
-#include "SimoxXMLDocument.h"
-#include "MjcfDocument.h"
-#include "MjcfMasslessBodySanitizer.h"
+#include "mjcf/exceptions.h"
+#include "mjcf/MjcfDocument.h"
+#include "mjcf/MjcfMasslessBodySanitizer.h"
 
 
 namespace VirtualRobot
diff --git a/VirtualRobot/examples/MjcfConverter/main.cpp b/VirtualRobot/examples/MjcfConverter/main.cpp
index 85140df6c..c50c22877 100644
--- a/VirtualRobot/examples/MjcfConverter/main.cpp
+++ b/VirtualRobot/examples/MjcfConverter/main.cpp
@@ -3,7 +3,6 @@
 #include <VirtualRobot/RuntimeEnvironment.h>
 
 #include "MjcfConverter.h"
-#include "utils.h"
 
 using namespace VirtualRobot;
 
diff --git a/VirtualRobot/examples/MjcfConverter/mjcf/MjcfDocument.cpp b/VirtualRobot/examples/MjcfConverter/mjcf/MjcfDocument.cpp
index 4bab25adf..e803fda40 100644
--- a/VirtualRobot/examples/MjcfConverter/mjcf/MjcfDocument.cpp
+++ b/VirtualRobot/examples/MjcfConverter/mjcf/MjcfDocument.cpp
@@ -256,19 +256,3 @@ Element* Document::topLevelElement(const std::string& name)
 }
 
 
-std::string Document::toAttr(bool b)
-{
-    static const std::string strings[] = { "false", "true" };
-    return strings[int(b)];
-}
-
-std::string Document::toAttr(const Eigen::Quaternionf& quat)
-{
-    std::stringstream ss;
-    ss << quat.w() << " " << quat.x() << " " << quat.y() << " " << quat.z();
-    return ss.str();
-}
-
-
-
-
diff --git a/VirtualRobot/examples/MjcfConverter/mjcf/MjcfDocument.h b/VirtualRobot/examples/MjcfConverter/mjcf/MjcfDocument.h
index 123ff373b..4c3ef944b 100644
--- a/VirtualRobot/examples/MjcfConverter/mjcf/MjcfDocument.h
+++ b/VirtualRobot/examples/MjcfConverter/mjcf/MjcfDocument.h
@@ -85,13 +85,7 @@ namespace mjcf
         /// Gets the top-level element (child of root element) with a name. 
         /// If it does not exist, it is created.
         Element* topLevelElement(const std::string& name);
-        
-        
-        /// Convert to MJCF XML attribute format.
-        std::string toAttr(bool b);
-        template <int dim>
-        std::string toAttr(const Eigen::Matrix<float, dim, 1>& v);
-        std::string toAttr(const Eigen::Quaternionf& v);
+
         
         /// Scaling for lengths, such as positions and translations.
         float lengthScaling = 0.001f;
@@ -100,9 +94,6 @@ namespace mjcf
         /// Mass used for dummy inertial elements.
         float dummyMass = 0.0001f;
         
-        /// IOFormat for vectors.
-        Eigen::IOFormat iofVector {5, 0, "", " ", "", "", "", ""};
-        
         /// The "mujoco" root element.
         Element* root;
         
@@ -113,14 +104,7 @@ namespace mjcf
     
     using DocumentPtr = std::unique_ptr<Document>;
  
-    
-    template <int dim>
-    std::string Document::toAttr(const Eigen::Matrix<float, dim, 1>& v)
-    {
-        std::stringstream ss;
-        ss << v.format(iofVector);
-        return ss.str();
-    }
+
     
     
 
diff --git a/VirtualRobot/examples/MjcfConverter/mjcf/MjcfMasslessBodySanitizer.cpp b/VirtualRobot/examples/MjcfConverter/mjcf/MjcfMasslessBodySanitizer.cpp
index a7d6c14b1..05b926dd6 100644
--- a/VirtualRobot/examples/MjcfConverter/mjcf/MjcfMasslessBodySanitizer.cpp
+++ b/VirtualRobot/examples/MjcfConverter/mjcf/MjcfMasslessBodySanitizer.cpp
@@ -36,94 +36,31 @@ void MjcfMasslessBodySanitizer::sanitizeRecursion(Element* body)
 {
     assertElementIsBody(body);
 
-    std::cout << "- Node '" << body->Attribute("name") << "': " << std::endl;
-    const std::string t = "  | ";
-    
     std::string bodyName = body->Attribute("name");
     RobotNodePtr bodyNode = robot->getRobotNode(bodyName);
-    Eigen::Matrix4f bodyPose = bodyNode->getTransformationFrom(bodyNode->getParent());
+    Eigen::Matrix4f accChildPose = Eigen::Matrix4f::Identity();
     
     while (!hasMass(body))
     {
-        std::cout << t << "Massless" << std::endl;
+        std::cout << t << bodyName << ": \t";
         
         if (!hasElementChild(body, "body"))
         {
             // leaf => end of recursion
-            std::cout << t << "Leaf" << std::endl;
             sanitizeLeafBody(body);
-            return; 
+            return;
         }
         
         // non-leaf body
-        std::cout << t << "Non-leaf" << std::endl;
-        
         // check whether there is only one child body
         Element* childBody = body->FirstChildElement("body");
         if (!childBody->NextSiblingElement("body"))
         {
-            std::string childBodyName = childBody->Attribute("name");
-            
-            std::cout << t << "Single child body => merging '" << childBodyName
-                      << "' into '" << bodyName << "'" << std::endl;
-
-            // check child for pose attributes
-            if (childBody->Attribute("pos") || childBody->Attribute("quat"))
-            {
-                // update body's transform and joint axes
-                
-                RobotNodePtr childNode = robot->getRobotNode(childBodyName);
-                Eigen::Matrix4f childPose = childNode->getTransformationFrom(childNode->getParent());
-                
-                // body's pose
-                bodyPose = bodyPose * childPose;
-                document->setBodyPose(body, bodyPose);
-                
-                /* Adapt axes of joints in body:
-                 * The axes will be relative to the new pose. Therefore, the additional rotation
-                 * of the child must be subtracted from the joint axis.
-                 */
-                
-                Eigen::Matrix3f revChildOri = childPose.block<3,3>(0, 0).transpose();
-                
-                for (Element* joint = body->FirstChildElement("joint"); joint;
-                     joint = joint->NextSiblingElement("joint"))
-                {
-                    Eigen::Vector3f axis = strToVec(joint->Attribute("axis"));
-                    // apply child orientation
-                    axis = revChildOri * axis;
-                    document->setJointAxis(joint, axis);
-                }
-            }
-            
-            
-            // merge childBody into body => move all its elements here
-            for (tx::XMLNode* grandChild = childBody->FirstChild(); grandChild;
-                 grandChild = grandChild->NextSibling())
-            {
-                std::cout << t << " | Moving '" << grandChild->Value() << "'" << std::endl;
-                
-                // clone grandchild
-                tx::XMLNode* grandChildClone = grandChild->DeepClone(nullptr);
-                
-                // insert to body
-                body->InsertEndChild(grandChildClone);
-            }
-            
-            // update body name
-            MergedBodySet& bodySet = getMergedBodySetWith(bodyName);
-            bodySet.addBody(childBodyName);
-            body->SetAttribute("name", bodySet.getMergedBodyName().c_str());
-            
-            // delete child
-            body->DeleteChild(childBody);
-            
-            std::cout << t << "=> New body name: " << bodySet.getMergedBodyName() << std::endl;
+            mergeBodies(body, childBody, accChildPose);
         }
         else
         {
-            std::cout << "[WARN]" << t << "Massless body with >1 child body: " 
-                       << body->Attribute("name") << std::endl;
+            std::cout << "[WARN] Massless body with >1 child bodies." << std::endl;
             break;
         }
     }
@@ -140,6 +77,106 @@ void MjcfMasslessBodySanitizer::sanitizeRecursion(Element* body)
     
 }
 
+void MjcfMasslessBodySanitizer::mergeBodies(Element* body, Element* childBody, 
+                                            Eigen::Matrix4f& accChildPose)
+{
+    std::string childBodyName = childBody->Attribute("name");
+    
+    std::cout << "Merging with '" << childBodyName << "' ";
+
+    RobotNodePtr childNode = robot->getRobotNode(childBodyName);
+    Eigen::Matrix4f childPose = childNode->getTransformationFrom(childNode->getParent());
+    
+    // update accumulated child pose
+    // merged child's frame w.r.t. body's frame
+    accChildPose = childPose * accChildPose;
+    Eigen::Matrix3f accChildOri = accChildPose.block<3,3>(0, 0);
+    
+    // merge childBody into body => move all its elements here
+    // while doing this, apply accChildPose to elements
+    for (tx::XMLNode* grandChild = childBody->FirstChild(); grandChild;
+         grandChild = grandChild->NextSibling())
+    {
+        // clone grandchild
+        tx::XMLNode* grandChildClone = grandChild->DeepClone(nullptr);
+        
+        Element* elem = grandChildClone->ToElement();
+        if (elem)
+        {
+            /* Adapt pose/axis elements in child. Their poses/axes will be
+                 * relative to body's frame, so the transformation from body
+                 * to child will be lost. Therefore, apply accChildPose to
+                 * their poses/axes. */
+            
+            if (isElement(elem, "joint"))
+            {
+                // update pos and axis
+                updateChildPos(elem, accChildPose);
+                updateChildAxis(elem, accChildOri);
+            }
+            else if (isElement(elem, "body") 
+                     || isElement(elem, "inertial")
+                     || isElement(elem, "geom") 
+                     || isElement(elem, "site")
+                     || isElement(elem, "camera"))
+            {
+                updateChildPos(elem, accChildPose);
+                updateChildQuat(elem, accChildOri);
+            }
+            else if (isElement(elem, "light"))
+            {
+                updateChildPos(elem, accChildPose);
+                updateChildAxis(elem, accChildOri, "dir");
+            }
+        }
+        
+        // insert to body
+        body->InsertEndChild(grandChildClone);
+    }
+    
+    // update body name
+    MergedBodySet& bodySet = getMergedBodySetWith(body->Attribute("name"));
+    bodySet.addBody(childBodyName);
+    body->SetAttribute("name", bodySet.getMergedBodyName().c_str());
+    
+    std::cout << "(new name: " << bodySet.getMergedBodyName() << ")" << std::endl;
+    
+    // delete child
+    body->DeleteChild(childBody);
+}
+
+void MjcfMasslessBodySanitizer::updateChildPos(Element* elem, const Eigen::Matrix4f& accChildPose)
+{
+    const char* posStr = elem->Attribute("pos");
+    Eigen::Vector3f pos = posStr ? strToVec(posStr) : 
+                                   Eigen::Vector3f::Zero();
+    
+    Eigen::Vector4f posHom;
+    posHom << pos, 1;
+    posHom = accChildPose * posHom;
+    pos = posHom.head<3>();
+    
+    elem->SetAttribute("pos", toAttr(pos).c_str());
+}
+
+void MjcfMasslessBodySanitizer::updateChildQuat(Element* elem, const Eigen::Matrix3f& accChildOri)
+{
+    const char* quatStr = elem->Attribute("quat");
+    Eigen::Quaternionf quat = quatStr ? strToQuat(quatStr) : 
+                                        Eigen::Quaternionf::Identity();
+    
+    quat = accChildOri * quat;
+    elem->SetAttribute("quat", toAttr(quat).c_str());
+}
+
+void MjcfMasslessBodySanitizer::updateChildAxis(Element* elem, const Eigen::Matrix3f& accChildOri, 
+                                                const char* attrName)
+{
+    Eigen::Vector3f axis = strToVec(elem->Attribute("axis"));
+    axis = accChildOri * axis;
+    elem->SetAttribute(attrName, toAttr(axis).c_str());
+}
+
 void MjcfMasslessBodySanitizer::sanitizeLeafBody(Element* body)
 {
     assert(!hasElementChild(body, "body"));
@@ -148,13 +185,13 @@ void MjcfMasslessBodySanitizer::sanitizeLeafBody(Element* body)
     if (body->NoChildren()) // is completely empty?
     {
         // leaf without geom: make it a site
-        std::cout << "  | Empty => Changing body '" << body->Attribute("name") << "' to site." << std::endl;
+        std::cout << "Changing to site." << std::endl;
         body->SetValue("site");
     }
     else
     {
         // add a small mass
-        std::cout << "  | Not empty => Adding dummy inertial." << std::endl;
+        std::cout << "Adding dummy inertial." << std::endl;
         document->addDummyInertial(body);
     }
 }
@@ -197,13 +234,14 @@ MergedBodySet::MergedBodySet(const std::string& bodyName)
 
 void MergedBodySet::addBody(const std::string& bodyName)
 {
-    originalBodyNames.insert(bodyName);
+    originalBodyNames.push_back(bodyName);
     updateMergedBodyName();
 }
 
 bool MergedBodySet::containsBody(const std::string& bodyName) const
 {
-    return originalBodyNames.find(bodyName) != originalBodyNames.end();
+    return std::find(originalBodyNames.begin(), originalBodyNames.end(),
+                     bodyName) != originalBodyNames.end();
 }
 
 const std::string& MergedBodySet::getMergedBodyName() const
diff --git a/VirtualRobot/examples/MjcfConverter/mjcf/MjcfMasslessBodySanitizer.h b/VirtualRobot/examples/MjcfConverter/mjcf/MjcfMasslessBodySanitizer.h
index 65b1f0988..a2f1170c0 100644
--- a/VirtualRobot/examples/MjcfConverter/mjcf/MjcfMasslessBodySanitizer.h
+++ b/VirtualRobot/examples/MjcfConverter/mjcf/MjcfMasslessBodySanitizer.h
@@ -26,7 +26,7 @@ namespace mjcf
         void updateMergedBodyName();
         
         std::string mergedBodyName;
-        std::set<std::string> originalBodyNames;
+        std::vector<std::string> originalBodyNames;
         
     };
     
@@ -51,7 +51,16 @@ namespace mjcf
         void sanitizeRecursion(mjcf::Element* body);
         void sanitizeLeafBody(mjcf::Element* body);
         
+        void mergeBodies(Element* body, Element* childBody, Eigen::Matrix4f& accChildPose);
         
+        void updateChildPos(Element* elem, const Eigen::Matrix4f& accChildPose);
+        void updateChildQuat(Element* elem, const Eigen::Matrix3f& accChildOri);
+        void updateChildAxis(Element* elem, const Eigen::Matrix3f& accChildOri, 
+                             const char* attrName = "axis");
+        
+        
+        
+        const std::string t = "| ";
         
         
         DocumentPtr& document;
diff --git a/VirtualRobot/examples/MjcfConverter/mjcf/utils.cpp b/VirtualRobot/examples/MjcfConverter/mjcf/utils.cpp
index f740c5b03..d79c42add 100644
--- a/VirtualRobot/examples/MjcfConverter/mjcf/utils.cpp
+++ b/VirtualRobot/examples/MjcfConverter/mjcf/utils.cpp
@@ -30,6 +30,13 @@ Eigen::Vector3f strToVec(const char* string)
     return v;
 }
 
+Eigen::Quaternionf strToQuat(const char* string)
+{
+    Eigen::Quaternionf q;
+    sscanf(string, "%f %f %f %f", &q.w(), &q.x(), &q.y(), &q.z());
+    return q;
+}
+
 std::size_t commonPrefixLength(const std::string& a, const std::string& b)
 {
     const std::string* smaller = &a;
@@ -64,5 +71,18 @@ bool isElement(const mjcf::Element& elem, const std::string& tag)
     return std::strcmp(elem.Value(), tag.c_str()) == 0;
 }
 
+std::string toAttr(bool b)
+{
+    static const std::string strings[] = { "false", "true" };
+    return strings[int(b)];
+}
+
+std::string toAttr(const Eigen::Quaternionf& quat)
+{
+    std::stringstream ss;
+    ss << quat.w() << " " << quat.x() << " " << quat.y() << " " << quat.z();
+    return ss.str();
+}
+
 
 }
diff --git a/VirtualRobot/examples/MjcfConverter/mjcf/utils.h b/VirtualRobot/examples/MjcfConverter/mjcf/utils.h
index ad1701de5..2d59c00cc 100644
--- a/VirtualRobot/examples/MjcfConverter/mjcf/utils.h
+++ b/VirtualRobot/examples/MjcfConverter/mjcf/utils.h
@@ -17,18 +17,44 @@ bool hasElementChild(const mjcf::Element* elem, const std::string& elemName);
 bool hasMass(const mjcf::Element* body);
 
 
+// Convert to MJCF XML attribute format.
+std::string toAttr(bool b);
+template <int dim>
+std::string toAttr(const Eigen::Matrix<float, dim, 1>& v);
+std::string toAttr(const Eigen::Quaternionf& v);
+
 Eigen::Vector3f strToVec(const char* string);
+Eigen::Quaternionf strToQuat(const char* string);
 
 std::size_t commonPrefixLength(const std::string& a, const std::string& b);
 
 
+template <class StringT>
+void assertElementIs(const mjcf::Element* elem, const StringT tag);
+
+void assertElementIsBody(const mjcf::Element* elem);
+
+
+
+
+// DEFINITIONS of templated methods
+
+
+template <int dim>
+std::string toAttr(const Eigen::Matrix<float, dim, 1>& v)
+{
+    static const Eigen::IOFormat iofVector {7, 0, "", " ", "", "", "", ""};
+    
+    std::stringstream ss;
+    ss << v.format(iofVector);
+    return ss.str();
+}
+
 template <class StringT>
 void assertElementIs(const mjcf::Element* elem, const StringT tag)
 {
     assert(isElement(elem, tag));
 }
 
-void assertElementIsBody(const mjcf::Element* elem);
-
 }
 
-- 
GitLab