From f295d84c2f9b237746382925bb4da539110a878a Mon Sep 17 00:00:00 2001
From: Rainer Kartmann <rainer.kartmann@student.kit.edu>
Date: Tue, 13 Nov 2018 15:34:54 +0100
Subject: [PATCH] Removed unecessary files. Added class attributes

---
 VirtualRobot/CMakeLists.txt                   |   9 +-
 VirtualRobot/XML/RobotIO.cpp                  |  15 +--
 .../XML/mjcf/MasslessBodySanitizer.cpp        |  28 ++---
 VirtualRobot/XML/mjcf/MasslessBodySanitizer.h |   4 +-
 VirtualRobot/XML/mjcf/MjcfDocument.cpp        | 112 ++++++++++++++++--
 VirtualRobot/XML/mjcf/MjcfDocument.h          | 103 +++++++++++-----
 VirtualRobot/XML/mjcf/MujocoIO.cpp            |  39 +++---
 VirtualRobot/XML/mjcf/MujocoIO.h              |   6 +-
 VirtualRobot/XML/mjcf/exceptions.cpp          |  19 ---
 VirtualRobot/XML/mjcf/exceptions.h            |  38 ------
 10 files changed, 225 insertions(+), 148 deletions(-)
 delete mode 100644 VirtualRobot/XML/mjcf/exceptions.cpp
 delete mode 100644 VirtualRobot/XML/mjcf/exceptions.h

diff --git a/VirtualRobot/CMakeLists.txt b/VirtualRobot/CMakeLists.txt
index 525131154..d848ce0df 100644
--- a/VirtualRobot/CMakeLists.txt
+++ b/VirtualRobot/CMakeLists.txt
@@ -77,7 +77,6 @@ XML/RobotIO.cpp
 XML/SceneIO.cpp
 XML/ObjectIO.cpp
 XML/FileIO.cpp
-XML/mjcf/exceptions.cpp
 XML/mjcf/MasslessBodySanitizer.cpp
 XML/mjcf/MjcfDocument.cpp
 XML/mjcf/MujocoIO.cpp
@@ -229,7 +228,6 @@ XML/RobotIO.h
 XML/SceneIO.h
 XML/ObjectIO.h
 XML/FileIO.h
-XML/mjcf/exceptions.h
 XML/mjcf/MasslessBodySanitizer.h
 XML/mjcf/MjcfDocument.h
 XML/mjcf/MujocoIO.h
@@ -563,6 +561,11 @@ ADD_LIBRARY (VirtualRobot SHARED ${SOURCES} ${INCLUDES})
 TARGET_LINK_LIBRARIES(VirtualRobot PUBLIC ColCheckerPQP ${Simox_EXTERNAL_LIBRARIES})
 target_include_directories(VirtualRobot SYSTEM PUBLIC ${Simox_EXTERNAL_INCLUDE_DIRS})
 
+# against undefined reference to boost::filesystem::detail::copy_file
+# source: https://stackoverflow.com/a/3500721
+target_compile_definitions(${PROJECT_NAME} PRIVATE -DBOOST_NO_CXX11_SCOPED_ENUMS)
+
+
 # .DLL path
 SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${Simox_BIN_DIR})
 # .so path
@@ -571,6 +574,8 @@ SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${Simo
 SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${Simox_LIB_DIR})
 
 
+
+
 #######################################################################################
 ############################ Setup for installation ###################################
 #######################################################################################
diff --git a/VirtualRobot/XML/RobotIO.cpp b/VirtualRobot/XML/RobotIO.cpp
index 6feadd35d..d78de0344 100644
--- a/VirtualRobot/XML/RobotIO.cpp
+++ b/VirtualRobot/XML/RobotIO.cpp
@@ -1518,19 +1518,8 @@ namespace VirtualRobot
     void RobotIO::saveMJCF(RobotPtr robot, const std::string& filename, 
                            const std::string& basePath, const std::string& meshDir)
     {
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
+        mjcf::MujocoIO mujocoIO;
+        mujocoIO.saveMJCF(robot, filename, basePath, meshDir);
     }
 
 
