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
@@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cctype>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -84,6 +86,22 @@ constexpr auto XArrayShapeImpl(const std::string_view dim1_str) {
|
|||||||
inline std::size_t NConMax(const mjData* d) {
|
inline std::size_t NConMax(const mjData* d) {
|
||||||
return d->narena / sizeof(mjContact);
|
return d->narena / sizeof(mjContact);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// strip path prefix from filename and make lowercase
|
||||||
|
std::string StripPath(const char* name) {
|
||||||
|
std::string filename(name);
|
||||||
|
size_t start = filename.find_last_of("/\\");
|
||||||
|
|
||||||
|
// get name without path
|
||||||
|
if (start != std::string::npos) {
|
||||||
|
filename = filename.substr(start + 1, filename.size() - start - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make lowercase
|
||||||
|
std::transform(filename.begin(), filename.end(), filename.begin(),
|
||||||
|
[](unsigned char c) { return std::tolower(c); });
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// ==================== MJOPTION ===============================================
|
// ==================== MJOPTION ===============================================
|
||||||
@@ -323,8 +341,9 @@ static raw::MjModel* LoadModelFileImpl(
|
|||||||
mj_defaultVFS(&vfs);
|
mj_defaultVFS(&vfs);
|
||||||
vfs_ptr = &vfs;
|
vfs_ptr = &vfs;
|
||||||
for (const auto& asset : assets) {
|
for (const auto& asset : assets) {
|
||||||
|
std::string buffer_name = StripPath(asset.name);
|
||||||
const int vfs_error = InterceptMjErrors(mj_addBufferVFS)(
|
const int vfs_error = InterceptMjErrors(mj_addBufferVFS)(
|
||||||
vfs_ptr, asset.name, asset.content, asset.content_size);
|
vfs_ptr, buffer_name.c_str(), asset.content, asset.content_size);
|
||||||
if (vfs_error) {
|
if (vfs_error) {
|
||||||
throw py::value_error("assets dict is too big");
|
throw py::value_error("assets dict is too big");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -900,12 +900,12 @@ bool mjCFlexcomp::MakeMesh(mjCModel* model, char* error, int error_sz) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// load resource
|
// load resource
|
||||||
std::string filename = mjuu_combinePaths(mjs_getString(model->spec.modelfiledir),
|
std::string filename = mjuu_combinePaths(mjs_getString(model->spec.meshdir), file);
|
||||||
mjs_getString(model->spec.meshdir), file);
|
|
||||||
mjResource* resource = nullptr;
|
mjResource* resource = nullptr;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
resource = mjCBase::LoadResource(filename, 0);
|
resource = mjCBase::LoadResource(mjs_getString(model->spec.modelfiledir),
|
||||||
|
filename, 0);
|
||||||
} catch (mjCError err) {
|
} catch (mjCError err) {
|
||||||
return comperr(error, err.message, error_sz);
|
return comperr(error, err.message, error_sz);
|
||||||
}
|
}
|
||||||
@@ -999,12 +999,11 @@ bool mjCFlexcomp::MakeGMSH(mjCModel* model, char* error, int error_sz) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// open resource
|
// open resource
|
||||||
std::string filename = mjuu_combinePaths(mjs_getString(model->spec.modelfiledir),
|
|
||||||
mjs_getString(model->spec.meshdir), file);
|
|
||||||
mjResource* resource = nullptr;
|
mjResource* resource = nullptr;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
resource = mjCBase::LoadResource(filename, 0);
|
std::string filename = mjuu_combinePaths(mjs_getString(model->spec.meshdir), file);
|
||||||
|
resource = mjCBase::LoadResource(mjs_getString(model->spec.modelfiledir),
|
||||||
|
filename, 0);
|
||||||
} catch (mjCError err) {
|
} catch (mjCError err) {
|
||||||
return comperr(error, err.message, error_sz);
|
return comperr(error, err.message, error_sz);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -402,8 +402,8 @@ void mjCMesh::Compile(const mjVFS* vfs) {
|
|||||||
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
|
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
|
std::string filename = mjuu_combinePaths(model->meshdir_, file_);
|
||||||
mjResource* resource = LoadResource(filename, vfs);
|
mjResource* resource = LoadResource(model->modelfiledir_, filename, vfs);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (asset_type == "model/stl") {
|
if (asset_type == "model/stl") {
|
||||||
@@ -2095,8 +2095,8 @@ void mjCSkin::Compile(const mjVFS* vfs) {
|
|||||||
throw mjCError(this, "Unknown skin file type: %s", file_.c_str());
|
throw mjCError(this, "Unknown skin file type: %s", file_.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
|
std::string filename = mjuu_combinePaths(model->meshdir_, file_);
|
||||||
mjResource* resource = LoadResource(filename, vfs);
|
mjResource* resource = LoadResource(model->modelfiledir_, filename, vfs);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
LoadSKN(resource);
|
LoadSKN(resource);
|
||||||
|
|||||||
@@ -701,10 +701,12 @@ void mjCBase::NameSpace(const mjCModel* m) {
|
|||||||
|
|
||||||
|
|
||||||
// load resource if found (fallback to OS filesystem)
|
// load resource if found (fallback to OS filesystem)
|
||||||
mjResource* mjCBase::LoadResource(std::string filename, const mjVFS* vfs) {
|
mjResource* mjCBase::LoadResource(const std::string& modelfiledir,
|
||||||
|
const std::string& filename,
|
||||||
|
const mjVFS* vfs) {
|
||||||
// try reading from provided VFS or fallback to OS filesystem
|
// try reading from provided VFS or fallback to OS filesystem
|
||||||
std::array<char, 1024> error;
|
std::array<char, 1024> error;
|
||||||
mjResource* resource = mju_openResource(filename.c_str(), vfs,
|
mjResource* resource = mju_openResource(modelfiledir.c_str(), filename.c_str(), vfs,
|
||||||
error.data(), error.size());
|
error.data(), error.size());
|
||||||
if (!resource) {
|
if (!resource) {
|
||||||
throw mjCError(nullptr, "%s", error.data());
|
throw mjCError(nullptr, "%s", error.data());
|
||||||
@@ -3159,8 +3161,8 @@ void mjCHField::Compile(const mjVFS* vfs) {
|
|||||||
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
|
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string filename = mjuu_combinePaths(model->modelfiledir_, model->meshdir_, file_);
|
std::string filename = mjuu_combinePaths(model->meshdir_, file_);
|
||||||
mjResource* resource = LoadResource(filename, vfs);
|
mjResource* resource = LoadResource(model->modelfiledir_, filename, vfs);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (asset_type == "image/png") {
|
if (asset_type == "image/png") {
|
||||||
@@ -3594,7 +3596,7 @@ void mjCTexture::LoadFlip(std::string filename, const mjVFS* vfs,
|
|||||||
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
|
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
mjResource* resource = LoadResource(filename, vfs);
|
mjResource* resource = LoadResource(model->modelfiledir_, filename, vfs);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (asset_type == "image/png") {
|
if (asset_type == "image/png") {
|
||||||
@@ -3797,7 +3799,7 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// make filename
|
// make filename
|
||||||
std::string filename = mjuu_combinePaths(model->modelfiledir_, model->texturedir_, cubefiles_[i]);
|
std::string filename = mjuu_combinePaths(model->texturedir_, cubefiles_[i]);
|
||||||
|
|
||||||
// load PNG or custom
|
// load PNG or custom
|
||||||
unsigned int w, h;
|
unsigned int w, h;
|
||||||
@@ -3895,7 +3897,7 @@ void mjCTexture::Compile(const mjVFS* vfs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// make filename
|
// make filename
|
||||||
std::string filename = mjuu_combinePaths(model->modelfiledir_, model->texturedir_, file_);
|
std::string filename = mjuu_combinePaths(model->texturedir_, file_);
|
||||||
|
|
||||||
// dispatch
|
// dispatch
|
||||||
if (type==mjTEXTURE_2D) {
|
if (type==mjTEXTURE_2D) {
|
||||||
|
|||||||
@@ -181,7 +181,8 @@ class mjCBase : public mjCBase_ {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// load resource if found (fallback to OS filesystem)
|
// load resource if found (fallback to OS filesystem)
|
||||||
static mjResource* LoadResource(std::string filename, const mjVFS* vfs);
|
static mjResource* LoadResource(const std::string& modelfiledir,
|
||||||
|
const std::string& filename, const mjVFS* vfs);
|
||||||
|
|
||||||
// Get and sanitize content type from raw_text if not empty, otherwise parse
|
// Get and sanitize content type from raw_text if not empty, otherwise parse
|
||||||
// content type from resource_name; throw on failure
|
// content type from resource_name; throw on failure
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
|
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
|
||||||
@@ -117,8 +118,8 @@ int FileModified(const mjResource* resource, const char*timestamp) {
|
|||||||
|
|
||||||
// open the given resource; if the name doesn't have a prefix matching with a
|
// open the given resource; if the name doesn't have a prefix matching with a
|
||||||
// resource provider, then the OS filesystem is used
|
// resource provider, then the OS filesystem is used
|
||||||
mjResource* mju_openResource(const char* name, const mjVFS* vfs,
|
mjResource* mju_openResource(const char* dir, const char* name,
|
||||||
char* error, size_t nerror) {
|
const mjVFS* vfs, char* error, size_t nerror) {
|
||||||
// no error so far
|
// no error so far
|
||||||
if (error) {
|
if (error) {
|
||||||
error[0] = '\0';
|
error[0] = '\0';
|
||||||
@@ -136,8 +137,10 @@ mjResource* mju_openResource(const char* name, const mjVFS* vfs,
|
|||||||
// clear out resource
|
// clear out resource
|
||||||
memset(resource, 0, sizeof(mjResource));
|
memset(resource, 0, sizeof(mjResource));
|
||||||
|
|
||||||
// copy name
|
// make space for filename
|
||||||
resource->name = (char*) mju_malloc(sizeof(char) * (strlen(name) + 1));
|
std::string fullname = mjuu_combinePaths(dir, name);
|
||||||
|
std::size_t n = fullname.size();
|
||||||
|
resource->name = (char*) mju_malloc(sizeof(char) * (n + 1));
|
||||||
if (resource->name == nullptr) {
|
if (resource->name == nullptr) {
|
||||||
if (error) {
|
if (error) {
|
||||||
strncpy(error, "could not allocate memory", nerror);
|
strncpy(error, "could not allocate memory", nerror);
|
||||||
@@ -146,10 +149,11 @@ mjResource* mju_openResource(const char* name, const mjVFS* vfs,
|
|||||||
mju_closeResource(resource);
|
mju_closeResource(resource);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
memcpy(resource->name, name, sizeof(char) * (strlen(name) + 1));
|
|
||||||
|
|
||||||
// first priority is to check the VFS
|
// first priority is to check the VFS
|
||||||
if (vfs != nullptr) {
|
if (vfs != nullptr) {
|
||||||
|
memcpy(resource->name, name,
|
||||||
|
sizeof(char) * (std::strlen(name) + 1));
|
||||||
const mjpResourceProvider* provider = GetVfsResourceProvider();
|
const mjpResourceProvider* provider = GetVfsResourceProvider();
|
||||||
resource->data = (void*) vfs;
|
resource->data = (void*) vfs;
|
||||||
resource->provider = provider;
|
resource->provider = provider;
|
||||||
@@ -158,8 +162,11 @@ mjResource* mju_openResource(const char* name, const mjVFS* vfs,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy full path over
|
||||||
|
memcpy(resource->name, fullname.c_str(), sizeof(char) * (n + 1));
|
||||||
|
|
||||||
// find provider based off prefix of name
|
// find provider based off prefix of name
|
||||||
const mjpResourceProvider* provider = mjp_getResourceProvider(name);
|
const mjpResourceProvider* provider = mjp_getResourceProvider(resource->name);
|
||||||
if (provider != nullptr) {
|
if (provider != nullptr) {
|
||||||
resource->provider = provider;
|
resource->provider = provider;
|
||||||
resource->data = nullptr;
|
resource->data = nullptr;
|
||||||
@@ -170,7 +177,7 @@ mjResource* mju_openResource(const char* name, const mjVFS* vfs,
|
|||||||
if (error) {
|
if (error) {
|
||||||
snprintf(error, nerror, "could not open '%s'"
|
snprintf(error, nerror, "could not open '%s'"
|
||||||
"using a resource provider matching prefix '%s'",
|
"using a resource provider matching prefix '%s'",
|
||||||
name, provider->prefix);
|
resource->name, provider->prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
mju_closeResource(resource);
|
mju_closeResource(resource);
|
||||||
|
|||||||
@@ -18,8 +18,6 @@
|
|||||||
#define MUJOCO_SRC_ENGINE_ENGINE_RESOURCE_H_
|
#define MUJOCO_SRC_ENGINE_ENGINE_RESOURCE_H_
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <mujoco/mjexport.h>
|
#include <mujoco/mjexport.h>
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
@@ -30,8 +28,8 @@ extern "C" {
|
|||||||
|
|
||||||
// open the given resource; if the name doesn't have a prefix matching with a
|
// open the given resource; if the name doesn't have a prefix matching with a
|
||||||
// resource provider, then the OS filesystem is used
|
// resource provider, then the OS filesystem is used
|
||||||
MJAPI mjResource* mju_openResource(const char* name, const mjVFS* vfs,
|
MJAPI mjResource* mju_openResource(const char* dir, const char* name,
|
||||||
char* error, std::size_t nerror);
|
const mjVFS* vfs, char* error, size_t nerror);
|
||||||
|
|
||||||
// close the given resource; no-op if resource is NULL
|
// close the given resource; no-op if resource is NULL
|
||||||
MJAPI void mju_closeResource(mjResource* resource);
|
MJAPI void mju_closeResource(mjResource* resource);
|
||||||
|
|||||||
+12
-7
@@ -142,9 +142,13 @@ int Open(mjResource* resource) {
|
|||||||
const VFS* cvfs = GetVFSImpl(vfs);
|
const VFS* cvfs = GetVFSImpl(vfs);
|
||||||
const VFSFile* file = cvfs->GetFile(StripPath(resource->name));
|
const VFSFile* file = cvfs->GetFile(StripPath(resource->name));
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
return 0;
|
file = cvfs->GetFile(FilePath(resource->name));
|
||||||
|
if (file == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resource->data = (void*) file;
|
||||||
resource->timestamp[0] = '\0';
|
resource->timestamp[0] = '\0';
|
||||||
if (file->filestamp) {
|
if (file->filestamp) {
|
||||||
mju_encodeBase64(resource->timestamp, (uint8_t*) &file->filestamp,
|
mju_encodeBase64(resource->timestamp, (uint8_t*) &file->filestamp,
|
||||||
@@ -160,8 +164,7 @@ int Read(mjResource* resource, const void** buffer) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VFS* vfs = GetVFSImpl(static_cast<const mjVFS*>(resource->data));
|
const VFSFile* file = static_cast<const VFSFile*>(resource->data);
|
||||||
const VFSFile* file = vfs->GetFile(StripPath(resource->name));
|
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
*buffer = nullptr;
|
*buffer = nullptr;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -193,8 +196,7 @@ int Modified(const mjResource* resource, const char* timestamp) {
|
|||||||
if (!filestamp) return 3; // no hash (assume modified)
|
if (!filestamp) return 3; // no hash (assume modified)
|
||||||
|
|
||||||
if (resource) {
|
if (resource) {
|
||||||
const VFS* cvfs = GetVFSImpl(static_cast<const mjVFS*>(resource->data));
|
const VFSFile* file = static_cast<const VFSFile*>(resource->data);
|
||||||
const VFSFile* file = cvfs->GetFile(StripPath(resource->name));
|
|
||||||
if (file == nullptr) return 4; // missing file (assume modified)
|
if (file == nullptr) return 4; // missing file (assume modified)
|
||||||
if (!file->filestamp) return 5; // missing filestamp (assume modified)
|
if (!file->filestamp) return 5; // missing filestamp (assume modified)
|
||||||
|
|
||||||
@@ -245,7 +247,7 @@ int mj_addBufferVFS(mjVFS* vfs, const char* name, const void* buffer,
|
|||||||
std::vector<uint8_t> inbuffer;
|
std::vector<uint8_t> inbuffer;
|
||||||
VFS* cvfs = GetVFSImpl(vfs);
|
VFS* cvfs = GetVFSImpl(vfs);
|
||||||
VFSFile* file;
|
VFSFile* file;
|
||||||
if (!(file = cvfs->AddFile(StripPath(name), std::move(inbuffer), 0))) {
|
if (!(file = cvfs->AddFile(FilePath(name), std::move(inbuffer), 0))) {
|
||||||
return 2; // AddFile failed, repeated name
|
return 2; // AddFile failed, repeated name
|
||||||
}
|
}
|
||||||
file->filedata.reserve(nbuffer);
|
file->filedata.reserve(nbuffer);
|
||||||
@@ -256,7 +258,10 @@ int mj_addBufferVFS(mjVFS* vfs, const char* name, const void* buffer,
|
|||||||
// delete file from VFS, return 0: success, -1: not found in VFS
|
// delete file from VFS, return 0: success, -1: not found in VFS
|
||||||
int mj_deleteFileVFS(mjVFS* vfs, const char* filename) {
|
int mj_deleteFileVFS(mjVFS* vfs, const char* filename) {
|
||||||
VFS* cvfs = GetVFSImpl(vfs);
|
VFS* cvfs = GetVFSImpl(vfs);
|
||||||
return cvfs->DeleteFile(StripPath(filename));
|
if (cvfs->DeleteFile(StripPath(filename))) {
|
||||||
|
return cvfs->DeleteFile(FilePath(filename));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete all files from VFS
|
// delete all files from VFS
|
||||||
|
|||||||
+9
-10
@@ -156,8 +156,8 @@ void IncludeXML(mjXReader& reader, XMLElement* elem,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get filename
|
// get filename
|
||||||
auto file_attr = mjXUtil::ReadAttrFile(elem, "file", reader.ModelFileDir(),
|
auto file_attr = mjXUtil::ReadAttrFile(elem, "file", vfs,
|
||||||
true);
|
reader.ModelFileDir(), true);
|
||||||
if (!file_attr.has_value()) {
|
if (!file_attr.has_value()) {
|
||||||
throw mjXError(elem, "Include element missing file attribute");
|
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
|
// TODO: b/325905702 - We have a messy wrapper here to remain backwards
|
||||||
// compatible, which will be removed in the near future.
|
// compatible, which will be removed in the near future.
|
||||||
FilePath fullname = reader.ModelFileDir() + filename;
|
|
||||||
|
|
||||||
// legacy behavior: try to load in top level directory
|
// legacy behavior: try to load in top level directory
|
||||||
std::array<char, 1024> error;
|
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());
|
error.data(), error.size());
|
||||||
if (resource == nullptr) {
|
if (resource == nullptr) {
|
||||||
// new behavior: try to load in relative directory
|
// new behavior: try to load in relative directory
|
||||||
if (!filename.IsAbs()) {
|
if (!filename.IsAbs()) {
|
||||||
fullname = dir + filename;
|
FilePath fullname = dir + filename;
|
||||||
resource = mju_openResource(fullname.c_str(), vfs, error.data(), error.size());
|
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;
|
int ninclude_dir = 0;
|
||||||
mju_getResourceDir(resource, &include_dir, &ninclude_dir);
|
mju_getResourceDir(resource, &include_dir, &ninclude_dir);
|
||||||
FilePath next_dir = FilePath(std::string(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());
|
elem->SetAttribute("dir", next_dir.c_str());
|
||||||
|
|
||||||
const char* xmlstring = nullptr;
|
const char* xmlstring = nullptr;
|
||||||
@@ -285,7 +284,7 @@ mjSpec* ParseXML(const char* filename, const mjVFS* vfs,
|
|||||||
// get data source
|
// get data source
|
||||||
const char* xmlstring = nullptr;
|
const char* xmlstring = nullptr;
|
||||||
std::array<char, 1024> rerror;
|
std::array<char, 1024> rerror;
|
||||||
mjResource* resource = mju_openResource(filename, vfs,
|
mjResource* resource = mju_openResource("", filename, vfs,
|
||||||
rerror.data(), rerror.size());
|
rerror.data(), rerror.size());
|
||||||
if (resource == nullptr) {
|
if (resource == nullptr) {
|
||||||
std::snprintf(error, nerror, "ParseXML: %s", rerror.data());
|
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};
|
std::unordered_set<std::string> included = {filename};
|
||||||
mjXReader parser;
|
mjXReader parser;
|
||||||
parser.SetModelFileDir(mjs_getString(spec->modelfiledir));
|
parser.SetModelFileDir(mjs_getString(spec->modelfiledir));
|
||||||
IncludeXML(parser, root, parser.ModelFileDir(), vfs, included);
|
IncludeXML(parser, root, FilePath(), vfs, included);
|
||||||
|
|
||||||
// parse MuJoCo model
|
// parse MuJoCo model
|
||||||
parser.SetModel(spec);
|
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
|
// load model from binary MJB resource
|
||||||
mjModel* mj_loadModel(const char* filename, const mjVFS* vfs) {
|
mjModel* mj_loadModel(const char* filename, const mjVFS* vfs) {
|
||||||
std::array<char, 1024> error;
|
std::array<char, 1024> error;
|
||||||
mjResource* resource = mju_openResource(filename, vfs,
|
mjResource* resource = mju_openResource("", filename, vfs,
|
||||||
error.data(), error.size());
|
error.data(), error.size());
|
||||||
if (resource == nullptr) {
|
if (resource == nullptr) {
|
||||||
mju_warning("%s", error.data());
|
mju_warning("%s", error.data());
|
||||||
|
|||||||
@@ -893,7 +893,7 @@ void mjXReader::Parse(XMLElement* root, const mjVFS* vfs) {
|
|||||||
readingdefaults = true;
|
readingdefaults = true;
|
||||||
for (XMLElement* section = FirstChildElement(root, "default"); section;
|
for (XMLElement* section = FirstChildElement(root, "default"); section;
|
||||||
section = NextSiblingElement(section, "default")) {
|
section = NextSiblingElement(section, "default")) {
|
||||||
Default(section, nullptr);
|
Default(section, nullptr, vfs);
|
||||||
}
|
}
|
||||||
readingdefaults = false;
|
readingdefaults = false;
|
||||||
|
|
||||||
@@ -919,7 +919,7 @@ void mjXReader::Parse(XMLElement* root, const mjVFS* vfs) {
|
|||||||
|
|
||||||
for (XMLElement* section = FirstChildElement(root, "deformable"); section;
|
for (XMLElement* section = FirstChildElement(root, "deformable"); section;
|
||||||
section = NextSiblingElement(section, "deformable")) {
|
section = NextSiblingElement(section, "deformable")) {
|
||||||
Deformable(section);
|
Deformable(section, vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (XMLElement* section = FirstChildElement(root, "equality"); section;
|
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;
|
for (XMLElement* section = FirstChildElement(root, "worldbody"); section;
|
||||||
section = NextSiblingElement(section, "worldbody")) {
|
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
|
// mesh element parser
|
||||||
void mjXReader::OneMesh(XMLElement* elem, mjsMesh* pmesh) {
|
void mjXReader::OneMesh(XMLElement* elem, mjsMesh* pmesh, const mjVFS* vfs) {
|
||||||
int n;
|
int n;
|
||||||
string text, name, content_type;
|
string text, name, content_type;
|
||||||
|
|
||||||
@@ -1400,7 +1400,7 @@ void mjXReader::OneMesh(XMLElement* elem, mjsMesh* pmesh) {
|
|||||||
if (ReadAttrTxt(elem, "content_type", content_type)) {
|
if (ReadAttrTxt(elem, "content_type", content_type)) {
|
||||||
*pmesh->content_type = content_type;
|
*pmesh->content_type = content_type;
|
||||||
}
|
}
|
||||||
auto file = ReadAttrFile(elem, "file", MeshDir());
|
auto file = ReadAttrFile(elem, "file", vfs, MeshDir());
|
||||||
if (file) {
|
if (file) {
|
||||||
mjs_setString(pmesh->file, file->c_str());
|
mjs_setString(pmesh->file, file->c_str());
|
||||||
}
|
}
|
||||||
@@ -1461,7 +1461,7 @@ void mjXReader::OneMesh(XMLElement* elem, mjsMesh* pmesh) {
|
|||||||
|
|
||||||
|
|
||||||
// skin element parser
|
// skin element parser
|
||||||
void mjXReader::OneSkin(XMLElement* elem, mjsSkin* pskin) {
|
void mjXReader::OneSkin(XMLElement* elem, mjsSkin* pskin, const mjVFS* vfs) {
|
||||||
string text, name, material;
|
string text, name, material;
|
||||||
float data[4];
|
float data[4];
|
||||||
|
|
||||||
@@ -1469,7 +1469,7 @@ void mjXReader::OneSkin(XMLElement* elem, mjsSkin* pskin) {
|
|||||||
if (ReadAttrTxt(elem, "name", name)) {
|
if (ReadAttrTxt(elem, "name", name)) {
|
||||||
mjs_setString(pskin->name, name.c_str());
|
mjs_setString(pskin->name, name.c_str());
|
||||||
}
|
}
|
||||||
auto file = ReadAttrFile(elem, "file", AssetDir());
|
auto file = ReadAttrFile(elem, "file", vfs, AssetDir());
|
||||||
if (file.has_value()) {
|
if (file.has_value()) {
|
||||||
mjs_setString(pskin->file, file->c_str());
|
mjs_setString(pskin->file, file->c_str());
|
||||||
}
|
}
|
||||||
@@ -2536,7 +2536,7 @@ void mjXReader::OneComposite(XMLElement* elem, mjsBody* pbody, mjsDefault* def)
|
|||||||
|
|
||||||
|
|
||||||
// make flexcomp
|
// make flexcomp
|
||||||
void mjXReader::OneFlexcomp(XMLElement* elem, mjsBody* pbody) {
|
void mjXReader::OneFlexcomp(XMLElement* elem, mjsBody* pbody, const mjVFS* vfs) {
|
||||||
string text, material;
|
string text, material;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
@@ -2554,7 +2554,7 @@ void mjXReader::OneFlexcomp(XMLElement* elem, mjsBody* pbody) {
|
|||||||
ReadAttr(elem, "scale", 3, fcomp.scale, text);
|
ReadAttr(elem, "scale", 3, fcomp.scale, text);
|
||||||
ReadAttr(elem, "mass", 1, &fcomp.mass, text);
|
ReadAttr(elem, "mass", 1, &fcomp.mass, text);
|
||||||
ReadAttr(elem, "inertiabox", 1, &fcomp.inertiabox, 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()) {
|
if (maybe_file.has_value()) {
|
||||||
fcomp.file = std::move(maybe_file.value().Str());
|
fcomp.file = std::move(maybe_file.value().Str());
|
||||||
} else {
|
} else {
|
||||||
@@ -2690,7 +2690,7 @@ void mjXReader::OnePlugin(XMLElement* elem, mjsPlugin* plugin) {
|
|||||||
//------------------ MJCF-specific sections --------------------------------------------------------
|
//------------------ MJCF-specific sections --------------------------------------------------------
|
||||||
|
|
||||||
// default section parser
|
// default section parser
|
||||||
void mjXReader::Default(XMLElement* section, const mjsDefault* def) {
|
void mjXReader::Default(XMLElement* section, const mjsDefault* def, const mjVFS* vfs) {
|
||||||
XMLElement* elem;
|
XMLElement* elem;
|
||||||
string text, name;
|
string text, name;
|
||||||
|
|
||||||
@@ -2721,7 +2721,7 @@ void mjXReader::Default(XMLElement* section, const mjsDefault* def) {
|
|||||||
name = elem->Value();
|
name = elem->Value();
|
||||||
|
|
||||||
// read mesh
|
// read mesh
|
||||||
if (name=="mesh") OneMesh(elem, def->mesh);
|
if (name=="mesh") OneMesh(elem, def->mesh, vfs);
|
||||||
|
|
||||||
// read material
|
// read material
|
||||||
else if (name=="material") OneMaterial(elem, def->material);
|
else if (name=="material") OneMaterial(elem, def->material);
|
||||||
@@ -2775,7 +2775,7 @@ void mjXReader::Default(XMLElement* section, const mjsDefault* def) {
|
|||||||
|
|
||||||
// read default
|
// read default
|
||||||
if (name=="default") {
|
if (name=="default") {
|
||||||
Default(elem, def);
|
Default(elem, def, vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// advance
|
// advance
|
||||||
@@ -3136,7 +3136,7 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
|
|||||||
if (ReadAttrTxt(elem, "content_type", content_type)) {
|
if (ReadAttrTxt(elem, "content_type", content_type)) {
|
||||||
mjs_setString(ptex->content_type, content_type.c_str());
|
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()) {
|
if (file.has_value()) {
|
||||||
mjs_setString(ptex->file, file->c_str());
|
mjs_setString(ptex->file, file->c_str());
|
||||||
}
|
}
|
||||||
@@ -3182,7 +3182,7 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
|
|||||||
"fileup", "filedown",
|
"fileup", "filedown",
|
||||||
"filefront", "fileback"};
|
"filefront", "fileback"};
|
||||||
for (int i = 0; i < cubefiles.size(); i++) {
|
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());
|
TextureDir());
|
||||||
if (maybe_file.has_value()) {
|
if (maybe_file.has_value()) {
|
||||||
cubefiles[i] = maybe_file.value().Str();
|
cubefiles[i] = maybe_file.value().Str();
|
||||||
@@ -3204,14 +3204,14 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
|
|||||||
else if (name=="mesh") {
|
else if (name=="mesh") {
|
||||||
// create mesh and parse
|
// create mesh and parse
|
||||||
mjsMesh* pmesh = mjs_addMesh(spec, def);
|
mjsMesh* pmesh = mjs_addMesh(spec, def);
|
||||||
OneMesh(elem, pmesh);
|
OneMesh(elem, pmesh, vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// skin sub-element... deprecate ???
|
// skin sub-element... deprecate ???
|
||||||
else if (name=="skin") {
|
else if (name=="skin") {
|
||||||
// create skin and parse
|
// create skin and parse
|
||||||
mjsSkin* pskin = mjs_addSkin(spec);
|
mjsSkin* pskin = mjs_addSkin(spec);
|
||||||
OneSkin(elem, pskin);
|
OneSkin(elem, pskin, vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// hfield sub-element
|
// hfield sub-element
|
||||||
@@ -3230,7 +3230,7 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
|
|||||||
if (ReadAttrTxt(elem, "content_type", content_type)) {
|
if (ReadAttrTxt(elem, "content_type", content_type)) {
|
||||||
mjs_setString(phf->content_type, content_type.c_str());
|
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()) {
|
if (file.has_value()) {
|
||||||
mjs_setString(phf->file, file->c_str());
|
mjs_setString(phf->file, file->c_str());
|
||||||
}
|
}
|
||||||
@@ -3274,7 +3274,7 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
|
|||||||
|
|
||||||
// model sub-element
|
// model sub-element
|
||||||
else if (name=="model") {
|
else if (name=="model") {
|
||||||
auto filename = modelfiledir_ + ReadAttrFile(elem, "file").value();
|
auto filename = modelfiledir_ + ReadAttrFile(elem, "file", vfs).value();
|
||||||
|
|
||||||
// parse the child
|
// parse the child
|
||||||
std::array<char, 1024> error;
|
std::array<char, 1024> error;
|
||||||
@@ -3301,7 +3301,8 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
|
|||||||
|
|
||||||
|
|
||||||
// body/world section parser; recursive
|
// 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;
|
string text, name;
|
||||||
XMLElement* elem;
|
XMLElement* elem;
|
||||||
int n;
|
int n;
|
||||||
@@ -3427,7 +3428,7 @@ void mjXReader::Body(XMLElement* section, mjsBody* pbody, mjsFrame* frame) {
|
|||||||
// flexcomp sub-element
|
// flexcomp sub-element
|
||||||
else if (name=="flexcomp") {
|
else if (name=="flexcomp") {
|
||||||
// parse flexcomp
|
// parse flexcomp
|
||||||
OneFlexcomp(elem, pbody);
|
OneFlexcomp(elem, pbody, vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// frame sub-element
|
// frame sub-element
|
||||||
@@ -3459,7 +3460,7 @@ void mjXReader::Body(XMLElement* section, mjsBody* pbody, mjsFrame* frame) {
|
|||||||
ReadQuat(elem, "quat", pframe->quat, text);
|
ReadQuat(elem, "quat", pframe->quat, text);
|
||||||
ReadAlternative(elem, pframe->alt);
|
ReadAlternative(elem, pframe->alt);
|
||||||
|
|
||||||
Body(elem, pbody, pframe);
|
Body(elem, pbody, pframe, vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// replicate sub-element
|
// replicate sub-element
|
||||||
@@ -3518,7 +3519,7 @@ void mjXReader::Body(XMLElement* section, mjsBody* pbody, mjsFrame* frame) {
|
|||||||
UpdateString(suffix, count, i);
|
UpdateString(suffix, count, i);
|
||||||
|
|
||||||
// process subtree
|
// process subtree
|
||||||
Body(elem, subtree, pframe);
|
Body(elem, subtree, pframe, vfs);
|
||||||
|
|
||||||
// attach to parent
|
// attach to parent
|
||||||
if (mjs_attachFrame(pbody, pframe, /*prefix=*/"", suffix.c_str()) != 0) {
|
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);
|
mjs_setFrame(pchild->element, frame);
|
||||||
|
|
||||||
// make recursive call
|
// make recursive call
|
||||||
Body(elem, pchild, nullptr);
|
Body(elem, pchild, nullptr, vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// attachment
|
// attachment
|
||||||
@@ -3695,7 +3696,7 @@ void mjXReader::Equality(XMLElement* section) {
|
|||||||
|
|
||||||
|
|
||||||
// deformable section parser
|
// deformable section parser
|
||||||
void mjXReader::Deformable(XMLElement* section) {
|
void mjXReader::Deformable(XMLElement* section, const mjVFS* vfs) {
|
||||||
string name;
|
string name;
|
||||||
XMLElement* elem;
|
XMLElement* elem;
|
||||||
|
|
||||||
@@ -3722,7 +3723,7 @@ void mjXReader::Deformable(XMLElement* section) {
|
|||||||
else if (name=="skin") {
|
else if (name=="skin") {
|
||||||
// create skin and parse
|
// create skin and parse
|
||||||
mjsSkin* pskin = mjs_addSkin(spec);
|
mjsSkin* pskin = mjs_addSkin(spec);
|
||||||
OneSkin(elem, pskin);
|
OneSkin(elem, pskin, vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// advance to next element
|
// advance to next element
|
||||||
|
|||||||
@@ -49,16 +49,17 @@ class mjXReader : public mjXBase {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// XML section specific to MJCF
|
// 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 Extension(tinyxml2::XMLElement* section); // extension section
|
||||||
void Custom(tinyxml2::XMLElement* section); // custom section
|
void Custom(tinyxml2::XMLElement* section); // custom section
|
||||||
void Visual(tinyxml2::XMLElement* section); // visual section
|
void Visual(tinyxml2::XMLElement* section); // visual section
|
||||||
void Statistic(tinyxml2::XMLElement* section); // statistic section
|
void Statistic(tinyxml2::XMLElement* section); // statistic section
|
||||||
void Asset(tinyxml2::XMLElement* section, const mjVFS* vfs); // asset section
|
void Asset(tinyxml2::XMLElement* section, const mjVFS* vfs); // asset section
|
||||||
void Body(tinyxml2::XMLElement* section, mjsBody* pbody,
|
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 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 Equality(tinyxml2::XMLElement* section); // equality section
|
||||||
void Tendon(tinyxml2::XMLElement* section); // tendon section
|
void Tendon(tinyxml2::XMLElement* section); // tendon section
|
||||||
void Actuator(tinyxml2::XMLElement* section); // actuator 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
|
// single element parsers, used in defaults and main body
|
||||||
void OneFlex(tinyxml2::XMLElement* elem, mjsFlex* pflex);
|
void OneFlex(tinyxml2::XMLElement* elem, mjsFlex* pflex);
|
||||||
void OneMesh(tinyxml2::XMLElement* elem, mjsMesh* pmesh);
|
void OneMesh(tinyxml2::XMLElement* elem, mjsMesh* pmesh, const mjVFS* vfs);
|
||||||
void OneSkin(tinyxml2::XMLElement* elem, mjsSkin* pskin);
|
void OneSkin(tinyxml2::XMLElement* elem, mjsSkin* pskin, const mjVFS* vfs);
|
||||||
void OneMaterial(tinyxml2::XMLElement* elem, mjsMaterial* pmaterial);
|
void OneMaterial(tinyxml2::XMLElement* elem, mjsMaterial* pmaterial);
|
||||||
void OneJoint(tinyxml2::XMLElement* elem, mjsJoint* pjoint);
|
void OneJoint(tinyxml2::XMLElement* elem, mjsJoint* pjoint);
|
||||||
void OneGeom(tinyxml2::XMLElement* elem, mjsGeom* pgeom);
|
void OneGeom(tinyxml2::XMLElement* elem, mjsGeom* pgeom);
|
||||||
@@ -80,7 +81,7 @@ class mjXReader : public mjXBase {
|
|||||||
void OneTendon(tinyxml2::XMLElement* elem, mjsTendon* ptendon);
|
void OneTendon(tinyxml2::XMLElement* elem, mjsTendon* ptendon);
|
||||||
void OneActuator(tinyxml2::XMLElement* elem, mjsActuator* pactuator);
|
void OneActuator(tinyxml2::XMLElement* elem, mjsActuator* pactuator);
|
||||||
void OneComposite(tinyxml2::XMLElement* elem, mjsBody* pbody, mjsDefault* def);
|
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);
|
void OnePlugin(tinyxml2::XMLElement* elem, mjsPlugin* plugin);
|
||||||
|
|
||||||
mjXSchema schema; // schema used for validation
|
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,
|
FilePath ResolveFilePath(XMLElement* e, const FilePath& filename,
|
||||||
const FilePath& dir) {
|
const FilePath& dir, const mjVFS* vfs) {
|
||||||
std::string path = "";
|
std::string path = "";
|
||||||
if (filename.IsAbs()) {
|
if (filename.IsAbs()) {
|
||||||
return filename;
|
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
|
// TODO(kylebayes): We first look in the base model directory for files to
|
||||||
// remain backwards compatible.
|
// remain backwards compatible.
|
||||||
FilePath fullname = dir + filename;
|
FilePath fullname = dir + filename;
|
||||||
mjResource *resource = mju_openResource(fullname.c_str(), nullptr,
|
mjResource *resource = mju_openResource("", fullname.c_str(), vfs,
|
||||||
nullptr, 0);
|
nullptr, 0);
|
||||||
if (resource != nullptr) {
|
if (resource != nullptr) {
|
||||||
mju_closeResource(resource);
|
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
|
// if attribute is present, return attribute as a filename
|
||||||
std::optional<FilePath>
|
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) {
|
const FilePath& dir, bool required) {
|
||||||
auto maybe_str = ReadAttrStr(elem, attr, required);
|
auto maybe_str = ReadAttrStr(elem, attr, required);
|
||||||
if (!maybe_str.has_value()) {
|
if (!maybe_str.has_value()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
FilePath filename(maybe_str.value());
|
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
|
// if attribute is present, return numerical value of attribute
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include "tinyxml2.h"
|
#include "tinyxml2.h"
|
||||||
|
|
||||||
|
#include <mujoco/mujoco.h>
|
||||||
#include "user/user_util.h"
|
#include "user/user_util.h"
|
||||||
|
|
||||||
// error string copy
|
// error string copy
|
||||||
@@ -114,6 +115,7 @@ class mjXUtil {
|
|||||||
// if attribute is present, return attribute as a filename
|
// if attribute is present, return attribute as a filename
|
||||||
static std::optional<mujoco::user::FilePath>
|
static std::optional<mujoco::user::FilePath>
|
||||||
ReadAttrFile(tinyxml2::XMLElement* elem, const char* attr,
|
ReadAttrFile(tinyxml2::XMLElement* elem, const char* attr,
|
||||||
|
const mjVFS* vfs,
|
||||||
const mujoco::user::FilePath& dir = mujoco::user::FilePath(),
|
const mujoco::user::FilePath& dir = mujoco::user::FilePath(),
|
||||||
bool required = false);
|
bool required = false);
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ void CacheText(mjCCache& cache, const std::string& model,
|
|||||||
mjVFS vfs;
|
mjVFS vfs;
|
||||||
mj_defaultVFS(&vfs);
|
mj_defaultVFS(&vfs);
|
||||||
mj_addBufferVFS(&vfs, name.c_str(), text.data(), text.size());
|
mj_addBufferVFS(&vfs, name.c_str(), text.data(), text.size());
|
||||||
mjResource* resource = mju_openResource(name.c_str(), &vfs, nullptr, 0);
|
mjResource* resource = mju_openResource("", name.c_str(), &vfs, nullptr, 0);
|
||||||
std::shared_ptr<const void> data(&text, +[](const void* data) {});
|
std::shared_ptr<const void> data(&text, +[](const void* data) {});
|
||||||
cache.Insert(model, resource, data, text.size());
|
cache.Insert(model, resource, data, text.size());
|
||||||
mju_closeResource(resource);
|
mju_closeResource(resource);
|
||||||
@@ -61,7 +61,7 @@ GetCachedText(mjCCache& cache, const std::string& model,
|
|||||||
mjVFS vfs;
|
mjVFS vfs;
|
||||||
mj_defaultVFS(&vfs);
|
mj_defaultVFS(&vfs);
|
||||||
mj_addBufferVFS(&vfs, name.c_str(), text.data(), std::strlen(text.c_str()));
|
mj_addBufferVFS(&vfs, name.c_str(), text.data(), std::strlen(text.c_str()));
|
||||||
mjResource* resource = mju_openResource(name.c_str(), &vfs, nullptr, 0);
|
mjResource* resource = mju_openResource("", name.c_str(), &vfs, nullptr, 0);
|
||||||
bool inserted = cache.PopulateData(resource,
|
bool inserted = cache.PopulateData(resource,
|
||||||
[&cached_text](const void* data) {
|
[&cached_text](const void* data) {
|
||||||
cached_text = *(static_cast<const std::string*>(data));
|
cached_text = *(static_cast<const std::string*>(data));
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ TEST_F(ResourceTest, GeneralTest) {
|
|||||||
EXPECT_GT(i, 0);
|
EXPECT_GT(i, 0);
|
||||||
|
|
||||||
// open resource
|
// open resource
|
||||||
mjResource* resource = mju_openResource("str:file", nullptr, nullptr, 0);
|
mjResource* resource = mju_openResource("", "str:file", nullptr, nullptr, 0);
|
||||||
ASSERT_THAT(resource, NotNull());
|
ASSERT_THAT(resource, NotNull());
|
||||||
|
|
||||||
const char* buffer = NULL;
|
const char* buffer = NULL;
|
||||||
@@ -256,7 +256,7 @@ TEST_F(ResourceTest, GeneralFailureTest) {
|
|||||||
static std::array<char, 1024> error;
|
static std::array<char, 1024> error;
|
||||||
|
|
||||||
// open resource
|
// open resource
|
||||||
mjResource* resource = mju_openResource("str:notfound", nullptr,
|
mjResource* resource = mju_openResource("", "str:notfound", nullptr,
|
||||||
error.data(), error.size());
|
error.data(), error.size());
|
||||||
ASSERT_THAT(resource, IsNull());
|
ASSERT_THAT(resource, IsNull());
|
||||||
|
|
||||||
@@ -281,7 +281,7 @@ TEST_F(ResourceTest, NameWithValidPrefix) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// open resource
|
// open resource
|
||||||
mjResource* resource = mju_openResource("nop:found", nullptr, nullptr, 0);
|
mjResource* resource = mju_openResource("", "nop:found", nullptr, nullptr, 0);
|
||||||
ASSERT_THAT(resource, NotNull());
|
ASSERT_THAT(resource, NotNull());
|
||||||
mju_closeResource(resource);
|
mju_closeResource(resource);
|
||||||
}
|
}
|
||||||
@@ -304,7 +304,7 @@ TEST_F(ResourceTest, NameWithUpperCasePrefix) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// open resource
|
// open resource
|
||||||
mjResource* resource = mju_openResource("NOP:found", nullptr, nullptr, 0);
|
mjResource* resource = mju_openResource("", "NOP:found", nullptr, nullptr, 0);
|
||||||
ASSERT_THAT(resource, NotNull());
|
ASSERT_THAT(resource, NotNull());
|
||||||
mju_closeResource(resource);
|
mju_closeResource(resource);
|
||||||
}
|
}
|
||||||
@@ -327,7 +327,7 @@ TEST_F(ResourceTest, NameWithInvalidPrefix) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// open resource
|
// open resource
|
||||||
mjResource* resource = mju_openResource("nopfound", nullptr, nullptr, 0);
|
mjResource* resource = mju_openResource("", "nopfound", nullptr, nullptr, 0);
|
||||||
ASSERT_THAT(resource, IsNull());
|
ASSERT_THAT(resource, IsNull());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,7 +338,7 @@ TEST_F(ResourceTest, OSFilesystemTimestamps) {
|
|||||||
const char* const file = "engine/testdata/collision_box/boxbox_deep.xml";
|
const char* const file = "engine/testdata/collision_box/boxbox_deep.xml";
|
||||||
const std::string xml_path = GetTestDataFilePath(file);
|
const std::string xml_path = GetTestDataFilePath(file);
|
||||||
|
|
||||||
mjResource* resource = mju_openResource(xml_path.c_str(), nullptr,
|
mjResource* resource = mju_openResource("", xml_path.c_str(), nullptr,
|
||||||
nullptr, 0);
|
nullptr, 0);
|
||||||
mju_decodeBase64((uint8_t*) &t, resource->timestamp);
|
mju_decodeBase64((uint8_t*) &t, resource->timestamp);
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ using ::testing::NotNull;
|
|||||||
using UserVfsTest = MujocoTest;
|
using UserVfsTest = MujocoTest;
|
||||||
|
|
||||||
static bool HasFile(const mjVFS* vfs, const std::string& filename) {
|
static bool HasFile(const mjVFS* vfs, const std::string& filename) {
|
||||||
mjResource* resource = mju_openResource(filename.c_str(), vfs, nullptr, 0);
|
mjResource* resource = mju_openResource("", filename.c_str(), vfs, nullptr, 0);
|
||||||
bool result = resource != nullptr;
|
bool result = resource != nullptr;
|
||||||
mju_closeResource(resource);
|
mju_closeResource(resource);
|
||||||
return result;
|
return result;
|
||||||
@@ -196,14 +196,13 @@ TEST_F(UserVfsTest, AddBufferRepeat) {
|
|||||||
mj_deleteVFS(&vfs);
|
mj_deleteVFS(&vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(UserVfsTest, BufferStripPath) {
|
TEST_F(UserVfsTest, BufferPath) {
|
||||||
mjVFS vfs;
|
mjVFS vfs;
|
||||||
mj_defaultVFS(&vfs);
|
mj_defaultVFS(&vfs);
|
||||||
std::string buffer = "<mujoco/>";
|
std::string buffer = "<mujoco/>";
|
||||||
const void* ptr = static_cast<const void*>(buffer.c_str());
|
const void* ptr = static_cast<const void*>(buffer.c_str());
|
||||||
mj_addBufferVFS(&vfs, "dir/model", ptr, buffer.size());
|
mj_addBufferVFS(&vfs, "dir/model", ptr, buffer.size());
|
||||||
EXPECT_TRUE(HasFile(&vfs, "MODEL"));
|
EXPECT_TRUE(HasFile(&vfs, "files/../dir/model"));
|
||||||
EXPECT_TRUE(HasFile(&vfs, "dir\\model"));
|
|
||||||
mj_deleteVFS(&vfs);
|
mj_deleteVFS(&vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +221,7 @@ TEST_F(UserVfsTest, Timestamps) {
|
|||||||
mj_defaultVFS(&vfs);
|
mj_defaultVFS(&vfs);
|
||||||
mj_addBufferVFS(&vfs, "cube.obj", cube, sizeof(cube));
|
mj_addBufferVFS(&vfs, "cube.obj", cube, sizeof(cube));
|
||||||
|
|
||||||
mjResource* resource = mju_openResource("cube.obj", &vfs, nullptr, 0);
|
mjResource* resource = mju_openResource("", "cube.obj", &vfs, nullptr, 0);
|
||||||
|
|
||||||
// same timestamps
|
// same timestamps
|
||||||
EXPECT_EQ(mju_isModifiedResource(resource, resource->timestamp), 0);
|
EXPECT_EQ(mju_isModifiedResource(resource, resource->timestamp), 0);
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
// Tests for xml/xml_native_reader.cc.
|
// Tests for xml/xml_native_reader.cc.
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstring>
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -613,6 +612,18 @@ static constexpr unsigned char kTinyPng[] = {
|
|||||||
0x82
|
0x82
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// mesh OBJ file of a cube
|
||||||
|
static constexpr char kTinyObj[] = R"(
|
||||||
|
v -1 -1 1
|
||||||
|
v 1 -1 1
|
||||||
|
v -1 1 1
|
||||||
|
v 1 1 1
|
||||||
|
v -1 1 -1
|
||||||
|
v 1 1 -1
|
||||||
|
v -1 -1 -1
|
||||||
|
v 1 -1 -1)";
|
||||||
|
|
||||||
|
|
||||||
TEST_F(XMLReaderTest, IncludeTest) {
|
TEST_F(XMLReaderTest, IncludeTest) {
|
||||||
static constexpr char xml[] = R"(
|
static constexpr char xml[] = R"(
|
||||||
<mujoco>
|
<mujoco>
|
||||||
@@ -726,24 +737,22 @@ TEST_F(XMLReaderTest, IncludePathTest) {
|
|||||||
<geom name="another_box" type="box" size="2 2 2"/>
|
<geom name="another_box" type="box" size="2 2 2"/>
|
||||||
</mujoco>)";
|
</mujoco>)";
|
||||||
|
|
||||||
MockFilesystem fs("IncludePathTest");
|
mjVFS vfs;
|
||||||
fs.AddFile("model.xml", (const unsigned char*) xml, sizeof(xml));
|
mj_defaultVFS(&vfs);
|
||||||
std::string modelpath = fs.FullPath("model.xml");
|
mj_addBufferVFS(&vfs, "model.xml", xml, sizeof(xml));
|
||||||
|
mj_addBufferVFS(&vfs, "submodels/model1.xml", xml1, sizeof(xml1));
|
||||||
fs.ChangeDirectory("submodels/");
|
mj_addBufferVFS(&vfs, "submodels/model2.xml", xml2, sizeof(xml2));
|
||||||
fs.AddFile("model1.xml", (const unsigned char*) xml1, sizeof(xml1));
|
mj_addBufferVFS(&vfs, "submodels/subsubmodels/model3.xml", xml3,
|
||||||
fs.AddFile("model2.xml", (const unsigned char*) xml2, sizeof(xml2));
|
sizeof(xml3));
|
||||||
fs.AddFile("subsubmodels/model3.xml", (const unsigned char*) xml3,
|
|
||||||
sizeof(xml3));
|
|
||||||
fs.ChangeDirectory("/");
|
|
||||||
|
|
||||||
std::array<char, 1024> error;
|
std::array<char, 1024> error;
|
||||||
mjModel* model = mj_loadXML(modelpath.c_str(), nullptr, error.data(),
|
mjModel* model = mj_loadXML("model.xml", &vfs, error.data(),
|
||||||
error.size());
|
error.size());
|
||||||
ASSERT_THAT(model, NotNull()) << error.data();
|
ASSERT_THAT(model, NotNull()) << error.data();
|
||||||
EXPECT_EQ(mj_name2id(model, mjOBJ_GEOM, "ball"), 2);
|
EXPECT_EQ(mj_name2id(model, mjOBJ_GEOM, "ball"), 2);
|
||||||
EXPECT_EQ(mj_name2id(model, mjOBJ_GEOM, "another_box"), 3);
|
EXPECT_EQ(mj_name2id(model, mjOBJ_GEOM, "another_box"), 3);
|
||||||
mj_deleteModel(model);
|
mj_deleteModel(model);
|
||||||
|
mj_deleteVFS(&vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(XMLReaderTest, FallbackIncludePathTest) {
|
TEST_F(XMLReaderTest, FallbackIncludePathTest) {
|
||||||
@@ -772,22 +781,21 @@ TEST_F(XMLReaderTest, FallbackIncludePathTest) {
|
|||||||
<geom name="another_box" type="box" size="2 2 2"/>
|
<geom name="another_box" type="box" size="2 2 2"/>
|
||||||
</mujoco>)";
|
</mujoco>)";
|
||||||
|
|
||||||
MockFilesystem fs("FallbackIncludePathTest");
|
mjVFS vfs;
|
||||||
fs.AddFile("model.xml", (const unsigned char*) xml, sizeof(xml));
|
mj_defaultVFS(&vfs);
|
||||||
std::string modelpath = fs.FullPath("model.xml");
|
mj_addBufferVFS(&vfs, "model.xml", xml, sizeof(xml));
|
||||||
|
mj_addBufferVFS(&vfs, "model1.xml", xml1, sizeof(xml1));
|
||||||
fs.AddFile("model1.xml", (const unsigned char*) xml1, sizeof(xml1));
|
mj_addBufferVFS(&vfs, "submodels/model2.xml", xml2, sizeof(xml2));
|
||||||
fs.AddFile("submodels/model2.xml", (const unsigned char*) xml2, sizeof(xml2));
|
mj_addBufferVFS(&vfs, "subsubmodels/model3.xml", xml3, sizeof(xml3));
|
||||||
fs.AddFile("subsubmodels/model3.xml", (const unsigned char*) xml3,
|
|
||||||
sizeof(xml3));
|
|
||||||
|
|
||||||
std::array<char, 1024> error;
|
std::array<char, 1024> error;
|
||||||
mjModel* model = mj_loadXML(modelpath.c_str(), nullptr,
|
mjModel* model = mj_loadXML("model.xml", &vfs,
|
||||||
error.data(), error.size());
|
error.data(), error.size());
|
||||||
ASSERT_THAT(model, NotNull()) << error.data();
|
ASSERT_THAT(model, NotNull()) << error.data();
|
||||||
EXPECT_EQ(mj_name2id(model, mjOBJ_GEOM, "ball"), 2);
|
EXPECT_EQ(mj_name2id(model, mjOBJ_GEOM, "ball"), 2);
|
||||||
EXPECT_EQ(mj_name2id(model, mjOBJ_GEOM, "another_box"), 3);
|
EXPECT_EQ(mj_name2id(model, mjOBJ_GEOM, "another_box"), 3);
|
||||||
mj_deleteModel(model);
|
mj_deleteModel(model);
|
||||||
|
mj_deleteVFS(&vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(XMLReaderTest, MaterialTextureTest) {
|
TEST_F(XMLReaderTest, MaterialTextureTest) {
|
||||||
@@ -809,14 +817,14 @@ TEST_F(XMLReaderTest, MaterialTextureTest) {
|
|||||||
</mujoco>
|
</mujoco>
|
||||||
)";
|
)";
|
||||||
|
|
||||||
MockFilesystem fs("MaterialTextureTest");
|
mjVFS vfs;
|
||||||
fs.AddFile("tiny0.png", kTinyPng, sizeof(kTinyPng));
|
mj_defaultVFS(&vfs);
|
||||||
fs.AddFile("tiny1.png", kTinyPng, sizeof(kTinyPng));
|
mj_addBufferVFS(&vfs, "tiny0.png", kTinyPng, sizeof(kTinyPng));
|
||||||
fs.AddFile("model.xml", (const unsigned char*) xml, sizeof(xml));
|
mj_addBufferVFS(&vfs, "tiny1.png", kTinyPng, sizeof(kTinyPng));
|
||||||
std::string modelpath = fs.FullPath("model.xml");
|
mj_addBufferVFS(&vfs, "model.xml", xml, sizeof(xml));
|
||||||
|
|
||||||
char error[1024];
|
char error[1024];
|
||||||
mjModel* model = mj_loadXML(modelpath.c_str(), nullptr, error, 1024);
|
mjModel* model = mj_loadXML("model.xml", &vfs, error, 1024);
|
||||||
|
|
||||||
EXPECT_THAT(model, NotNull()) << error;
|
EXPECT_THAT(model, NotNull()) << error;
|
||||||
EXPECT_EQ(model->mat_texid[mjTEXROLE_RGB], 1);
|
EXPECT_EQ(model->mat_texid[mjTEXROLE_RGB], 1);
|
||||||
@@ -825,6 +833,7 @@ TEST_F(XMLReaderTest, MaterialTextureTest) {
|
|||||||
EXPECT_EQ(model->mat_texid[mjTEXROLE_OCCLUSION], 0);
|
EXPECT_EQ(model->mat_texid[mjTEXROLE_OCCLUSION], 0);
|
||||||
|
|
||||||
mj_deleteModel(model);
|
mj_deleteModel(model);
|
||||||
|
mj_deleteVFS(&vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(XMLReaderTest, LegacyMaterialTextureTest) {
|
TEST_F(XMLReaderTest, LegacyMaterialTextureTest) {
|
||||||
@@ -841,19 +850,20 @@ TEST_F(XMLReaderTest, LegacyMaterialTextureTest) {
|
|||||||
</mujoco>
|
</mujoco>
|
||||||
)";
|
)";
|
||||||
|
|
||||||
MockFilesystem fs("LegacyMaterialTextureTest");
|
mjVFS vfs;
|
||||||
fs.AddFile("tiny0.png", kTinyPng, sizeof(kTinyPng));
|
mj_defaultVFS(&vfs);
|
||||||
fs.AddFile("tiny1.png", kTinyPng, sizeof(kTinyPng));
|
mj_addBufferVFS(&vfs, "tiny0.png", kTinyPng, sizeof(kTinyPng));
|
||||||
fs.AddFile("model.xml", (const unsigned char*) xml, sizeof(xml));
|
mj_addBufferVFS(&vfs, "tiny1.png", kTinyPng, sizeof(kTinyPng));
|
||||||
std::string modelpath = fs.FullPath("model.xml");
|
mj_addBufferVFS(&vfs, "model.xml", xml, sizeof(xml));
|
||||||
|
|
||||||
char error[1024];
|
char error[1024];
|
||||||
mjModel* model = mj_loadXML(modelpath.c_str(), nullptr, error, 1024);
|
mjModel* model = mj_loadXML("model.xml", &vfs, error, 1024);
|
||||||
|
|
||||||
EXPECT_THAT(model, NotNull()) << error;
|
EXPECT_THAT(model, NotNull()) << error;
|
||||||
EXPECT_EQ(model->mat_texid[mjTEXROLE_RGB], 1);
|
EXPECT_EQ(model->mat_texid[mjTEXROLE_RGB], 1);
|
||||||
|
|
||||||
mj_deleteModel(model);
|
mj_deleteModel(model);
|
||||||
|
mj_deleteVFS(&vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(XMLReaderTest, MaterialTextureFailTest) {
|
TEST_F(XMLReaderTest, MaterialTextureFailTest) {
|
||||||
@@ -873,12 +883,6 @@ TEST_F(XMLReaderTest, MaterialTextureFailTest) {
|
|||||||
</mujoco>
|
</mujoco>
|
||||||
)";
|
)";
|
||||||
|
|
||||||
MockFilesystem fs("MaterialTextureFailTest");
|
|
||||||
fs.AddFile("tiny0.png", kTinyPng, sizeof(kTinyPng));
|
|
||||||
fs.AddFile("tiny1.png", kTinyPng, sizeof(kTinyPng));
|
|
||||||
fs.AddFile("model.xml", (const unsigned char*) xml, sizeof(xml));
|
|
||||||
std::string modelpath = fs.FullPath("model.xml");
|
|
||||||
|
|
||||||
std::array<char, 1024> error;
|
std::array<char, 1024> error;
|
||||||
mjModel* m = LoadModelFromString(xml, error.data(), error.size());
|
mjModel* m = LoadModelFromString(xml, error.data(), error.size());
|
||||||
EXPECT_THAT(m, IsNull());
|
EXPECT_THAT(m, IsNull());
|
||||||
@@ -907,28 +911,32 @@ TEST_F(XMLReaderTest, IncludeAssetsTest) {
|
|||||||
static constexpr char subassets[] = R"(
|
static constexpr char subassets[] = R"(
|
||||||
<mujoco>
|
<mujoco>
|
||||||
<texture file="subtiny.png" type="2d"/>
|
<texture file="subtiny.png" type="2d"/>
|
||||||
|
<mesh name="cube" file="cube.obj"/>
|
||||||
<material name="submaterial" texture="subtiny"/>
|
<material name="submaterial" texture="subtiny"/>
|
||||||
</mujoco>
|
</mujoco>
|
||||||
)";
|
)";
|
||||||
|
|
||||||
MockFilesystem fs("IncludeAssetsTest");
|
mjVFS vfs;
|
||||||
fs.AddFile("assets/tiny.png", kTinyPng, sizeof(kTinyPng));
|
mj_defaultVFS(&vfs);
|
||||||
fs.AddFile("assets/subassets/subtiny.png", kTinyPng, sizeof(kTinyPng));
|
mj_addBufferVFS(&vfs, "assets/tiny.png", kTinyPng, sizeof(kTinyPng));
|
||||||
fs.AddFile("assets/assets.xml", (const unsigned char*) assets,
|
mj_addBufferVFS(&vfs, "assets/subassets/subtiny.png", kTinyPng,
|
||||||
sizeof(assets));
|
sizeof(kTinyPng));
|
||||||
fs.AddFile("assets/subassets/assets.xml", (const unsigned char*) subassets,
|
mj_addBufferVFS(&vfs, "assets/subassets/cube.obj", kTinyObj,
|
||||||
sizeof(subassets));
|
sizeof(kTinyObj));
|
||||||
fs.AddFile("model.xml", (const unsigned char*) xml, sizeof(xml));
|
mj_addBufferVFS(&vfs, "assets/assets.xml", assets, sizeof(assets));
|
||||||
std::string modelpath = fs.FullPath("model.xml");
|
mj_addBufferVFS(&vfs, "assets/subassets/assets.xml", subassets,
|
||||||
|
sizeof(subassets));
|
||||||
|
mj_addBufferVFS(&vfs, "model.xml", xml, sizeof(xml));
|
||||||
|
|
||||||
// loading the file should be successful
|
// loading the file should be successful
|
||||||
std::array<char, 1024> error;
|
std::array<char, 1024> error;
|
||||||
mjModel* model = mj_loadXML(modelpath.c_str(), nullptr, error.data(),
|
mjModel* model = mj_loadXML("model.xml", &vfs, error.data(),
|
||||||
error.size());
|
error.size());
|
||||||
|
|
||||||
ASSERT_THAT(model, NotNull()) << error.data();
|
ASSERT_THAT(model, NotNull()) << error.data();
|
||||||
|
|
||||||
mj_deleteModel(model);
|
mj_deleteModel(model);
|
||||||
|
mj_deleteVFS(&vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(XMLReaderTest, FallbackIncludeAssetsTest) {
|
TEST_F(XMLReaderTest, FallbackIncludeAssetsTest) {
|
||||||
@@ -956,26 +964,26 @@ TEST_F(XMLReaderTest, FallbackIncludeAssetsTest) {
|
|||||||
</mujoco>
|
</mujoco>
|
||||||
)";
|
)";
|
||||||
|
|
||||||
MockFilesystem fs("FallbackIncludeAssetsTest");
|
mjVFS vfs;
|
||||||
fs.AddFile("assets/tiny.png", kTinyPng, sizeof(kTinyPng));
|
mj_defaultVFS(&vfs);
|
||||||
|
mj_addBufferVFS(&vfs, "assets/tiny.png", kTinyPng, sizeof(kTinyPng));
|
||||||
|
|
||||||
// need to fallback for backwards compatibility
|
// need to fallback for backwards compatibility
|
||||||
fs.AddFile("subtiny.png", kTinyPng, sizeof(kTinyPng));
|
mj_addBufferVFS(&vfs, "subtiny.png", kTinyPng, sizeof(kTinyPng));
|
||||||
|
|
||||||
fs.AddFile("assets/assets.xml", (const unsigned char*) assets,
|
mj_addBufferVFS(&vfs, "assets/assets.xml", assets, sizeof(assets));
|
||||||
sizeof(assets));
|
mj_addBufferVFS(&vfs, "assets/subassets/assets.xml", subassets,
|
||||||
fs.AddFile("assets/subassets/assets.xml", (const unsigned char*) subassets,
|
sizeof(subassets));
|
||||||
sizeof(subassets));
|
mj_addBufferVFS(&vfs, "model.xml", xml, sizeof(xml));
|
||||||
fs.AddFile("model.xml", (const unsigned char*) xml, sizeof(xml));
|
|
||||||
std::string modelpath = fs.FullPath("model.xml");
|
|
||||||
|
|
||||||
// loading the file should be successful
|
// loading the file should be successful
|
||||||
std::array<char, 1024> error;
|
std::array<char, 1024> error;
|
||||||
mjModel* model = mj_loadXML(modelpath.c_str(), nullptr,
|
mjModel* model = mj_loadXML("model.xml", &vfs,
|
||||||
error.data(), error.size());
|
error.data(), error.size());
|
||||||
ASSERT_THAT(model, NotNull()) << error.data();
|
ASSERT_THAT(model, NotNull()) << error.data();
|
||||||
|
|
||||||
mj_deleteModel(model);
|
mj_deleteModel(model);
|
||||||
|
mj_deleteVFS(&vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(XMLReaderTest, IncludeAbsoluteTest) {
|
TEST_F(XMLReaderTest, IncludeAbsoluteTest) {
|
||||||
|
|||||||
Reference in New Issue
Block a user