Store model paths in assets.

This enables to preserve the paths of the child spec during `attach`.

PiperOrigin-RevId: 673349237
Change-Id: Ibf075c7ec037ff5d540bc60d6e77c12f60269791
This commit is contained in:
Alessio Quaglino
2024-09-11 05:19:08 -07:00
committed by Copybara-Service
parent cf721def3a
commit 567793c26f
5 changed files with 136 additions and 12 deletions
+2
View File
@@ -72,6 +72,8 @@ Bug fixes
integrators, wrong derivatives would be computed. integrators, wrong derivatives would be computed.
- Fixed a bug in tendon wrapping around spheres. Before this fix, tendons that wrapped around spheres with an - Fixed a bug in tendon wrapping around spheres. Before this fix, tendons that wrapped around spheres with an
externally-placed :ref:`sidesite<spatial-geom-sidesite>` could jump inside the sphere instead of wrapping around it. externally-placed :ref:`sidesite<spatial-geom-sidesite>` could jump inside the sphere instead of wrapping around it.
- Fixed a bug that caused :at:`meshdir` and :at:`texturedir` to be overwritten during model
:ref:`attachment<meAttachment>.
Python bindings Python bindings
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
+31 -4
View File
@@ -72,6 +72,7 @@ extern "C" {
namespace { namespace {
using mujoco::user::VectorToString; using mujoco::user::VectorToString;
using mujoco::user::FilePath;
} // namespace } // namespace
// compute triangle area, surface normal, center // compute triangle area, surface normal, center
@@ -218,6 +219,14 @@ void mjCMesh::PointToLocal() {
void mjCMesh::NameSpace(const mjCModel* m) {
mjCBase::NameSpace(m);
modelfiledir_ = FilePath(m->spec_modelfiledir_);
meshdir_ = FilePath(m->spec_meshdir_);
}
void mjCMesh::CopyFromSpec() { void mjCMesh::CopyFromSpec() {
*static_cast<mjsMesh*>(this) = spec; *static_cast<mjsMesh*>(this) = spec;
file_ = spec_file_; file_ = spec_file_;
@@ -388,6 +397,14 @@ void mjCMesh::Compile(const mjVFS* vfs) {
facenormal_.clear(); facenormal_.clear();
facetexcoord_.clear(); facetexcoord_.clear();
// copy paths from model if not already defined
if (modelfiledir_.empty()) {
modelfiledir_ = FilePath(model->modelfiledir_);
}
if (meshdir_.empty()) {
meshdir_ = FilePath(model->meshdir_);
}
// remove path from file if necessary // remove path from file if necessary
if (model->strippath) { if (model->strippath) {
file_ = mjuu_strippath(file_); file_ = mjuu_strippath(file_);
@@ -402,8 +419,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->meshdir_, file_); FilePath filename = meshdir_ + FilePath(file_);
resource = LoadResource(model->modelfiledir_, filename, vfs); resource = LoadResource(modelfiledir_.Str(), filename.Str(), vfs);
// try loading from cache // try loading from cache
if (cache != nullptr && LoadCachedMesh(cache, resource)) { if (cache != nullptr && LoadCachedMesh(cache, resource)) {
@@ -2016,6 +2033,8 @@ void mjCSkin::NameSpace(const mjCModel* m) {
for (auto& name : spec_bodyname_) { for (auto& name : spec_bodyname_) {
name = m->prefix + name + m->suffix; name = m->prefix + name + m->suffix;
} }
modelfiledir_ = FilePath(m->spec_modelfiledir_);
meshdir_ = FilePath(m->spec_meshdir_);
} }
@@ -2097,8 +2116,16 @@ 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->meshdir_, file_); // copy paths from model if not already defined
mjResource* resource = LoadResource(model->modelfiledir_, filename, vfs); if (modelfiledir_.empty()) {
modelfiledir_ = FilePath(model->modelfiledir_);
}
if (meshdir_.empty()) {
meshdir_ = FilePath(model->meshdir_);
}
FilePath filename = meshdir_ + FilePath(file_);
mjResource* resource = LoadResource(modelfiledir_.Str(), filename.Str(), vfs);
try { try {
LoadSKN(resource); LoadSKN(resource);
+41 -8
View File
@@ -50,6 +50,7 @@
namespace { namespace {
namespace mju = ::mujoco::util; namespace mju = ::mujoco::util;
using mujoco::user::FilePath;
class PNGImage { class PNGImage {
public: public:
@@ -3402,6 +3403,14 @@ void mjCHField::CopyFromSpec() {
void mjCHField::NameSpace(const mjCModel* m) {
mjCBase::NameSpace(m);
modelfiledir_ = FilePath(m->spec_modelfiledir_);
meshdir_ = FilePath(m->spec_meshdir_);
}
// destructor // destructor
mjCHField::~mjCHField() { mjCHField::~mjCHField() {
data.clear(); data.clear();
@@ -3514,8 +3523,16 @@ 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->meshdir_, file_); // copy paths from model if not already defined
mjResource* resource = LoadResource(model->modelfiledir_, filename, vfs); if (modelfiledir_.empty()) {
modelfiledir_ = FilePath(model->modelfiledir_);
}
if (meshdir_.empty()) {
meshdir_ = FilePath(model->meshdir_);
}
FilePath filename = meshdir_ + FilePath(file_);
mjResource* resource = LoadResource(modelfiledir_.Str(), filename.Str(), vfs);
try { try {
if (asset_type == "image/png") { if (asset_type == "image/png") {
@@ -3631,6 +3648,14 @@ void mjCTexture::CopyFromSpec() {
void mjCTexture::NameSpace(const mjCModel* m) {
mjCBase::NameSpace(m);
modelfiledir_ = FilePath(m->spec_modelfiledir_);
texturedir_ = FilePath(m->spec_texturedir_);
}
// free data storage allocated by lodepng // free data storage allocated by lodepng
mjCTexture::~mjCTexture() { mjCTexture::~mjCTexture() {
data_.clear(); data_.clear();
@@ -3960,7 +3985,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(model->modelfiledir_, filename, vfs); mjResource* resource = LoadResource(modelfiledir_.Str(), filename, vfs);
try { try {
if (asset_type == "image/png") { if (asset_type == "image/png") {
@@ -4176,12 +4201,12 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
} }
// make filename // make filename
std::string filename = mjuu_combinePaths(model->texturedir_, cubefiles_[i]); FilePath filename = texturedir_ + FilePath(cubefiles_[i]);
// load PNG or custom // load PNG or custom
unsigned int w, h; unsigned int w, h;
std::vector<unsigned char> image; std::vector<unsigned char> image;
LoadFlip(filename, vfs, image, w, h); LoadFlip(filename.Str(), vfs, image, w, h);
// PNG must be square // PNG must be square
if (w != h) { if (w != h) {
@@ -4244,6 +4269,14 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
void mjCTexture::Compile(const mjVFS* vfs) { void mjCTexture::Compile(const mjVFS* vfs) {
CopyFromSpec(); CopyFromSpec();
// copy paths from model if not already defined
if (modelfiledir_.empty()) {
modelfiledir_ = FilePath(model->modelfiledir_);
}
if (texturedir_.empty()) {
texturedir_ = FilePath(model->texturedir_);
}
// buffer from user // buffer from user
if (!data_.empty()) { if (!data_.empty()) {
if (data_.size() != nchannel*width*height) { if (data_.size() != nchannel*width*height) {
@@ -4299,13 +4332,13 @@ void mjCTexture::Compile(const mjVFS* vfs) {
} }
// make filename // make filename
std::string filename = mjuu_combinePaths(model->texturedir_, file_); FilePath filename = texturedir_ + FilePath(file_);
// dispatch // dispatch
if (type==mjTEXTURE_2D) { if (type==mjTEXTURE_2D) {
Load2D(filename, vfs); Load2D(filename.Str(), vfs);
} else { } else {
LoadCubeSingle(filename, vfs); LoadCubeSingle(filename.Str(), vfs);
} }
} }
+19
View File
@@ -808,6 +808,10 @@ class mjCMesh_ : public mjCBase {
mjCBoundingVolumeHierarchy tree_; // bounding volume hierarchy mjCBoundingVolumeHierarchy tree_; // bounding volume hierarchy
std::vector<double> face_aabb_; // bounding boxes of all faces std::vector<double> face_aabb_; // bounding boxes of all faces
// paths stored during model attachment
mujoco::user::FilePath modelfiledir_;
mujoco::user::FilePath meshdir_;
}; };
class mjCMesh: public mjCMesh_, private mjsMesh { class mjCMesh: public mjCMesh_, private mjsMesh {
@@ -823,6 +827,7 @@ class mjCMesh: public mjCMesh_, private mjsMesh {
void CopyFromSpec(void); void CopyFromSpec(void);
void PointToLocal(void); void PointToLocal(void);
void NameSpace(const mjCModel* m);
// accessors // accessors
const mjsPlugin& Plugin() const { return plugin; } const mjsPlugin& Plugin() const { return plugin; }
@@ -952,6 +957,10 @@ class mjCSkin_ : public mjCBase {
int matid; // material id int matid; // material id
std::vector<int> bodyid; // body ids std::vector<int> bodyid; // body ids
// paths stored during model attachment
mujoco::user::FilePath modelfiledir_;
mujoco::user::FilePath meshdir_;
}; };
class mjCSkin: public mjCSkin_, private mjsSkin { class mjCSkin: public mjCSkin_, private mjsSkin {
@@ -1005,6 +1014,10 @@ class mjCHField_ : public mjCBase {
std::string spec_file_; std::string spec_file_;
std::string spec_content_type_; std::string spec_content_type_;
std::vector<float> spec_userdata_; std::vector<float> spec_userdata_;
// paths stored during model attachment
mujoco::user::FilePath modelfiledir_;
mujoco::user::FilePath meshdir_;
}; };
class mjCHField : public mjCHField_, private mjsHField { class mjCHField : public mjCHField_, private mjsHField {
@@ -1024,6 +1037,7 @@ class mjCHField : public mjCHField_, private mjsHField {
void CopyFromSpec(void); void CopyFromSpec(void);
void PointToLocal(void); void PointToLocal(void);
void NameSpace(const mjCModel* m);
std::string File() const { return file_; } std::string File() const { return file_; }
@@ -1052,6 +1066,10 @@ class mjCTexture_ : public mjCBase {
std::string spec_file_; std::string spec_file_;
std::string spec_content_type_; std::string spec_content_type_;
std::vector<std::string> spec_cubefiles_; std::vector<std::string> spec_cubefiles_;
// paths stored during model attachment
mujoco::user::FilePath modelfiledir_;
mujoco::user::FilePath texturedir_;
}; };
class mjCTexture : public mjCTexture_, private mjsTexture { class mjCTexture : public mjCTexture_, private mjsTexture {
@@ -1071,6 +1089,7 @@ class mjCTexture : public mjCTexture_, private mjsTexture {
void CopyFromSpec(void); void CopyFromSpec(void);
void PointToLocal(void); void PointToLocal(void);
void NameSpace(const mjCModel* m);
std::string File() const { return file_; } std::string File() const { return file_; }
std::string get_content_type() const { return content_type_; } std::string get_content_type() const { return content_type_; }
+43
View File
@@ -17,6 +17,7 @@
#include <array> #include <array>
#include <cmath> #include <cmath>
#include <cstddef> #include <cstddef>
#include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -502,5 +503,47 @@ TEST_F(LengthRangeTest, LengthRangeThreading) {
mj_deleteSpec(spec); mj_deleteSpec(spec);
} }
// ----------------------------- test modeldir --------------------------------
TEST_F(MujocoTest, Modeldir) {
static constexpr char cube[] = 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)";
auto vfs = std::make_unique<mjVFS>();
mj_defaultVFS(vfs.get());
mj_addBufferVFS(vfs.get(), "meshdir/cube.obj", cube, sizeof(cube));
// child with the asset
mjSpec* child = mj_makeSpec();
mjsMesh* mesh = mjs_addMesh(child, 0);
mjsFrame* frame = mjs_addFrame(mjs_findBody(child, "world"), 0);
mjsGeom* geom = mjs_addGeom(mjs_findBody(child, "world"), 0);
mjs_setString(child->meshdir, "meshdir");
mjs_setString(mesh->file, "cube.obj");
mjs_setString(mesh->name, "cube");
mjs_setString(geom->meshname, "cube");
mjs_setFrame(geom->element, frame);
geom->type = mjGEOM_MESH;
// parent attaching the child
mjSpec* spec = mj_makeSpec();
mjs_setString(spec->meshdir, "asset");
mjs_attachFrame(mjs_findBody(spec, "world"), frame, "_", "");
mjModel* model = mj_compile(spec, vfs.get());
EXPECT_THAT(model, NotNull());
mj_deleteSpec(child);
mj_deleteSpec(spec);
mj_deleteModel(model);
mj_deleteVFS(vfs.get());
}
} // namespace } // namespace
} // namespace mujoco } // namespace mujoco