diff --git a/VirtualRobot/XML/mjcf/MasslessBodySanitizer.cpp b/VirtualRobot/XML/mjcf/MasslessBodySanitizer.cpp
index 05b926dd6..ea6ceadd7 100644
--- a/VirtualRobot/XML/mjcf/MasslessBodySanitizer.cpp
+++ b/VirtualRobot/XML/mjcf/MasslessBodySanitizer.cpp
@@ -1,23 +1,21 @@
-#include "MjcfMasslessBodySanitizer.h"
+#include "MasslessBodySanitizer.h"
+#include "utils.h"
 
 #include <boost/algorithm/string/join.hpp>
 
 
-#include "utils.h"
-
-
 using namespace VirtualRobot;
 using namespace mjcf;
 namespace tx = tinyxml2;
 
 
-MjcfMasslessBodySanitizer::MjcfMasslessBodySanitizer(DocumentPtr& document, RobotPtr& robot) :
+MasslessBodySanitizer::MasslessBodySanitizer(DocumentPtr& document, RobotPtr& robot) :
     document(document), robot(robot)
 {
     
 }
 
-void MjcfMasslessBodySanitizer::sanitize()
+void MasslessBodySanitizer::sanitize()
 {
     // merge body leaf nodes with parent if they do not have a mass (inertial or geom)
     
@@ -32,7 +30,7 @@ void MjcfMasslessBodySanitizer::sanitize()
 }
 
 
-void MjcfMasslessBodySanitizer::sanitizeRecursion(Element* body)
+void MasslessBodySanitizer::sanitizeRecursion(Element* body)
 {
     assertElementIsBody(body);
 
@@ -77,7 +75,7 @@ void MjcfMasslessBodySanitizer::sanitizeRecursion(Element* body)
     
 }
 
