diff --git a/doc/includes/references.h b/doc/includes/references.h index 0b75f5a9..f454c394 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -1845,8 +1845,6 @@ typedef struct mjsCompiler_ { // compiler options mjtByte saveinertial; // save explicit inertial clause for all bodies to XML int alignfree; // align free joints with inertial frame mjLROpt LRopt; // options for lengthrange computation - mjString* meshdir; // mesh and hfield directory - mjString* texturedir; // texture directory } mjsCompiler; typedef struct mjSpec_ { // model specification mjsElement* element; // element type @@ -1855,6 +1853,8 @@ typedef struct mjSpec_ { // model specification // compiler data mjsCompiler compiler; // compiler options mjtByte strippath; // automatically strip paths from mesh files + mjString* meshdir; // mesh and hfield directory + mjString* texturedir; // texture directory // engine data mjOption option; // physics options diff --git a/include/mujoco/mjspec.h b/include/mujoco/mjspec.h index eba3a66a..5b2f42d8 100644 --- a/include/mujoco/mjspec.h +++ b/include/mujoco/mjspec.h @@ -151,8 +151,6 @@ typedef struct mjsCompiler_ { // compiler options mjtByte saveinertial; // save explicit inertial clause for all bodies to XML int alignfree; // align free joints with inertial frame mjLROpt LRopt; // options for lengthrange computation - mjString* meshdir; // mesh and hfield directory - mjString* texturedir; // texture directory } mjsCompiler; @@ -163,6 +161,8 @@ typedef struct mjSpec_ { // model specification // compiler data mjsCompiler compiler; // compiler options mjtByte strippath; // automatically strip paths from mesh files + mjString* meshdir; // mesh and hfield directory + mjString* texturedir; // texture directory // engine data mjOption option; // physics options diff --git a/python/mujoco/introspect/structs.py b/python/mujoco/introspect/structs.py index 599669f8..e3c035d2 100644 --- a/python/mujoco/introspect/structs.py +++ b/python/mujoco/introspect/structs.py @@ -7928,20 +7928,6 @@ STRUCTS: Mapping[str, StructDecl] = dict([ type=ValueType(name='mjLROpt'), doc='options for lengthrange computation', ), - StructFieldDecl( - name='meshdir', - type=PointerType( - inner_type=ValueType(name='mjString'), - ), - doc='mesh and hfield directory', - ), - StructFieldDecl( - name='texturedir', - type=PointerType( - inner_type=ValueType(name='mjString'), - ), - doc='texture directory', - ), ), )), ('mjSpec', @@ -7973,6 +7959,20 @@ STRUCTS: Mapping[str, StructDecl] = dict([ type=ValueType(name='mjtByte'), doc='automatically strip paths from mesh files', ), + StructFieldDecl( + name='meshdir', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='mesh and hfield directory', + ), + StructFieldDecl( + name='texturedir', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='texture directory', + ), StructFieldDecl( name='option', type=ValueType(name='mjOption'), diff --git a/python/mujoco/specs.cc b/python/mujoco/specs.cc index 384b1ebe..324a6207 100644 --- a/python/mujoco/specs.cc +++ b/python/mujoco/specs.cc @@ -593,22 +593,6 @@ PYBIND11_MODULE(_specs, m) { }, py::arg("degree"), py::arg("sequence") = py::none(), py::arg("orientation"), py::return_value_policy::copy); - mjSpec.def_property( - "meshdir", - [](MjSpec& self) -> std::string_view { - return *self.ptr->compiler.meshdir; - }, - [](MjSpec& self, std::string_view meshdir) { - *(self.ptr->compiler.meshdir) = meshdir; - }); - mjSpec.def_property( - "texturedir", - [](MjSpec& self) -> std::string_view { - return *self.ptr->compiler.texturedir; - }, - [](MjSpec& self, std::string_view texturedir) { - *(self.ptr->compiler.texturedir) = texturedir; - }); // ============================= MJSBODY ===================================== mjsBody.def( diff --git a/src/user/user_api.cc b/src/user/user_api.cc index 6e2e2ee3..803f07a6 100644 --- a/src/user/user_api.cc +++ b/src/user/user_api.cc @@ -297,7 +297,7 @@ int mjs_isWarning(mjSpec* s) { void mj_deleteSpec(mjSpec* s) { if (s) { mjCModel* model = static_cast(s->element); - model->Release(); + delete model; } } diff --git a/src/user/user_flexcomp.cc b/src/user/user_flexcomp.cc index 6c4f5eb5..8d378b8c 100644 --- a/src/user/user_flexcomp.cc +++ b/src/user/user_flexcomp.cc @@ -170,11 +170,11 @@ bool mjCFlexcomp::Make(mjsBody* body, char* error, int error_sz) { break; case mjFCOMPTYPE_MESH: - res = MakeMesh(model, compiler, error, error_sz); + res = MakeMesh(model, error, error_sz); break; case mjFCOMPTYPE_GMSH: - res = MakeGMSH(model, compiler, error, error_sz); + res = MakeGMSH(model, error, error_sz); break; case mjFCOMPTYPE_DIRECT: @@ -1075,7 +1075,7 @@ template static T* VecToArray(std::vector& vector, bool clear = // make mesh -bool mjCFlexcomp::MakeMesh(mjCModel* model, mjsCompiler* compiler, char* error, int error_sz) { +bool mjCFlexcomp::MakeMesh(mjCModel* model, char* error, int error_sz) { // strip path if (!file.empty() && model->spec.strippath) { file = mjuu_strippath(file); @@ -1092,7 +1092,7 @@ bool mjCFlexcomp::MakeMesh(mjCModel* model, mjsCompiler* compiler, char* error, } // load resource - std::string filename = mjuu_combinePaths(mjs_getString(compiler->meshdir), file); + std::string filename = mjuu_combinePaths(mjs_getString(model->spec.meshdir), file); mjResource* resource = nullptr; @@ -1194,7 +1194,7 @@ static int findstring(const char* buffer, int buffer_sz, const char* str) { // load points and elements from GMSH file -bool mjCFlexcomp::MakeGMSH(mjCModel* model, mjsCompiler* compiler, char* error, int error_sz) { +bool mjCFlexcomp::MakeGMSH(mjCModel* model, char* error, int error_sz) { // strip path if (!file.empty() && model->spec.strippath) { file = mjuu_strippath(file); @@ -1208,7 +1208,7 @@ bool mjCFlexcomp::MakeGMSH(mjCModel* model, mjsCompiler* compiler, char* error, // open resource mjResource* resource = nullptr; try { - std::string filename = mjuu_combinePaths(mjs_getString(compiler->meshdir), file); + std::string filename = mjuu_combinePaths(mjs_getString(model->spec.meshdir), file); resource = mjCBase::LoadResource(mjs_getString(model->spec.modelfiledir), filename, 0); } catch (mjCError err) { diff --git a/src/user/user_flexcomp.h b/src/user/user_flexcomp.h index 577c06b7..5e2108fd 100644 --- a/src/user/user_flexcomp.h +++ b/src/user/user_flexcomp.h @@ -57,8 +57,8 @@ class mjCFlexcomp { bool MakeGrid(char* error, int error_sz); bool MakeBox(char* error, int error_sz, int dim, bool open = true); bool MakeSquare(char* error, int error_sz); - bool MakeMesh(mjCModel* model, mjsCompiler* compiler, char* error, int error_sz); - bool MakeGMSH(mjCModel* model, mjsCompiler* compiler, char* error, int error_sz); + bool MakeMesh(mjCModel* model, char* error, int error_sz); + bool MakeGMSH(mjCModel* model, char* error, int error_sz); void LoadGMSH(mjCModel* model, mjResource* resource); void LoadGMSH41(char* buffer, int binary, int nodeend, int nodebegin, int elemend, int elembegin); diff --git a/src/user/user_mesh.cc b/src/user/user_mesh.cc index 060610d9..c0e725da 100644 --- a/src/user/user_mesh.cc +++ b/src/user/user_mesh.cc @@ -303,6 +303,9 @@ void mjCMesh::NameSpace(const mjCModel* m) { if (modelfiledir_.empty()) { modelfiledir_ = FilePath(m->spec_modelfiledir_); } + if (meshdir_.empty()) { + meshdir_ = FilePath(m->spec_meshdir_); + } if (!plugin_instance_name.empty()) { plugin_instance_name = m->prefix + plugin_instance_name + m->suffix; } @@ -702,12 +705,12 @@ void mjCMesh::TryCompile(const mjVFS* vfs) { } // copy paths from model if not already defined - mujoco::user::FilePath meshdir_; - meshdir_ = FilePath(mjs_getString(compiler->meshdir)); - if (modelfiledir_.empty()) { modelfiledir_ = FilePath(model->modelfiledir_); } + if (meshdir_.empty()) { + meshdir_ = FilePath(model->meshdir_); + } // remove path from file if necessary if (model->strippath) { @@ -3128,6 +3131,9 @@ void mjCSkin::NameSpace(const mjCModel* m) { if (modelfiledir_.empty()) { modelfiledir_ = FilePath(m->spec_modelfiledir_); } + if (meshdir_.empty()) { + meshdir_ = FilePath(m->spec_meshdir_); + } } @@ -3218,8 +3224,9 @@ void mjCSkin::Compile(const mjVFS* vfs) { if (modelfiledir_.empty()) { modelfiledir_ = FilePath(model->modelfiledir_); } - mujoco::user::FilePath meshdir_; - meshdir_ = FilePath(mjs_getString(compiler->meshdir)); + if (meshdir_.empty()) { + meshdir_ = FilePath(model->meshdir_); + } FilePath filename = meshdir_ + FilePath(file_); mjResource* resource = LoadResource(modelfiledir_.Str(), filename.Str(), vfs); diff --git a/src/user/user_model.cc b/src/user/user_model.cc index c7a7fd61..31e81b60 100644 --- a/src/user/user_model.cc +++ b/src/user/user_model.cc @@ -140,8 +140,8 @@ mjCModel::mjCModel() { elemtype = mjOBJ_MODEL; spec_comment_.clear(); spec_modelfiledir_.clear(); - meshdir_.clear(); - texturedir_.clear(); + spec_meshdir_.clear(); + spec_texturedir_.clear(); spec_modelname_ = "MuJoCo Model"; //------------------------ auto-computed statistics @@ -201,12 +201,10 @@ mjCModel& mjCModel::operator=(const mjCModel& other) { this->spec = other.spec; *static_cast(this) = static_cast(other); *static_cast(this) = static_cast(other); - PointToLocal(); // copy attached specs first so that we can resolve references to them - for (auto* s : other.specs_) { - specs_.push_back(s); - static_cast(s->element)->AddRef(); + for (const auto* s : other.specs_) { + specs_.push_back(mj_copySpec(s)); compiler2spec_[&s->compiler] = specs_.back(); } @@ -871,11 +869,13 @@ void mjCModel::PointToLocal() { spec.comment = &spec_comment_; spec.modelfiledir = &spec_modelfiledir_; spec.modelname = &spec_modelname_; - spec.compiler.meshdir = &meshdir_; - spec.compiler.texturedir = &texturedir_; + spec.meshdir = &spec_meshdir_; + spec.texturedir = &spec_texturedir_; comment = nullptr; modelfiledir = nullptr; modelname = nullptr; + meshdir = nullptr; + texturedir = nullptr; } @@ -885,6 +885,8 @@ void mjCModel::CopyFromSpec() { comment_ = spec_comment_; modelfiledir_ = spec_modelfiledir_; modelname_ = spec_modelname_; + meshdir_ = spec_meshdir_; + texturedir_ = spec_texturedir_; } diff --git a/src/user/user_model.h b/src/user/user_model.h index 725289eb..8639f319 100644 --- a/src/user/user_model.h +++ b/src/user/user_model.h @@ -148,6 +148,8 @@ class mjCModel_ : public mjsElement { std::string spec_comment_; std::string spec_modelfiledir_; std::string spec_modelname_; + std::string spec_meshdir_; + std::string spec_texturedir_; }; // mjCModel contains everything needed to generate the low-level model. @@ -328,18 +330,7 @@ class mjCModel : public mjCModel_, private mjSpec { // check for repeated names in list void CheckRepeat(mjtObj type); - // increment and decrement reference count - void AddRef() { ++refcount; } - int GetRef() const { return refcount; } - void Release() { - if (--refcount == 0) { - delete this; - } - } - private: - int refcount = 1; - // settings for each defaults class std::vector defaults_; diff --git a/src/user/user_objects.cc b/src/user/user_objects.cc index d0f4483e..c445790e 100644 --- a/src/user/user_objects.cc +++ b/src/user/user_objects.cc @@ -1210,13 +1210,6 @@ void mjCBase::NameSpace(const mjCModel* m) { -mjsCompiler* mjCBase::FindCompiler(const mjsCompiler* compiler) const { - mjSpec* origin = model->FindSpec(compiler); - return origin ? &origin->compiler : &model->spec.compiler; -} - - - // load resource if found (fallback to OS filesystem) mjResource* mjCBase::LoadResource(const std::string& modelfiledir, const std::string& filename, @@ -1320,7 +1313,8 @@ mjCBody::mjCBody(mjCModel* _model) { mjCBody::mjCBody(const mjCBody& other, mjCModel* _model) { model = _model; - compiler = FindCompiler(other.compiler); + mjSpec* origin = model->FindSpec(other.compiler); + compiler = origin ? &origin->compiler : &model->spec.compiler; *this = other; CopyPlugin(); } @@ -1395,8 +1389,7 @@ mjCBody& mjCBody::operator+=(const mjCBody& other) { mjCBody& mjCBody::operator+=(const mjCFrame& other) { // append a copy of the attached spec if (other.model != model && !model->FindSpec(&other.model->spec.compiler)) { - model->AppendSpec(&other.model->spec, &other.model->spec.compiler); - static_cast(other.model->spec.element)->AddRef(); + model->AppendSpec(mj_copySpec(&other.model->spec), &other.model->spec.compiler); } // create a copy of the subtree that contains the frame @@ -2651,8 +2644,7 @@ mjCFrame& mjCFrame::operator=(const mjCFrame& other) { mjCFrame& mjCFrame::operator+=(const mjCBody& other) { // append a copy of the attached spec if (other.model != model && !model->FindSpec(&other.model->spec.compiler)) { - model->AppendSpec(&other.model->spec, &other.model->spec.compiler); - static_cast(other.model->spec.element)->AddRef(); + model->AppendSpec(mj_copySpec(&other.model->spec), &other.model->spec.compiler); } // apply namespace and store keyframes in the source model @@ -4371,6 +4363,9 @@ void mjCHField::NameSpace(const mjCModel* m) { if (modelfiledir_.empty()) { modelfiledir_ = FilePath(m->spec_modelfiledir_); } + if (meshdir_.empty()) { + meshdir_ = FilePath(m->spec_meshdir_); + } } @@ -4494,8 +4489,9 @@ void mjCHField::Compile(const mjVFS* vfs) { if (modelfiledir_.empty()) { modelfiledir_ = FilePath(model->modelfiledir_); } - mujoco::user::FilePath meshdir_; - meshdir_ = FilePath(mjs_getString(compiler->meshdir)); + if (meshdir_.empty()) { + meshdir_ = FilePath(model->meshdir_); + } FilePath filename = meshdir_ + FilePath(file_); mjResource* resource = LoadResource(modelfiledir_.Str(), filename.Str(), vfs); @@ -4630,6 +4626,9 @@ void mjCTexture::NameSpace(const mjCModel* m) { if (modelfiledir_.empty()) { modelfiledir_ = FilePath(m->spec_modelfiledir_); } + if (texturedir_.empty()) { + texturedir_ = FilePath(m->spec_texturedir_); + } } @@ -5219,8 +5218,6 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) { } // make filename - mujoco::user::FilePath texturedir_; - texturedir_ = FilePath(mjs_getString(compiler->texturedir)); FilePath filename = texturedir_ + FilePath(cubefiles_[i]); // load PNG or custom @@ -5299,8 +5296,9 @@ void mjCTexture::Compile(const mjVFS* vfs) { if (modelfiledir_.empty()) { modelfiledir_ = FilePath(model->modelfiledir_); } - mujoco::user::FilePath texturedir_; - texturedir_ = FilePath(mjs_getString(compiler->texturedir)); + if (texturedir_.empty()) { + texturedir_ = FilePath(model->texturedir_); + } // buffer from user if (!data_.empty()) { diff --git a/src/user/user_objects.h b/src/user/user_objects.h index d464879f..0dd89477 100644 --- a/src/user/user_objects.h +++ b/src/user/user_objects.h @@ -361,9 +361,6 @@ class mjCBase : public mjCBase_ { // Returns parent of this object virtual mjCBase* GetParent() const { return nullptr; } - // Returns the model of this object - mjsCompiler* FindCompiler(const mjsCompiler* compiler) const; - // Copy assignment mjCBase& operator=(const mjCBase& other); @@ -1097,6 +1094,7 @@ class mjCMesh_ : public mjCBase { // paths stored during model attachment mujoco::user::FilePath modelfiledir_; + mujoco::user::FilePath meshdir_; }; class mjCMesh: public mjCMesh_, private mjsMesh { @@ -1312,6 +1310,7 @@ class mjCSkin_ : public mjCBase { // paths stored during model attachment mujoco::user::FilePath modelfiledir_; + mujoco::user::FilePath meshdir_; }; class mjCSkin: public mjCSkin_, private mjsSkin { @@ -1367,6 +1366,7 @@ class mjCHField_ : public mjCBase { // paths stored during model attachment mujoco::user::FilePath modelfiledir_; + mujoco::user::FilePath meshdir_; }; class mjCHField : public mjCHField_, private mjsHField { @@ -1417,6 +1417,7 @@ class mjCTexture_ : public mjCBase { // paths stored during model attachment mujoco::user::FilePath modelfiledir_; + mujoco::user::FilePath texturedir_; }; class mjCTexture : public mjCTexture_, private mjsTexture { diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc index 4a604173..ee2cbb0d 100644 --- a/src/xml/xml_native_reader.cc +++ b/src/xml/xml_native_reader.cc @@ -1067,16 +1067,16 @@ void mjXReader::Compiler(XMLElement* section, mjSpec* s) { memcpy(s->compiler.eulerseq, text.c_str(), 3); } if (ReadAttrTxt(section, "assetdir", text)) { - mjs_setString(s->compiler.meshdir, text.c_str()); - mjs_setString(s->compiler.texturedir, text.c_str()); + mjs_setString(s->meshdir, text.c_str()); + mjs_setString(s->texturedir, text.c_str()); } // meshdir and texturedir take precedence over assetdir string meshdir, texturedir; if (ReadAttrTxt(section, "meshdir", meshdir)) { - mjs_setString(s->compiler.meshdir, meshdir.c_str()); + mjs_setString(s->meshdir, meshdir.c_str()); }; if (ReadAttrTxt(section, "texturedir", texturedir)) { - mjs_setString(s->compiler.texturedir, texturedir.c_str()); + mjs_setString(s->texturedir, texturedir.c_str()); } if (MapValue(section, "discardvisual", &n, bool_map, 2)) { s->compiler.discardvisual = (n == 1); diff --git a/test/user/user_api_test.cc b/test/user/user_api_test.cc index f686692f..9ec351df 100644 --- a/test/user/user_api_test.cc +++ b/test/user/user_api_test.cc @@ -2631,7 +2631,7 @@ TEST_F(MujocoTest, DifferentOptionsInAttachedFrame) { mj_deleteModel(m_attached); } -TEST_F(MujocoTest, NotCopyAttachedSpec) { +TEST_F(MujocoTest, CopyAttachedSpec) { static constexpr char xml_parent[] = R"( @@ -2675,7 +2675,7 @@ TEST_F(MujocoTest, NotCopyAttachedSpec) { mjSpec* child_copy = mjs_findSpec(copy, "child"); EXPECT_THAT(child_copy, NotNull()); - EXPECT_EQ(child_copy, child); + EXPECT_NE(child_copy, child); mj_deleteSpec(spec); mj_deleteSpec(copy); diff --git a/test/user/user_model_test.cc b/test/user/user_model_test.cc index b3af89d1..8c581421 100644 --- a/test/user/user_model_test.cc +++ b/test/user/user_model_test.cc @@ -781,7 +781,7 @@ TEST_F(MujocoTest, Modeldir) { 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->compiler.meshdir, "meshdir"); + mjs_setString(child->meshdir, "meshdir"); mjs_setString(mesh->file, "cube.obj"); mjs_setName(mesh->element, "cube"); mjs_setString(geom->meshname, "cube"); @@ -791,7 +791,7 @@ TEST_F(MujocoTest, Modeldir) { // parent attaching the child mjSpec* spec = mj_makeSpec(); mjs_setDeepCopy(spec, true); - mjs_setString(spec->compiler.meshdir, "asset"); + mjs_setString(spec->meshdir, "asset"); mjs_attach(mjs_findBody(spec, "world")->element, frame->element, "_", ""); mjModel* model = mj_compile(spec, vfs.get()); EXPECT_THAT(model, NotNull()); diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index a9b3b0bd..37df8b1d 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -5854,8 +5854,6 @@ public unsafe struct mjsCompiler_ { public byte saveinertial; public int alignfree; public mjLROpt_ LRopt; - public void* meshdir; - public void* texturedir; } [StructLayout(LayoutKind.Sequential)]