Support directories in VFS via mj_addBufferVFS.
PiperOrigin-RevId: 655286683 Change-Id: I846376a0571d8df28d979beb5981a0cbccd7a04f
This commit is contained in:
committed by
Copybara-Service
parent
2746cb8559
commit
5ac5cfb618
+9
-10
@@ -156,8 +156,8 @@ void IncludeXML(mjXReader& reader, XMLElement* elem,
|
||||
}
|
||||
|
||||
// get filename
|
||||
auto file_attr = mjXUtil::ReadAttrFile(elem, "file", reader.ModelFileDir(),
|
||||
true);
|
||||
auto file_attr = mjXUtil::ReadAttrFile(elem, "file", vfs,
|
||||
reader.ModelFileDir(), true);
|
||||
if (!file_attr.has_value()) {
|
||||
throw mjXError(elem, "Include element missing file attribute");
|
||||
}
|
||||
@@ -171,17 +171,17 @@ void IncludeXML(mjXReader& reader, XMLElement* elem,
|
||||
|
||||
// TODO: b/325905702 - We have a messy wrapper here to remain backwards
|
||||
// compatible, which will be removed in the near future.
|
||||
FilePath fullname = reader.ModelFileDir() + filename;
|
||||
|
||||
// legacy behavior: try to load in top level directory
|
||||
std::array<char, 1024> error;
|
||||
mjResource *resource = mju_openResource(fullname.c_str(), vfs,
|
||||
mjResource *resource = mju_openResource(reader.ModelFileDir().c_str(),
|
||||
filename.c_str(), vfs,
|
||||
error.data(), error.size());
|
||||
if (resource == nullptr) {
|
||||
// new behavior: try to load in relative directory
|
||||
if (!filename.IsAbs()) {
|
||||
fullname = dir + filename;
|
||||
resource = mju_openResource(fullname.c_str(), vfs, error.data(), error.size());
|
||||
FilePath fullname = dir + filename;
|
||||
resource = mju_openResource(reader.ModelFileDir().c_str(),
|
||||
fullname.c_str(), vfs, error.data(), error.size());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,6 @@ void IncludeXML(mjXReader& reader, XMLElement* elem,
|
||||
int ninclude_dir = 0;
|
||||
mju_getResourceDir(resource, &include_dir, &ninclude_dir);
|
||||
FilePath next_dir = FilePath(std::string(include_dir, ninclude_dir));
|
||||
next_dir = dir + next_dir;
|
||||
elem->SetAttribute("dir", next_dir.c_str());
|
||||
|
||||
const char* xmlstring = nullptr;
|
||||
@@ -285,7 +284,7 @@ mjSpec* ParseXML(const char* filename, const mjVFS* vfs,
|
||||
// get data source
|
||||
const char* xmlstring = nullptr;
|
||||
std::array<char, 1024> rerror;
|
||||
mjResource* resource = mju_openResource(filename, vfs,
|
||||
mjResource* resource = mju_openResource("", filename, vfs,
|
||||
rerror.data(), rerror.size());
|
||||
if (resource == nullptr) {
|
||||
std::snprintf(error, nerror, "ParseXML: %s", rerror.data());
|
||||
@@ -352,7 +351,7 @@ mjSpec* ParseXML(const char* filename, const mjVFS* vfs,
|
||||
std::unordered_set<std::string> included = {filename};
|
||||
mjXReader parser;
|
||||
parser.SetModelFileDir(mjs_getString(spec->modelfiledir));
|
||||
IncludeXML(parser, root, parser.ModelFileDir(), vfs, included);
|
||||
IncludeXML(parser, root, FilePath(), vfs, included);
|
||||
|
||||
// parse MuJoCo model
|
||||
parser.SetModel(spec);
|
||||
|
||||
+1
-1
@@ -192,7 +192,7 @@ int mj_printSchema(const char* filename, char* buffer, int buffer_sz, int flg_ht
|
||||
// load model from binary MJB resource
|
||||
mjModel* mj_loadModel(const char* filename, const mjVFS* vfs) {
|
||||
std::array<char, 1024> error;
|
||||
mjResource* resource = mju_openResource(filename, vfs,
|
||||
mjResource* resource = mju_openResource("", filename, vfs,
|
||||
error.data(), error.size());
|
||||
if (resource == nullptr) {
|
||||
mju_warning("%s", error.data());
|
||||
|
||||
@@ -893,7 +893,7 @@ void mjXReader::Parse(XMLElement* root, const mjVFS* vfs) {
|
||||
readingdefaults = true;
|
||||
for (XMLElement* section = FirstChildElement(root, "default"); section;
|
||||
section = NextSiblingElement(section, "default")) {
|
||||
Default(section, nullptr);
|
||||
Default(section, nullptr, vfs);
|
||||
}
|
||||
readingdefaults = false;
|
||||
|
||||
@@ -919,7 +919,7 @@ void mjXReader::Parse(XMLElement* root, const mjVFS* vfs) {
|
||||
|
||||
for (XMLElement* section = FirstChildElement(root, "deformable"); section;
|
||||
section = NextSiblingElement(section, "deformable")) {
|
||||
Deformable(section);
|
||||
Deformable(section, vfs);
|
||||
}
|
||||
|
||||
for (XMLElement* section = FirstChildElement(root, "equality"); section;
|
||||
@@ -949,7 +949,7 @@ void mjXReader::Parse(XMLElement* root, const mjVFS* vfs) {
|
||||
|
||||
for (XMLElement* section = FirstChildElement(root, "worldbody"); section;
|
||||
section = NextSiblingElement(section, "worldbody")) {
|
||||
Body(section, mjs_findBody(spec, "world"), nullptr);
|
||||
Body(section, mjs_findBody(spec, "world"), nullptr, vfs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1389,7 +1389,7 @@ void mjXReader::OneFlex(XMLElement* elem, mjsFlex* pflex) {
|
||||
|
||||
|
||||
// mesh element parser
|
||||
void mjXReader::OneMesh(XMLElement* elem, mjsMesh* pmesh) {
|
||||
void mjXReader::OneMesh(XMLElement* elem, mjsMesh* pmesh, const mjVFS* vfs) {
|
||||
int n;
|
||||
string text, name, content_type;
|
||||
|
||||
@@ -1400,7 +1400,7 @@ void mjXReader::OneMesh(XMLElement* elem, mjsMesh* pmesh) {
|
||||
if (ReadAttrTxt(elem, "content_type", content_type)) {
|
||||
*pmesh->content_type = content_type;
|
||||
}
|
||||
auto file = ReadAttrFile(elem, "file", MeshDir());
|
||||
auto file = ReadAttrFile(elem, "file", vfs, MeshDir());
|
||||
if (file) {
|
||||
mjs_setString(pmesh->file, file->c_str());
|
||||
}
|
||||
@@ -1461,7 +1461,7 @@ void mjXReader::OneMesh(XMLElement* elem, mjsMesh* pmesh) {
|
||||
|
||||
|
||||
// skin element parser
|
||||
void mjXReader::OneSkin(XMLElement* elem, mjsSkin* pskin) {
|
||||
void mjXReader::OneSkin(XMLElement* elem, mjsSkin* pskin, const mjVFS* vfs) {
|
||||
string text, name, material;
|
||||
float data[4];
|
||||
|
||||
@@ -1469,7 +1469,7 @@ void mjXReader::OneSkin(XMLElement* elem, mjsSkin* pskin) {
|
||||
if (ReadAttrTxt(elem, "name", name)) {
|
||||
mjs_setString(pskin->name, name.c_str());
|
||||
}
|
||||
auto file = ReadAttrFile(elem, "file", AssetDir());
|
||||
auto file = ReadAttrFile(elem, "file", vfs, AssetDir());
|
||||
if (file.has_value()) {
|
||||
mjs_setString(pskin->file, file->c_str());
|
||||
}
|
||||
@@ -2536,7 +2536,7 @@ void mjXReader::OneComposite(XMLElement* elem, mjsBody* pbody, mjsDefault* def)
|
||||
|
||||
|
||||
// make flexcomp
|
||||
void mjXReader::OneFlexcomp(XMLElement* elem, mjsBody* pbody) {
|
||||
void mjXReader::OneFlexcomp(XMLElement* elem, mjsBody* pbody, const mjVFS* vfs) {
|
||||
string text, material;
|
||||
int n;
|
||||
|
||||
@@ -2554,7 +2554,7 @@ void mjXReader::OneFlexcomp(XMLElement* elem, mjsBody* pbody) {
|
||||
ReadAttr(elem, "scale", 3, fcomp.scale, text);
|
||||
ReadAttr(elem, "mass", 1, &fcomp.mass, text);
|
||||
ReadAttr(elem, "inertiabox", 1, &fcomp.inertiabox, text);
|
||||
auto maybe_file = ReadAttrFile(elem, "file", modelfiledir_);
|
||||
auto maybe_file = ReadAttrFile(elem, "file", vfs, modelfiledir_);
|
||||
if (maybe_file.has_value()) {
|
||||
fcomp.file = std::move(maybe_file.value().Str());
|
||||
} else {
|
||||
@@ -2690,7 +2690,7 @@ void mjXReader::OnePlugin(XMLElement* elem, mjsPlugin* plugin) {
|
||||
//------------------ MJCF-specific sections --------------------------------------------------------
|
||||
|
||||
// default section parser
|
||||
void mjXReader::Default(XMLElement* section, const mjsDefault* def) {
|
||||
void mjXReader::Default(XMLElement* section, const mjsDefault* def, const mjVFS* vfs) {
|
||||
XMLElement* elem;
|
||||
string text, name;
|
||||
|
||||
@@ -2721,7 +2721,7 @@ void mjXReader::Default(XMLElement* section, const mjsDefault* def) {
|
||||
name = elem->Value();
|
||||
|
||||
// read mesh
|
||||
if (name=="mesh") OneMesh(elem, def->mesh);
|
||||
if (name=="mesh") OneMesh(elem, def->mesh, vfs);
|
||||
|
||||
// read material
|
||||
else if (name=="material") OneMaterial(elem, def->material);
|
||||
@@ -2775,7 +2775,7 @@ void mjXReader::Default(XMLElement* section, const mjsDefault* def) {
|
||||
|
||||
// read default
|
||||
if (name=="default") {
|
||||
Default(elem, def);
|
||||
Default(elem, def, vfs);
|
||||
}
|
||||
|
||||
// advance
|
||||
@@ -3136,7 +3136,7 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
|
||||
if (ReadAttrTxt(elem, "content_type", content_type)) {
|
||||
mjs_setString(ptex->content_type, content_type.c_str());
|
||||
}
|
||||
auto file = ReadAttrFile(elem, "file", TextureDir());
|
||||
auto file = ReadAttrFile(elem, "file", vfs, TextureDir());
|
||||
if (file.has_value()) {
|
||||
mjs_setString(ptex->file, file->c_str());
|
||||
}
|
||||
@@ -3182,7 +3182,7 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
|
||||
"fileup", "filedown",
|
||||
"filefront", "fileback"};
|
||||
for (int i = 0; i < cubefiles.size(); i++) {
|
||||
auto maybe_file = ReadAttrFile(elem, cubefile_names[i].c_str(),
|
||||
auto maybe_file = ReadAttrFile(elem, cubefile_names[i].c_str(), vfs,
|
||||
TextureDir());
|
||||
if (maybe_file.has_value()) {
|
||||
cubefiles[i] = maybe_file.value().Str();
|
||||
@@ -3204,14 +3204,14 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
|
||||
else if (name=="mesh") {
|
||||
// create mesh and parse
|
||||
mjsMesh* pmesh = mjs_addMesh(spec, def);
|
||||
OneMesh(elem, pmesh);
|
||||
OneMesh(elem, pmesh, vfs);
|
||||
}
|
||||
|
||||
// skin sub-element... deprecate ???
|
||||
else if (name=="skin") {
|
||||
// create skin and parse
|
||||
mjsSkin* pskin = mjs_addSkin(spec);
|
||||
OneSkin(elem, pskin);
|
||||
OneSkin(elem, pskin, vfs);
|
||||
}
|
||||
|
||||
// hfield sub-element
|
||||
@@ -3230,7 +3230,7 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
|
||||
if (ReadAttrTxt(elem, "content_type", content_type)) {
|
||||
mjs_setString(phf->content_type, content_type.c_str());
|
||||
}
|
||||
auto file = ReadAttrFile(elem, "file", AssetDir());
|
||||
auto file = ReadAttrFile(elem, "file", vfs, AssetDir());
|
||||
if (file.has_value()) {
|
||||
mjs_setString(phf->file, file->c_str());
|
||||
}
|
||||
@@ -3274,7 +3274,7 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
|
||||
|
||||
// model sub-element
|
||||
else if (name=="model") {
|
||||
auto filename = modelfiledir_ + ReadAttrFile(elem, "file").value();
|
||||
auto filename = modelfiledir_ + ReadAttrFile(elem, "file", vfs).value();
|
||||
|
||||
// parse the child
|
||||
std::array<char, 1024> error;
|
||||
@@ -3301,7 +3301,8 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
|
||||
|
||||
|
||||
// body/world section parser; recursive
|
||||
void mjXReader::Body(XMLElement* section, mjsBody* pbody, mjsFrame* frame) {
|
||||
void mjXReader::Body(XMLElement* section, mjsBody* pbody, mjsFrame* frame,
|
||||
const mjVFS* vfs) {
|
||||
string text, name;
|
||||
XMLElement* elem;
|
||||
int n;
|
||||
@@ -3427,7 +3428,7 @@ void mjXReader::Body(XMLElement* section, mjsBody* pbody, mjsFrame* frame) {
|
||||
// flexcomp sub-element
|
||||
else if (name=="flexcomp") {
|
||||
// parse flexcomp
|
||||
OneFlexcomp(elem, pbody);
|
||||
OneFlexcomp(elem, pbody, vfs);
|
||||
}
|
||||
|
||||
// frame sub-element
|
||||
@@ -3459,7 +3460,7 @@ void mjXReader::Body(XMLElement* section, mjsBody* pbody, mjsFrame* frame) {
|
||||
ReadQuat(elem, "quat", pframe->quat, text);
|
||||
ReadAlternative(elem, pframe->alt);
|
||||
|
||||
Body(elem, pbody, pframe);
|
||||
Body(elem, pbody, pframe, vfs);
|
||||
}
|
||||
|
||||
// replicate sub-element
|
||||
@@ -3518,7 +3519,7 @@ void mjXReader::Body(XMLElement* section, mjsBody* pbody, mjsFrame* frame) {
|
||||
UpdateString(suffix, count, i);
|
||||
|
||||
// process subtree
|
||||
Body(elem, subtree, pframe);
|
||||
Body(elem, subtree, pframe, vfs);
|
||||
|
||||
// attach to parent
|
||||
if (mjs_attachFrame(pbody, pframe, /*prefix=*/"", suffix.c_str()) != 0) {
|
||||
@@ -3577,7 +3578,7 @@ void mjXReader::Body(XMLElement* section, mjsBody* pbody, mjsFrame* frame) {
|
||||
mjs_setFrame(pchild->element, frame);
|
||||
|
||||
// make recursive call
|
||||
Body(elem, pchild, nullptr);
|
||||
Body(elem, pchild, nullptr, vfs);
|
||||
}
|
||||
|
||||
// attachment
|
||||
@@ -3695,7 +3696,7 @@ void mjXReader::Equality(XMLElement* section) {
|
||||
|
||||
|
||||
// deformable section parser
|
||||
void mjXReader::Deformable(XMLElement* section) {
|
||||
void mjXReader::Deformable(XMLElement* section, const mjVFS* vfs) {
|
||||
string name;
|
||||
XMLElement* elem;
|
||||
|
||||
@@ -3722,7 +3723,7 @@ void mjXReader::Deformable(XMLElement* section) {
|
||||
else if (name=="skin") {
|
||||
// create skin and parse
|
||||
mjsSkin* pskin = mjs_addSkin(spec);
|
||||
OneSkin(elem, pskin);
|
||||
OneSkin(elem, pskin, vfs);
|
||||
}
|
||||
|
||||
// advance to next element
|
||||
|
||||
@@ -49,16 +49,17 @@ class mjXReader : public mjXBase {
|
||||
|
||||
private:
|
||||
// XML section specific to MJCF
|
||||
void Default(tinyxml2::XMLElement* section, const mjsDefault* def); // default section
|
||||
void Default(tinyxml2::XMLElement* section, const mjsDefault* def,
|
||||
const mjVFS* vfs); // default section
|
||||
void Extension(tinyxml2::XMLElement* section); // extension section
|
||||
void Custom(tinyxml2::XMLElement* section); // custom section
|
||||
void Visual(tinyxml2::XMLElement* section); // visual section
|
||||
void Statistic(tinyxml2::XMLElement* section); // statistic section
|
||||
void Asset(tinyxml2::XMLElement* section, const mjVFS* vfs); // asset section
|
||||
void Body(tinyxml2::XMLElement* section, mjsBody* pbody,
|
||||
mjsFrame* pframe); // body/world section
|
||||
mjsFrame* pframe, const mjVFS* vfs); // body/world section
|
||||
void Contact(tinyxml2::XMLElement* section); // contact section
|
||||
void Deformable(tinyxml2::XMLElement* section); // deformable section
|
||||
void Deformable(tinyxml2::XMLElement* section, const mjVFS* vfs); // deformable section
|
||||
void Equality(tinyxml2::XMLElement* section); // equality section
|
||||
void Tendon(tinyxml2::XMLElement* section); // tendon section
|
||||
void Actuator(tinyxml2::XMLElement* section); // actuator section
|
||||
@@ -67,8 +68,8 @@ class mjXReader : public mjXBase {
|
||||
|
||||
// single element parsers, used in defaults and main body
|
||||
void OneFlex(tinyxml2::XMLElement* elem, mjsFlex* pflex);
|
||||
void OneMesh(tinyxml2::XMLElement* elem, mjsMesh* pmesh);
|
||||
void OneSkin(tinyxml2::XMLElement* elem, mjsSkin* pskin);
|
||||
void OneMesh(tinyxml2::XMLElement* elem, mjsMesh* pmesh, const mjVFS* vfs);
|
||||
void OneSkin(tinyxml2::XMLElement* elem, mjsSkin* pskin, const mjVFS* vfs);
|
||||
void OneMaterial(tinyxml2::XMLElement* elem, mjsMaterial* pmaterial);
|
||||
void OneJoint(tinyxml2::XMLElement* elem, mjsJoint* pjoint);
|
||||
void OneGeom(tinyxml2::XMLElement* elem, mjsGeom* pgeom);
|
||||
@@ -80,7 +81,7 @@ class mjXReader : public mjXBase {
|
||||
void OneTendon(tinyxml2::XMLElement* elem, mjsTendon* ptendon);
|
||||
void OneActuator(tinyxml2::XMLElement* elem, mjsActuator* pactuator);
|
||||
void OneComposite(tinyxml2::XMLElement* elem, mjsBody* pbody, mjsDefault* def);
|
||||
void OneFlexcomp(tinyxml2::XMLElement* elem, mjsBody* pbody);
|
||||
void OneFlexcomp(tinyxml2::XMLElement* elem, mjsBody* pbody, const mjVFS* vfs);
|
||||
void OnePlugin(tinyxml2::XMLElement* elem, mjsPlugin* plugin);
|
||||
|
||||
mjXSchema schema; // schema used for validation
|
||||
|
||||
+4
-4
@@ -76,7 +76,7 @@ static std::optional<T> ParseInfOrNan(const std::string& s) {
|
||||
}
|
||||
|
||||
FilePath ResolveFilePath(XMLElement* e, const FilePath& filename,
|
||||
const FilePath& dir) {
|
||||
const FilePath& dir, const mjVFS* vfs) {
|
||||
std::string path = "";
|
||||
if (filename.IsAbs()) {
|
||||
return filename;
|
||||
@@ -85,7 +85,7 @@ FilePath ResolveFilePath(XMLElement* e, const FilePath& filename,
|
||||
// TODO(kylebayes): We first look in the base model directory for files to
|
||||
// remain backwards compatible.
|
||||
FilePath fullname = dir + filename;
|
||||
mjResource *resource = mju_openResource(fullname.c_str(), nullptr,
|
||||
mjResource *resource = mju_openResource("", fullname.c_str(), vfs,
|
||||
nullptr, 0);
|
||||
if (resource != nullptr) {
|
||||
mju_closeResource(resource);
|
||||
@@ -623,14 +623,14 @@ mjXUtil::ReadAttrStr(XMLElement* elem, const char* attr, bool required) {
|
||||
|
||||
// if attribute is present, return attribute as a filename
|
||||
std::optional<FilePath>
|
||||
mjXUtil::ReadAttrFile(XMLElement* elem, const char* attr,
|
||||
mjXUtil::ReadAttrFile(XMLElement* elem, const char* attr, const mjVFS* vfs,
|
||||
const FilePath& dir, bool required) {
|
||||
auto maybe_str = ReadAttrStr(elem, attr, required);
|
||||
if (!maybe_str.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
FilePath filename(maybe_str.value());
|
||||
return ResolveFilePath(elem, filename, dir);
|
||||
return ResolveFilePath(elem, filename, dir, vfs);
|
||||
}
|
||||
|
||||
// if attribute is present, return numerical value of attribute
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "tinyxml2.h"
|
||||
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "user/user_util.h"
|
||||
|
||||
// error string copy
|
||||
@@ -114,6 +115,7 @@ class mjXUtil {
|
||||
// if attribute is present, return attribute as a filename
|
||||
static std::optional<mujoco::user::FilePath>
|
||||
ReadAttrFile(tinyxml2::XMLElement* elem, const char* attr,
|
||||
const mjVFS* vfs,
|
||||
const mujoco::user::FilePath& dir = mujoco::user::FilePath(),
|
||||
bool required = false);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user