Fix bug with absolute meshdirs in included XML files. Fixes #1772.

PiperOrigin-RevId: 649150979
Change-Id: I5df08c689afc4f5bc625e989a8c0b9733fcfa3c2
This commit is contained in:
Kyle Bayes
2024-07-03 11:48:13 -07:00
committed by Copybara-Service
parent 9107d3b507
commit 2a4410e4e5
8 changed files with 75 additions and 45 deletions
+2 -2
View File
@@ -900,7 +900,7 @@ bool mjCFlexcomp::MakeMesh(mjCModel* model, char* error, int error_sz) {
}
// load resource
std::string filename = mjuu_makefullname(mjs_getString(model->spec.modelfiledir),
std::string filename = mjuu_combinePaths(mjs_getString(model->spec.modelfiledir),
mjs_getString(model->spec.meshdir), file);
mjResource* resource = nullptr;
@@ -999,7 +999,7 @@ bool mjCFlexcomp::MakeGMSH(mjCModel* model, char* error, int error_sz) {
}
// open resource
std::string filename = mjuu_makefullname(mjs_getString(model->spec.modelfiledir),
std::string filename = mjuu_combinePaths(mjs_getString(model->spec.modelfiledir),
mjs_getString(model->spec.meshdir), file);
mjResource* resource = nullptr;
+2 -2
View File
@@ -368,7 +368,7 @@ void mjCMesh::Compile(const mjVFS* vfs) {
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
}
string filename = mjuu_makefullname(model->modelfiledir_, model->meshdir_, file_);
string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
mjResource* resource = LoadResource(filename, vfs);
try {
@@ -2091,7 +2091,7 @@ void mjCSkin::Compile(const mjVFS* vfs) {
throw mjCError(this, "Unknown skin file type: %s", file_.c_str());
}
string filename = mjuu_makefullname(model->modelfiledir_, model->meshdir_, file_);
string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
mjResource* resource = LoadResource(filename, vfs);
try {
+3 -3
View File
@@ -3105,7 +3105,7 @@ void mjCHField::Compile(const mjVFS* vfs) {
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
}
string filename = mjuu_makefullname(model->modelfiledir_, model->meshdir_, file_);
string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
mjResource* resource = LoadResource(filename, vfs);
try {
@@ -3723,7 +3723,7 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
}
// make filename
string filename = mjuu_makefullname(model->modelfiledir_, model->texturedir_, cubefiles_[i]);
string filename = mjuu_combinePaths(model->modelfiledir_, model->texturedir_, cubefiles_[i]);
// load PNG or custom
unsigned int w, h;
@@ -3821,7 +3821,7 @@ void mjCTexture::Compile(const mjVFS* vfs) {
}
// make filename
string filename = mjuu_makefullname(model->modelfiledir_, model->texturedir_, file_);
string filename = mjuu_combinePaths(model->modelfiledir_, model->texturedir_, file_);
// dispatch
if (type==mjTEXTURE_2D) {
+18 -11
View File
@@ -837,23 +837,30 @@ bool mjuu_isabspath(string path) {
// assemble full filename
string mjuu_makefullname(string filedir, string meshdir, string filename) {
// filename has absolute path: filename
if (mjuu_isabspath(filename)) {
return filename;
// assemble two file paths
std::string mjuu_combinePaths(const string& path1, const string& path2) {
// path2 has absolute path
if (mjuu_isabspath(path2)) {
return path2;
}
// meshdir has absolute path: meshdir + filename
if (mjuu_isabspath(meshdir)) {
return meshdir + filename;
std::size_t n = path1.size();
if (n > 0 && path1[n - 1] != '\\' && path1[n - 1] != '/') {
return path1 + "/" + path2;
}
// default
return filedir + meshdir + filename;
return path1 + path2;
}
// assemble three file paths
std::string mjuu_combinePaths(const string& path1, const string& path2,
const string& path3) {
return mjuu_combinePaths(path1, mjuu_combinePaths(path2, path3));
}
// return true if the text is in a valid content type format:
// {type}/{subtype}[;{parameter}={value}]
static bool mjuu_isValidContentType(std::string_view text) {
+4 -2
View File
@@ -167,8 +167,10 @@ std::string mjuu_getext(std::string_view filename);
// check if path is absolute
bool mjuu_isabspath(std::string path);
// assemble full filename
std::string mjuu_makefullname(std::string filedir, std::string meshdir, std::string filename);
// assemble file paths
std::string mjuu_combinePaths(const std::string& path1, const std::string& path2);
std::string mjuu_combinePaths(const std::string& path1, const std::string& path2,
const std::string& path3);
// return type from content_type format {type}/{subtype}[;{parameter}={value}]
std::optional<std::string_view> mjuu_parseContentTypeAttrType(std::string_view text);
+3 -23
View File
@@ -4120,56 +4120,36 @@ mjsDefault* mjXReader::GetClass(XMLElement* section) {
return def;
}
// return true if c is a directory path separator (i.e. '/' or '\' on windows)
static bool IsSeperator(char c) {
return c == '/' || c == '\\';
}
void mjXReader::SetModelFileDir(std::string modelfiledir) {
modelfiledir_ = modelfiledir;
if (!modelfiledir_.empty() && !IsSeperator(modelfiledir_.back())) {
modelfiledir_.append("/");
}
}
void mjXReader::SetAssetDir(std::string assetdir) {
assetdir_ = assetdir;
if (!assetdir_.empty() && !IsSeperator(assetdir_.back())) {
assetdir_.append("/");
}
}
void mjXReader::SetMeshDir(std::string meshdir) {
meshdir_ = meshdir;
if (!meshdir_.empty() && !IsSeperator(meshdir_.back())) {
meshdir_.append("/");
}
}
void mjXReader::SetTextureDir(std::string texturedir) {
texturedir_ = texturedir;
if (!texturedir_.empty() && !IsSeperator(texturedir_.back())) {
texturedir_.append("/");
}
}
std::string mjXReader::AssetDir() const {
return modelfiledir_ + assetdir_;
return mjuu_combinePaths(modelfiledir_, assetdir_);
}
std::string mjXReader::MeshDir() const {
if (meshdir_.empty()) {
return AssetDir();
}
return modelfiledir_ + meshdir_;
return mjuu_combinePaths(modelfiledir_, meshdir_);
}
std::string mjXReader::TextureDir() const {
if (texturedir_.empty()) {
return AssetDir();
}
return modelfiledir_ + texturedir_;
return mjuu_combinePaths(modelfiledir_, texturedir_);
}
+2 -2
View File
@@ -168,7 +168,7 @@ static std::string ResolveFilePath(XMLElement* e, std::string filename,
// TODO(kylebayes): We first look in the base model directory for files to
// remain backwards compatible.
std::string full_filename = dir + filename;
std::string full_filename = mjuu_combinePaths(dir, filename);
mjResource *resource = mju_openResource(full_filename.c_str(), nullptr, 0);
if (resource != nullptr) {
mju_closeResource(resource);
@@ -185,7 +185,7 @@ static std::string ResolveFilePath(XMLElement* e, std::string filename,
break;
}
}
return path + filename;
return mjuu_combinePaths(path, filename);
}
// constructor
+41
View File
@@ -906,6 +906,47 @@ TEST_F(XMLReaderTest, IncludeAbsoluteTest) {
mj_deleteModel(model);
}
TEST_F(XMLReaderTest, IncludeAbsoluteMeshDirTest) {
static constexpr char xml[] = R"(
<mujoco>
<include file="assets.xml"/>
</mujoco>
)";
static constexpr char assets[] = R"(
<mujoco>
<compiler meshdir="mjMock.IncludeAbsoluteMeshDirTest:/assets"/>
<asset>
<mesh file="cube.obj"/>
</asset>
</mujoco>
)";
static constexpr char cube[] = R"(
v -0.500000 -0.500000 0.500000
v 0.500000 -0.500000 0.500000
v -0.500000 0.500000 0.500000
v 0.500000 0.500000 0.500000
v -0.500000 0.500000 -0.500000
v 0.500000 0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
v 0.500000 -0.500000 -0.500000)";
MockFilesystem fs("IncludeAbsoluteMeshDirTest");
fs.AddFile("/assets/cube.obj", (const unsigned char*) cube, sizeof(cube));
fs.AddFile("assets.xml", (const unsigned char*) assets,
sizeof(assets));
fs.AddFile("model.xml", (const unsigned char*) xml, sizeof(xml));
std::string modelpath = fs.FullPath("model.xml");
std::array<char, 1024> error;
// loading the file should be successful
mjModel* model = mj_loadXML(modelpath.c_str(), nullptr,
error.data(), error.size());
ASSERT_THAT(model, NotNull()) << "Failed to load model: " << error.data();
mj_deleteModel(model);
}
TEST_F(XMLReaderTest, ParsePolycoef) {
static constexpr char xml[] = R"(
<mujoco>