-void MjcfMasslessBodySanitizer::mergeBodies(Element* body, Element* childBody, 
+void MasslessBodySanitizer::mergeBodies(Element* body, Element* childBody, 
                                             Eigen::Matrix4f& accChildPose)
 {
     std::string childBodyName = childBody->Attribute("name");
@@ -145,7 +143,7 @@ void MjcfMasslessBodySanitizer::mergeBodies(Element* body, Element* childBody,
     body->DeleteChild(childBody);
 }
 
-void MjcfMasslessBodySanitizer::updateChildPos(Element* elem, const Eigen::Matrix4f& accChildPose)
+void MasslessBodySanitizer::updateChildPos(Element* elem, const Eigen::Matrix4f& accChildPose)
 {
     const char* posStr = elem->Attribute("pos");
     Eigen::Vector3f pos = posStr ? strToVec(posStr) : 
@@ -159,7 +157,7 @@ void MjcfMasslessBodySanitizer::updateChildPos(Element* elem, const Eigen::Matri
     elem->SetAttribute("pos", toAttr(pos).c_str());
 }
 
-void MjcfMasslessBodySanitizer::updateChildQuat(Element* elem, const Eigen::Matrix3f& accChildOri)
+void MasslessBodySanitizer::updateChildQuat(Element* elem, const Eigen::Matrix3f& accChildOri)
 {
     const char* quatStr = elem->Attribute("quat");
     Eigen::Quaternionf quat = quatStr ? strToQuat(quatStr) : 
@@ -169,7 +167,7 @@ void MjcfMasslessBodySanitizer::updateChildQuat(Element* elem, const Eigen::Matr
     elem->SetAttribute("quat", toAttr(quat).c_str());
 }
 
-void MjcfMasslessBodySanitizer::updateChildAxis(Element* elem, const Eigen::Matrix3f& accChildOri, 
+void MasslessBodySanitizer::updateChildAxis(Element* elem, const Eigen::Matrix3f& accChildOri, 
                                                 const char* attrName)
 {
     Eigen::Vector3f axis = strToVec(elem->Attribute("axis"));
@@ -177,7 +175,7 @@ void MjcfMasslessBodySanitizer::updateChildAxis(Element* elem, const Eigen::Matr
     elem->SetAttribute(attrName, toAttr(axis).c_str());
 }
 
-void MjcfMasslessBodySanitizer::sanitizeLeafBody(Element* body)
+void MasslessBodySanitizer::sanitizeLeafBody(Element* body)
 {
     assert(!hasElementChild(body, "body"));
     assert(!hasMass(body));
@@ -196,17 +194,17 @@ void MjcfMasslessBodySanitizer::sanitizeLeafBody(Element* body)
     }
 }
 
-const std::vector<MergedBodySet>& MjcfMasslessBodySanitizer::getMergedBodySets() const
+const std::vector<MergedBodySet>& MasslessBodySanitizer::getMergedBodySets() const
 {
     return mergedBodySets;
 }
 
-const std::string& MjcfMasslessBodySanitizer::getMergedBodyName(const std::string& originalBodyName)
+const std::string& MasslessBodySanitizer::getMergedBodyName(const std::string& originalBodyName)
 {
     return getMergedBodySetWith(originalBodyName).getMergedBodyName();
 }
 
-MergedBodySet&MjcfMasslessBodySanitizer::getMergedBodySetWith(const std::string& bodyName)
+MergedBodySet&MasslessBodySanitizer::getMergedBodySetWith(const std::string& bodyName)
 {
     for (auto& set : mergedBodySets)
     {
diff --git a/VirtualRobot/XML/mjcf/MasslessBodySanitizer.h b/VirtualRobot/XML/mjcf/MasslessBodySanitizer.h
index a2f1170c0..397318a08 100644
--- a/VirtualRobot/XML/mjcf/MasslessBodySanitizer.h
+++ b/VirtualRobot/XML/mjcf/MasslessBodySanitizer.h
@@ -31,11 +31,11 @@ namespace mjcf
     };
     
 
-    class MjcfMasslessBodySanitizer
+    class MasslessBodySanitizer
     {
     public:
         
-        MjcfMasslessBodySanitizer(DocumentPtr& document, RobotPtr& robot);
+        MasslessBodySanitizer(DocumentPtr& document, RobotPtr& robot);
         
         void sanitize();
         
diff --git a/VirtualRobot/XML/mjcf/MjcfDocument.cpp b/VirtualRobot/XML/mjcf/MjcfDocument.cpp
index e803fda40..261b8e9c1 100644
--- a/VirtualRobot/XML/mjcf/MjcfDocument.cpp
+++ b/VirtualRobot/XML/mjcf/MjcfDocument.cpp
@@ -11,9 +11,12 @@ using namespace VirtualRobot;
 using namespace mjcf;
 
 
-Document::Document() : root(NewElement("mujoco"))
+
+
+Document::Document()
 {
     // create root element
+    root = NewElement("mujoco");
     this->InsertEndChild(root);
 }
 
@@ -22,6 +25,12 @@ void Document::setModelName(const std::string& name)
     root->SetAttribute("model", name.c_str());
 }
 
+void Document::setNewElementClass(const std::string& className, bool excludeBody)
+{
+    this->newElementClass = className;
+    this->newElementClassExcludeBody = excludeBody;
+}
+
 Element* Document::addSkyboxTexture(const Eigen::Vector3f& rgb1, const Eigen::Vector3f& rgb2)
 {
     Element* texSkybox = addNewElement(asset(), "texture");
@@ -36,9 +45,22 @@ Element* Document::addSkyboxTexture(const Eigen::Vector3f& rgb1, const Eigen::Ve
     return texSkybox;
 }
 
-Element* Document::addNewElement(Element* parent, const std::string& elemName, bool first)
+Element* Document::addNewElement(Element* parent, const std::string& elemName, 
+                                 const std::string& className, bool first)
 {
     Element* elem = NewElement(elemName.c_str());
+    
+    if (!className.empty())
+    {
+        elem->SetAttribute("class", className.c_str());
+    }
+    else if (!newElementClass.empty()
+             && !(newElementClassExcludeBody && (isElement(parent, "body") || isElement(elem, "body")))
+             && allowsClassAttr({parent->Value(), elemName}))
+    {
+        elem->SetAttribute("class", newElementClass.c_str());
+    }
+    
     if (first)
     {
         parent->InsertFirstChild(elem);
@@ -50,6 +72,23 @@ Element* Document::addNewElement(Element* parent, const std::string& elemName, b
     return elem;
 }
 
+Element* Document::addDefaultsClass(const std::string& className)
+{
+    Element* def = addNewElement(default_(), "default", className);
+    
+    return def;
+}
+
+Element* Document::addRobotRootBodyElement(const std::string& robotName)
+{
+    this->robotRootBody_ = addNewElement(worldbody(), "body");
+    
+    robotRootBody_->SetAttribute("name", robotName.c_str());
+    robotRootBody_->SetAttribute("childclass", robotName.c_str());
+    
+    return robotRootBody_;
+}
+
 Element* Document::addBodyElement(Element* parent, RobotNodePtr node)
 {
     Element* body = addNewElement(parent, "body");
@@ -76,7 +115,7 @@ Element* Document::addGeomElement(Element* body, const std::string& meshName)
 {
     assertElementIsBody(body);
     
-    Element* geom = addNewElement(body, "geom", true);
+    Element* geom = addNewElement(body, "geom", "", true);
     
     geom->SetAttribute("type", "mesh");
 //    geom->SetAttribute("type", "capsule");
@@ -177,9 +216,9 @@ Element* Document::addJointElement(Element* body, RobotNodePtr node)
     return joint;
 }
 
-Element* Document::addMeshElement(const std::string& name, const std::string& file)
+Element* Document::addMeshElement(const std::string& name, const std::string& file, const std::string& className)
 {
-    Element* mesh = addNewElement(asset(), "mesh");
+    Element* mesh = addNewElement(asset(), "mesh", className);
     
     mesh->SetAttribute("name", name.c_str());
     mesh->SetAttribute("file", file.c_str());
@@ -187,9 +226,9 @@ Element* Document::addMeshElement(const std::string& name, const std::string& fi
     return mesh;
 }
 
-Element*Document::addMotorElement(const std::string& jointName)
+Element*Document::addMotorElement(const std::string& jointName, const std::string& className)
 {
-    Element* motor = addNewElement(actuator(), "motor");
+    Element* motor = addNewElement(actuator(), "motor", className);
     
     motor->SetAttribute("name",  jointName.c_str());
     motor->SetAttribute("joint", jointName.c_str());
@@ -229,6 +268,8 @@ void Document::setJointAxis(Element* joint, const Eigen::Vector3f& axis)
     joint->SetAttribute("axis", toAttr(axis).c_str());
 }
 
+
+
 Element*Document::addContactExclude(const Element& body1, const Element& body2)
 {
     return addContactExclude(body1.Attribute("name"), body2.Attribute("name"));
@@ -245,7 +286,7 @@ Element* Document::addContactExclude(const std::string& body1Name, const std::st
 }
 
 
-Element* Document::topLevelElement(const std::string& name)
+Element* Document::section(const std::string& name)
 {
     Element* elem = root->FirstChildElement(name.c_str());
     if (!elem)
@@ -255,4 +296,59 @@ Element* Document::topLevelElement(const std::string& name)
     return elem;
 }
 
+void Document::setDummyMass(float value)
+{
+    dummyMass = value;
+}
+
+void Document::setFloatCompPrecision(float value)
+{
+    floatCompPrecision = value;
+}
+
+void Document::setLengthScaling(float value)
+{
+    lengthScaling = value;
+}
+
+Element* Document::robotRootBody() const
+{
+    return robotRootBody_;
+}
+
+
+const std::set<Document::ElementType> Document::ELEM_NAMES_WITH_ATTR_CLASS = 
+{
+    {"asset", "mesh"    },
+    {"asset", "material"},
+    
+    {"body", "joint"    },
+    {"body", "geom"     },
+    {"body", "site"     },
+    {"body", "camera"   },
+    {"body", "light"    },
+    
+    {"contact", "pair"  },
+    
+    {"equality", "connect"  },
+    {"equality", "weld"     },
+    {"equality", "joint"    },
+    {"equality", "tendon"   },
+    {"equality", "distance" },
+    
+    {"tendon", "spatial" },
+    {"tendon", "fixed"   },
+    
+    {"actuator", "general"  },
+    {"actuator", "motor"    },
+    {"actuator", "position" },
+    {"actuator", "velocity" },
+    {"actuator", "cylinder" },
+    {"actuator", "muscle"   },
+};
+
+bool Document::allowsClassAttr(const Document::ElementType& type)
+{
+    return ELEM_NAMES_WITH_ATTR_CLASS.find(type) != ELEM_NAMES_WITH_ATTR_CLASS.end();
+}
 
diff --git a/VirtualRobot/XML/mjcf/MjcfDocument.h b/VirtualRobot/XML/mjcf/MjcfDocument.h
index 4c3ef944b..09a9eab63 100644
--- a/VirtualRobot/XML/mjcf/MjcfDocument.h
+++ b/VirtualRobot/XML/mjcf/MjcfDocument.h
@@ -23,32 +23,52 @@ namespace mjcf
         
         Document();
         
-        // Top level elements
-        Element* compiler()  { return topLevelElement("compiler");  }
-        Element* option()    { return topLevelElement("option");    }
-        Element* size()      { return topLevelElement("size");      }
-        Element* visual()    { return topLevelElement("visual");    }
-        Element* statistic() { return topLevelElement("statistic"); }
-        Element* default_()  { return topLevelElement("default");   }
-        Element* asset()     { return topLevelElement("asset");     }
-        Element* worldbody() { return topLevelElement("worldbody"); }
-        Element* contact()   { return topLevelElement("contact");   }
-        Element* equality()  { return topLevelElement("equality");  }
-        Element* tendon()    { return topLevelElement("tendon");    }
-        Element* actuator()  { return topLevelElement("actuator");  }
-        Element* sensor()    { return topLevelElement("sensor");    }
-        Element* keyframe()  { return topLevelElement("keyframe");  }
+        
+        void setLengthScaling(float value);
+        void setFloatCompPrecision(float value);
+        void setDummyMass(float value);
         
         
         /// Set the name of the Mujoco model.
         void setModelName(const std::string& name);
         
-        /// Add a skybox texture asset (only one allowed).
-        Element* addSkyboxTexture(const Eigen::Vector3f& rgb1, const Eigen::Vector3f& rgb2);
+        /**
+         * @brief Set the class attribute of all new applicable elements 
+         * (after calling this method). Pass an empty string to disable class attributes.
+         * @param excludeBody If true (default), the class will not be set on
+         *  new body elements and its children (inertial, joint, ...). 
+         *  They should be the childclass attribute of the robot root body.
+         */
+        void setNewElementClass(const std::string& className, bool excludeBody = true);
         
-        /// Add a new element with a name (tag) to a parent.
-        /// @param first if true, will be inserted as first, otherweise at end (default)
-        Element* addNewElement(Element* parent, const std::string& elemName, bool first = false);
+        
+        // Top level elements
+        Element* compiler()  { return section("compiler");  }
+        Element* option()    { return section("option");    }
+        Element* size()      { return section("size");      }
+        Element* visual()    { return section("visual");    }
+        Element* statistic() { return section("statistic"); }
+        Element* default_()  { return section("default");   }
+        Element* asset()     { return section("asset");     }
+        Element* worldbody() { return section("worldbody"); }
+        Element* contact()   { return section("contact");   }
+        Element* equality()  { return section("equality");  }
+        Element* tendon()    { return section("tendon");    }
+        Element* actuator()  { return section("actuator");  }
+        Element* sensor()    { return section("sensor");    }
+        Element* keyframe()  { return section("keyframe");  }
+
+        
+        /// Adds a body element to the worldbody with the robot's name as name and childclass.
+        Element* addRobotRootBodyElement(const std::string& robotName);
+        /// Get the (least recently added) robot root body.
+        Element* robotRootBody() const;
+        
+        /// Add a new defaults class with a class name.
+        Element* addDefaultsClass(const std::string& className);
+
+        /// Add a skybox texture asset (only one allowed).
+        Element* addSkyboxTexture(const Eigen::Vector3f& rgb1, const Eigen::Vector3f& rgb2);        
         
         
         /// Add a body element to a parent from a RobotNode.
@@ -65,27 +85,46 @@ namespace mjcf
         /// Add a geom to a body, referencing a mesh.
         Element* addGeomElement(Element* body, const std::string& meshName);
         /// Add a mesh asset with a name and a file.
-        Element* addMeshElement(const std::string& name, const std::string& file);
+        Element* addMeshElement(const std::string& name, const std::string& file,
+                                const std::string& className = "");
+
+        /// Add a conact/exclude element between the given bodies.
+        Element* addContactExclude(const Element& body1, const Element& body2);
+        /// Add a conact/exclude element between the given bodies.
+        Element* addContactExclude(const std::string& body1Name, const std::string& body2Name);
         
-        Element* addMotorElement(const std::string& jointName);
+        Element* addMotorElement(const std::string& jointName, const std::string& className = "");
+        
+
         
         /// Set the pos and quat attribute of a body.
         void setBodyPose(Element* body, const Eigen::Matrix4f& pose);
         /// Set the axis attribute of a joint.
         void setJointAxis(Element* joint, const Eigen::Vector3f& axis);
+
         
-        /// Add a conact/exclude element between the given bodies.
-        Element* addContactExclude(const Element& body1, const Element& body2);
-        /// Add a conact/exclude element between the given bodies.
-        Element* addContactExclude(const std::string& body1Name, const std::string& body2Name);
+        
+    private:
+        
+        using ElementType = std::pair<std::string, std::string>;
+        
+        static const std::set<ElementType> ELEM_NAMES_WITH_ATTR_CLASS;
+        
+        static bool allowsClassAttr(const ElementType& type);
         
         
     private:
         
+        /// Add a new element with a name (tag) to a parent.
+        /// @param className If not empty, set the class attribute of the new element.
+        /// @param first If true, will be inserted as first, otherweise at end (default)
+        Element* addNewElement(Element* parent, const std::string& elemName, 
+                               const std::string& className = "", bool first = false);
+        
         /// 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);
-
+        Element* section(const std::string& name);
+        
         
         /// Scaling for lengths, such as positions and translations.
         float lengthScaling = 0.001f;
@@ -97,11 +136,17 @@ namespace mjcf
         /// The "mujoco" root element.
         Element* root;
         
+        /// The wrapper element of the robot.
+        Element* robotRootBody_;
+        
+        std::string newElementClass = "";
+        bool newElementClassExcludeBody = true;
+        
         
         friend class ContactExcludeVisitor;
         
     };
-    
+
     using DocumentPtr = std::unique_ptr<Document>;
  
 
diff --git a/VirtualRobot/XML/mjcf/MujocoIO.cpp b/VirtualRobot/XML/mjcf/MujocoIO.cpp
index 4627d3c68..a49bb8adf 100644
--- a/VirtualRobot/XML/mjcf/MujocoIO.cpp
+++ b/VirtualRobot/XML/mjcf/MujocoIO.cpp
@@ -4,6 +4,8 @@
 
 #include <VirtualRobot/RobotNodeSet.h>
 #include <VirtualRobot/XML/RobotIO.h>
+#include <VirtualRobot/VirtualRobotException.h>
+#include <VirtualRobot/Visualization/TriMeshModel.h>
 
 
 using namespace VirtualRobot;
@@ -20,17 +22,28 @@ MujocoIO::MujocoIO() :
 
 void MujocoIO::saveMJCF(RobotPtr robot, const std::string& filename, const std::string& basePath, const std::string& meshDir)
 {
+    THROW_VR_EXCEPTION_IF(!robot, "Given RobotPtr robot is null.");
+    THROW_VR_EXCEPTION_IF(filename.empty(), "Given filename is empty.");
+    
     this->robot = robot;
     this->outputDirectory = basePath;
     this->outputFileName = filename;
     this->outputMeshRelDirectory = meshDir;
     
     document.reset(new mjcf::Document());
-    
     document->setModelName(robot->getName());
+    
     document->compiler()->SetAttribute("angle", "radian");
     document->compiler()->SetAttribute("balanceinertia", "true");
     
+    Element* defaultsClass = document->addDefaultsClass(robot->getName());
+    std::stringstream ss;
+    ss << "Add default values for " << robot->getName() << " here.";
+    defaultsClass->InsertFirstChild(document->NewComment(ss.str().c_str()));
+    
+    document->setNewElementClass(robot->getName(), true);
+    
+    
     makeEnvironment();
     
     std::cout << "Creating bodies structure ..." << std::endl;
@@ -63,7 +76,10 @@ void MujocoIO::saveMJCF(RobotPtr robot, const std::string& filename, const std::
     document->Print();
     std::cout << "===========================" << std::endl;
     
-    writeOutputFile();
+    
+    assert(!outputFileName.empty());
+    std::cout << "Writing to " << (outputDirectory / outputFileName) << std::endl;
+    document->SaveFile((outputDirectory / outputFileName).c_str());
 }
 
 
@@ -73,13 +89,6 @@ void MujocoIO::makeEnvironment()
                                Eigen::Vector3f(.4f, .6f, .8f));
 }
 
-void MujocoIO::writeOutputFile()
-{
-    assert(!outputFileName.empty());
-    std::cout << "Writing to " << outputFileName << std::endl;
-    document->SaveFile(outputFileName.c_str());
-}
-
 
 void MujocoIO::addNodeBodies()
 {
@@ -89,7 +98,9 @@ void MujocoIO::addNodeBodies()
     assert(rootNode);
     
     // add root
-    Element* root = document->addBodyElement(document->worldbody(), rootNode);
+    Element* robotRootBody = document->addRobotRootBodyElement(robot->getName());
+    
+    Element* root = document->addBodyElement(robotRootBody, rootNode);
     nodeBodies[rootNode->getName()] = root;
     assert(root);
     
@@ -113,16 +124,10 @@ void MujocoIO::addNodeBodyMeshes()
             continue;
         }
         
-        
         std::cout << "Node " << node->getName() << ":\t";
         
         fs::path srcMeshPath = visualization->getFilename();
-        
-        if (srcMeshPath.is_relative())
-        {
-            // resolve relative path
-            srcMeshPath = inputFileDirectory / srcMeshPath;
-        }
+        assert(srcMeshPath.is_absolute());
         
         fs::path dstMeshFileName = srcMeshPath.filename();
         dstMeshFileName.replace_extension("stl");
diff --git a/VirtualRobot/XML/mjcf/MujocoIO.h b/VirtualRobot/XML/mjcf/MujocoIO.h
index b8e5614ad..ced89cb34 100644
--- a/VirtualRobot/XML/mjcf/MujocoIO.h
+++ b/VirtualRobot/XML/mjcf/MujocoIO.h
@@ -5,7 +5,6 @@
 #include <VirtualRobot/Robot.h>
 
 
-#include "exceptions.h"
 #include "MjcfDocument.h"
 #include "MasslessBodySanitizer.h"
 
@@ -30,8 +29,6 @@ namespace mjcf
     private:
         
         
-        void writeOutputFile();
-        
         void setPaths(const std::string& inputFilename, 
                       const std::string& outputDirectory);
         
@@ -56,7 +53,6 @@ namespace mjcf
         
         boost::filesystem::path outputDirectory;
         boost::filesystem::path outputFileName;
-        
         boost::filesystem::path outputMeshRelDirectory;
 
         
@@ -72,7 +68,7 @@ namespace mjcf
         
         // Processing
         
-        mjcf::MjcfMasslessBodySanitizer masslessBodySanitizer;
+        mjcf::MasslessBodySanitizer masslessBodySanitizer;
         
         std::map<std::string, mjcf::Element*> nodeBodies;
         
diff --git a/VirtualRobot/XML/mjcf/exceptions.cpp b/VirtualRobot/XML/mjcf/exceptions.cpp
deleted file mode 100644
index 34191e6d2..000000000
--- a/VirtualRobot/XML/mjcf/exceptions.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#include "exceptions.h"
-
-#include <sstream>
-
-
-using namespace VirtualRobot;
-
-
-MjcfXmlLoadFileFailed::MjcfXmlLoadFileFailed(const char* errorName, const char* errorStr) : 
-    MjcfConverterError(makeMsg(errorName, errorStr))
-{
-}
-
-std::string MjcfXmlLoadFileFailed::makeMsg(const char* errorName, const char* errorStr)
-{
-    std::stringstream ss;
-    ss << errorName << ": " << errorStr;
-    return ss.str();
-}
diff --git a/VirtualRobot/XML/mjcf/exceptions.h b/VirtualRobot/XML/mjcf/exceptions.h
deleted file mode 100644
index a2e8c80d9..000000000
--- a/VirtualRobot/XML/mjcf/exceptions.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include <stdexcept>
-
-
-namespace VirtualRobot
-{
-
-class MjcfConverterError : public std::runtime_error
-{
-public:
-    // inherit constructor
-    using std::runtime_error::runtime_error;
-};
-
-
-/**
- * @brief Indicates that tinyxml2::XMLDocument::LoadFile() failed.
- */
-class MjcfXmlLoadFileFailed : public MjcfConverterError
-{
-public:
-    MjcfXmlLoadFileFailed(const char* errorName, const char* errorStr);
-    
-private:
-    static std::string makeMsg(const char* errorName, const char* errorStr);
-    
-};
-
-
-
-
-
-
-
-
-
-}
-- 
GitLab