Improve include meta-element to load included resourced from the directory relative to the included XML file, fully backwards compatible.
PiperOrigin-RevId: 608312069 Change-Id: I80eadb32d7d78fb35bd24975f2e514021c2aa009
This commit is contained in:
committed by
Copybara-Service
parent
b77dfc683a
commit
24eb4c9f09
+75
-18
@@ -23,6 +23,7 @@
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "tinyxml2.h"
|
||||
@@ -34,6 +35,7 @@
|
||||
#include "engine/engine_resource.h"
|
||||
#include "engine/engine_vfs.h"
|
||||
#include "user/user_model.h"
|
||||
#include "user/user_util.h"
|
||||
#include "xml/xml_native_reader.h"
|
||||
#include "xml/xml_native_writer.h"
|
||||
#include "xml/xml_urdf.h"
|
||||
@@ -41,7 +43,6 @@
|
||||
|
||||
namespace {
|
||||
|
||||
using std::string;
|
||||
using tinyxml2::XMLDocument;
|
||||
using tinyxml2::XMLElement;
|
||||
using tinyxml2::XMLNode;
|
||||
@@ -97,7 +98,7 @@ class LocaleOverride {
|
||||
} // namespace
|
||||
|
||||
// Main writer function - calls mjXWrite
|
||||
string mjWriteXML(mjCModel* model, char* error, int error_sz) {
|
||||
std::string mjWriteXML(mjCModel* model, char* error, int error_sz) {
|
||||
LocaleOverride locale_override;
|
||||
|
||||
// check for empty model
|
||||
@@ -114,13 +115,32 @@ string mjWriteXML(mjCModel* model, char* error, int error_sz) {
|
||||
|
||||
|
||||
// find include elements recursively, replace them with subtree from xml file
|
||||
static void mjIncludeXML(XMLElement* elem, string dir, const mjVFS* vfs,
|
||||
std::unordered_set<string>& included) {
|
||||
static void mjIncludeXML(mjXReader& reader, XMLElement* elem,
|
||||
std::string_view dir, const mjVFS* vfs,
|
||||
std::unordered_set<std::string>& included) {
|
||||
// capture directory defaults on first pass of XML tree
|
||||
if (!strcasecmp(elem->Value(), "compiler")) {
|
||||
auto assetdir_attr = mjXUtil::ReadAttrStr(elem, "assetdir");
|
||||
if (assetdir_attr.has_value()) {
|
||||
reader.SetAssetDir(assetdir_attr.value());
|
||||
}
|
||||
|
||||
auto texturedir_attr = mjXUtil::ReadAttrStr(elem, "texturedir");
|
||||
if (texturedir_attr.has_value()) {
|
||||
reader.SetTextureDir(texturedir_attr.value());
|
||||
}
|
||||
|
||||
auto meshdir_attr = mjXUtil::ReadAttrStr(elem, "meshdir");
|
||||
if (meshdir_attr.has_value()) {
|
||||
reader.SetMeshDir(meshdir_attr.value());
|
||||
}
|
||||
}
|
||||
|
||||
// not an include, recursively go through all children
|
||||
if (strcasecmp(elem->Value(), "include")) {
|
||||
XMLElement* child = elem->FirstChildElement();
|
||||
for (; child; child = child->NextSiblingElement()) {
|
||||
mjIncludeXML(child, dir, vfs, included);
|
||||
mjIncludeXML(reader, child, dir, vfs, included);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -131,26 +151,60 @@ static void mjIncludeXML(XMLElement* elem, string dir, const mjVFS* vfs,
|
||||
}
|
||||
|
||||
// get filename
|
||||
string filename;
|
||||
mjXUtil::ReadAttrTxt(elem, "file", filename, true);
|
||||
filename = dir + filename;
|
||||
auto file_attr = mjXUtil::ReadAttrStr(elem, "file", true);
|
||||
if (!file_attr.has_value()) {
|
||||
throw mjXError(elem, "Include element missing file attribute");
|
||||
}
|
||||
std::string filename = file_attr.value();
|
||||
|
||||
|
||||
// block repeated include files
|
||||
if (included.find(filename) != included.end()) {
|
||||
throw mjXError(elem, "File '%s' already included", filename.c_str());
|
||||
}
|
||||
|
||||
// get data source
|
||||
mjResource *resource = mju_openVfsResource(filename.c_str(), vfs);
|
||||
// TODO: b/325905702 - We have a messy wrapper here to remain backwards
|
||||
// compatible, which will be removed in the near future.
|
||||
std::string fullname;
|
||||
if (!mjuu_isabspath(filename)) {
|
||||
fullname = reader.ModelFileDir() + filename;
|
||||
} else {
|
||||
fullname = filename;
|
||||
}
|
||||
mjResource *resource = mju_openVfsResource(fullname.c_str(), vfs);
|
||||
if (!resource) {
|
||||
// load from provider or OS filesystem
|
||||
std::array<char, 1024> error;
|
||||
resource = mju_openResource(filename.c_str(), error.data(), error.size());
|
||||
resource = mju_openResource(fullname.c_str(), error.data(), error.size());
|
||||
if (!resource) {
|
||||
throw mjXError(elem, "%s", error.data());
|
||||
if (!mjuu_isabspath(filename)) {
|
||||
fullname = std::string(dir) + filename;
|
||||
} else {
|
||||
fullname = filename;
|
||||
}
|
||||
|
||||
// load from provider or OS filesystem
|
||||
std::array<char, 1024> error;
|
||||
resource = mju_openResource(fullname.c_str(), error.data(), error.size());
|
||||
if (!resource) {
|
||||
throw mjXError(elem, "%s", error.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mjuu_isabspath(filename)) {
|
||||
filename = std::string(dir) + filename;
|
||||
}
|
||||
|
||||
const char* include_dir = nullptr;
|
||||
int ninclude_dir = 0;
|
||||
mju_getResourceDir(resource, &include_dir, &ninclude_dir);
|
||||
std::string next_dir = std::string(include_dir, ninclude_dir);
|
||||
if (!mjuu_isabspath(filename)) {
|
||||
next_dir = std::string(dir) + next_dir;
|
||||
}
|
||||
elem->SetAttribute("dir", next_dir.data());
|
||||
|
||||
const char* xmlstring = nullptr;
|
||||
int buffer_size = mju_readResource(resource, (const void**) &xmlstring);
|
||||
if (buffer_size < 0) {
|
||||
@@ -210,14 +264,15 @@ static void mjIncludeXML(XMLElement* elem, string dir, const mjVFS* vfs,
|
||||
// recursively run include
|
||||
child = include->FirstChildElement();
|
||||
for (; child; child = child->NextSiblingElement()) {
|
||||
mjIncludeXML(child, dir, vfs, included);
|
||||
mjIncludeXML(reader, child, next_dir, vfs, included);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Main parser function
|
||||
mjCModel* mjParseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz) {
|
||||
mjCModel* mjParseXML(const char* filename, const mjVFS* vfs,
|
||||
char* error, int error_sz) {
|
||||
LocaleOverride locale_override;
|
||||
|
||||
// check arguments
|
||||
@@ -251,7 +306,8 @@ mjCModel* mjParseXML(const char* filename, const mjVFS* vfs, char* error, int er
|
||||
int buffer_size = mju_readResource(resource, (const void**) &xmlstring);
|
||||
if (buffer_size < 0) {
|
||||
if (error) {
|
||||
std::snprintf(error, error_sz, "mjParseXML: error reading file '%s'", filename);
|
||||
std::snprintf(error, error_sz,
|
||||
"mjParseXML: error reading file '%s'", filename);
|
||||
}
|
||||
mju_closeResource(resource);
|
||||
return nullptr;
|
||||
@@ -304,11 +360,12 @@ mjCModel* mjParseXML(const char* filename, const mjVFS* vfs, char* error, int er
|
||||
try {
|
||||
if (!strcasecmp(root->Value(), "mujoco")) {
|
||||
// find include elements, replace them with subtree from xml file
|
||||
std::unordered_set<string> included = {filename};
|
||||
mjIncludeXML(root, model->modelfiledir, vfs, included);
|
||||
std::unordered_set<std::string> included = {filename};
|
||||
mjXReader parser;
|
||||
parser.SetModelFileDir(model->modelfiledir);
|
||||
mjIncludeXML(parser, root, model->modelfiledir, vfs, included);
|
||||
|
||||
// parse MuJoCo model
|
||||
mjXReader parser;
|
||||
parser.SetModel(model);
|
||||
parser.Parse(root);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user