Enable attaching plugins not instantiated in the extension section.
Fixes #2217. PiperOrigin-RevId: 695766524 Change-Id: I534b63400810bd6ef888f39f6665213280fd93ac
This commit is contained in:
committed by
Copybara-Service
parent
eb77d5b933
commit
45f908c165
@@ -268,10 +268,16 @@ void mjCMesh::CopyFromSpec() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void mjCMesh::CopyPlugin() {
|
||||||
|
model->CopyExplicitPlugin(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mjCMesh::~mjCMesh() {
|
mjCMesh::~mjCMesh() {
|
||||||
if (center_) mju_free(center_);
|
if (center_) mju_free(center_);
|
||||||
if (graph_) mju_free(graph_);
|
if (graph_) mju_free(graph_);
|
||||||
if (spec.plugin.active && spec.plugin.name->empty()) {
|
if (spec.plugin.active && spec.plugin.name->empty() && model) {
|
||||||
model->DeleteElement(spec.plugin.element);
|
model->DeleteElement(spec.plugin.element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-9
@@ -232,6 +232,7 @@ void mjCModel::CopyList(std::vector<T*>& dest,
|
|||||||
dest.back()->model = this;
|
dest.back()->model = this;
|
||||||
dest.back()->compiler = origin ? &origin->compiler : &spec.compiler;
|
dest.back()->compiler = origin ? &origin->compiler : &spec.compiler;
|
||||||
dest.back()->id = -1;
|
dest.back()->id = -1;
|
||||||
|
dest.back()->CopyPlugin();
|
||||||
}
|
}
|
||||||
if (!dest.empty()) {
|
if (!dest.empty()) {
|
||||||
processlist(ids, dest, dest[0]->elemtype);
|
processlist(ids, dest, dest[0]->elemtype);
|
||||||
@@ -309,8 +310,28 @@ void mjCModel::SaveDofOffsets(bool computesize) {
|
|||||||
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void mjCModel::CopyPlugin(std::vector<mjCPlugin*>& dest,
|
void mjCModel::CopyExplicitPlugin(T* obj) {
|
||||||
const std::vector<mjCPlugin*>& source,
|
if (!obj->plugin.active || !obj->plugin_instance_name.empty() || !obj->spec.plugin.element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mjCPlugin* origin = static_cast<mjCPlugin*>(obj->spec.plugin.element);
|
||||||
|
mjCPlugin* candidate = new mjCPlugin(*origin);
|
||||||
|
candidate->id = plugins_.size();
|
||||||
|
candidate->model = this;
|
||||||
|
plugins_.push_back(candidate);
|
||||||
|
obj->spec.plugin.element = candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
template void mjCModel::CopyExplicitPlugin<mjCBody>(mjCBody* obj);
|
||||||
|
template void mjCModel::CopyExplicitPlugin<mjCGeom>(mjCGeom* obj);
|
||||||
|
template void mjCModel::CopyExplicitPlugin<mjCMesh>(mjCMesh* obj);
|
||||||
|
template void mjCModel::CopyExplicitPlugin<mjCActuator>(mjCActuator* obj);
|
||||||
|
template void mjCModel::CopyExplicitPlugin<mjCSensor>(mjCSensor* obj);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void mjCModel::CopyPlugin(const std::vector<mjCPlugin*>& source,
|
||||||
const std::vector<T*>& list) {
|
const std::vector<T*>& list) {
|
||||||
// store elements that reference a plugin instance
|
// store elements that reference a plugin instance
|
||||||
std::unordered_map<std::string, T*> instances;
|
std::unordered_map<std::string, T*> instances;
|
||||||
@@ -330,9 +351,10 @@ void mjCModel::CopyPlugin(std::vector<mjCPlugin*>& dest,
|
|||||||
candidate->NameSpace(plugin->model);
|
candidate->NameSpace(plugin->model);
|
||||||
bool referenced = instances.find(candidate->name) != instances.end();
|
bool referenced = instances.find(candidate->name) != instances.end();
|
||||||
auto same_name = [candidate](const mjCPlugin* dest) { return dest->name == candidate->name; };
|
auto same_name = [candidate](const mjCPlugin* dest) { return dest->name == candidate->name; };
|
||||||
bool instance_exists = std::find_if(dest.begin(), dest.end(), same_name) != dest.end();
|
bool instance_exists = std::find_if(plugins_.begin(), plugins_.end(),
|
||||||
|
same_name) != plugins_.end();
|
||||||
if (referenced && !instance_exists) {
|
if (referenced && !instance_exists) {
|
||||||
dest.push_back(candidate);
|
plugins_.push_back(candidate);
|
||||||
instances.at(candidate->name)->spec.plugin.element = candidate;
|
instances.at(candidate->name)->spec.plugin.element = candidate;
|
||||||
} else {
|
} else {
|
||||||
delete candidate;
|
delete candidate;
|
||||||
@@ -390,11 +412,11 @@ mjCModel& mjCModel::operator+=(const mjCModel& other) {
|
|||||||
CopyList(tuples_, other.tuples_);
|
CopyList(tuples_, other.tuples_);
|
||||||
|
|
||||||
// create new plugins and map them
|
// create new plugins and map them
|
||||||
CopyPlugin(plugins_, other.plugins_, bodies_);
|
CopyPlugin(other.plugins_, bodies_);
|
||||||
CopyPlugin(plugins_, other.plugins_, geoms_);
|
CopyPlugin(other.plugins_, geoms_);
|
||||||
CopyPlugin(plugins_, other.plugins_, meshes_);
|
CopyPlugin(other.plugins_, meshes_);
|
||||||
CopyPlugin(plugins_, other.plugins_, actuators_);
|
CopyPlugin(other.plugins_, actuators_);
|
||||||
CopyPlugin(plugins_, other.plugins_, sensors_);
|
CopyPlugin(other.plugins_, sensors_);
|
||||||
for (const auto& [plugin, slot] : other.active_plugins_) {
|
for (const auto& [plugin, slot] : other.active_plugins_) {
|
||||||
if (!IsPluginActive(plugin, active_plugins_)) {
|
if (!IsPluginActive(plugin, active_plugins_)) {
|
||||||
active_plugins_.emplace_back(std::make_pair(plugin, slot));
|
active_plugins_.emplace_back(std::make_pair(plugin, slot));
|
||||||
|
|||||||
@@ -366,9 +366,11 @@ class mjCModel : public mjCModel_, private mjSpec {
|
|||||||
template <class T> void CopyList(std::vector<T*>& dest,
|
template <class T> void CopyList(std::vector<T*>& dest,
|
||||||
const std::vector<T*>& sources);
|
const std::vector<T*>& sources);
|
||||||
|
|
||||||
|
// copy plugins that are explicitly instantiated by the argument object to this model
|
||||||
|
template <class T> void CopyExplicitPlugin(T* obj);
|
||||||
|
|
||||||
// copy vector of plugins to this model
|
// copy vector of plugins to this model
|
||||||
template <class T> void CopyPlugin(std::vector<mjCPlugin*>& dest,
|
template <class T> void CopyPlugin(const std::vector<mjCPlugin*>& sources,
|
||||||
const std::vector<mjCPlugin*>& sources,
|
|
||||||
const std::vector<T*>& list);
|
const std::vector<T*>& list);
|
||||||
|
|
||||||
// delete from list the elements that cause an error
|
// delete from list the elements that cause an error
|
||||||
|
|||||||
@@ -789,6 +789,7 @@ mjCBody::mjCBody(const mjCBody& other, mjCModel* _model) {
|
|||||||
mjSpec* origin = model->FindSpec(mjs_getString(other.model->spec.modelname));
|
mjSpec* origin = model->FindSpec(mjs_getString(other.model->spec.modelname));
|
||||||
compiler = origin ? &origin->compiler : &model->spec.compiler;
|
compiler = origin ? &origin->compiler : &model->spec.compiler;
|
||||||
*this = other;
|
*this = other;
|
||||||
|
CopyPlugin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -942,6 +943,7 @@ void mjCBody::CopyList(std::vector<T*>& dst, const std::vector<T*>& src,
|
|||||||
dst.back()->model = model;
|
dst.back()->model = model;
|
||||||
dst.back()->compiler = origin ? &origin->compiler : &model->spec.compiler;
|
dst.back()->compiler = origin ? &origin->compiler : &model->spec.compiler;
|
||||||
dst.back()->id = -1;
|
dst.back()->id = -1;
|
||||||
|
dst.back()->CopyPlugin();
|
||||||
dst.back()->classname = src[i]->classname;
|
dst.back()->classname = src[i]->classname;
|
||||||
|
|
||||||
// assign dst frame to src frame
|
// assign dst frame to src frame
|
||||||
@@ -992,6 +994,12 @@ void mjCBody::CopyFromSpec() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void mjCBody::CopyPlugin() {
|
||||||
|
model->CopyExplicitPlugin(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// destructor
|
// destructor
|
||||||
mjCBody::~mjCBody() {
|
mjCBody::~mjCBody() {
|
||||||
// delete objects allocated here
|
// delete objects allocated here
|
||||||
@@ -1011,7 +1019,7 @@ mjCBody::~mjCBody() {
|
|||||||
cameras.clear();
|
cameras.clear();
|
||||||
lights.clear();
|
lights.clear();
|
||||||
|
|
||||||
if (spec.plugin.active && spec.plugin.name->empty()) {
|
if (spec.plugin.active && spec.plugin.name->empty() && model) {
|
||||||
model->DeleteElement(spec.plugin.element);
|
model->DeleteElement(spec.plugin.element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2221,7 +2229,7 @@ mjCGeom::mjCGeom(const mjCGeom& other) {
|
|||||||
|
|
||||||
|
|
||||||
mjCGeom::~mjCGeom() {
|
mjCGeom::~mjCGeom() {
|
||||||
if (spec.plugin.active && spec.plugin.name->empty()) {
|
if (spec.plugin.active && spec.plugin.name->empty() && model) {
|
||||||
model->DeleteElement(spec.plugin.element);
|
model->DeleteElement(spec.plugin.element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2273,6 +2281,12 @@ void mjCGeom::CopyFromSpec() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void mjCGeom::CopyPlugin() {
|
||||||
|
model->CopyExplicitPlugin(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void mjCGeom::NameSpace(const mjCModel* m) {
|
void mjCGeom::NameSpace(const mjCModel* m) {
|
||||||
mjCBase::NameSpace(m);
|
mjCBase::NameSpace(m);
|
||||||
if (!spec_material_.empty() && model != m) {
|
if (!spec_material_.empty() && model != m) {
|
||||||
@@ -5636,7 +5650,7 @@ mjCActuator::mjCActuator(const mjCActuator& other) {
|
|||||||
|
|
||||||
|
|
||||||
mjCActuator::~mjCActuator() {
|
mjCActuator::~mjCActuator() {
|
||||||
if (spec.plugin.active && spec.plugin.name->empty()) {
|
if (spec.plugin.active && spec.plugin.name->empty() && model) {
|
||||||
model->DeleteElement(spec.plugin.element);
|
model->DeleteElement(spec.plugin.element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5731,6 +5745,12 @@ void mjCActuator::CopyFromSpec() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void mjCActuator::CopyPlugin() {
|
||||||
|
model->CopyExplicitPlugin(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void mjCActuator::ResolveReferences(const mjCModel* m) {
|
void mjCActuator::ResolveReferences(const mjCModel* m) {
|
||||||
switch (trntype) {
|
switch (trntype) {
|
||||||
case mjTRN_JOINT:
|
case mjTRN_JOINT:
|
||||||
@@ -6000,7 +6020,7 @@ mjCSensor::mjCSensor(const mjCSensor& other) {
|
|||||||
|
|
||||||
|
|
||||||
mjCSensor::~mjCSensor() {
|
mjCSensor::~mjCSensor() {
|
||||||
if (spec.plugin.active && spec.plugin.name->empty()) {
|
if (spec.plugin.active && spec.plugin.name->empty() && model) {
|
||||||
model->DeleteElement(spec.plugin.element);
|
model->DeleteElement(spec.plugin.element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6063,6 +6083,12 @@ void mjCSensor::CopyFromSpec() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void mjCSensor::CopyPlugin() {
|
||||||
|
model->CopyExplicitPlugin(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void mjCSensor::ResolveReferences(const mjCModel* m) {
|
void mjCSensor::ResolveReferences(const mjCModel* m) {
|
||||||
objname_ = prefix + objname_ + suffix;
|
objname_ = prefix + objname_ + suffix;
|
||||||
refname_ = prefix + refname_ + suffix;
|
refname_ = prefix + refname_ + suffix;
|
||||||
|
|||||||
@@ -208,6 +208,9 @@ class mjCBase : public mjCBase_ {
|
|||||||
// Appends prefix and suffix to reference
|
// Appends prefix and suffix to reference
|
||||||
virtual void NameSpace(const mjCModel* m);
|
virtual void NameSpace(const mjCModel* m);
|
||||||
|
|
||||||
|
// Copy plugins instantiated in this object
|
||||||
|
virtual void CopyPlugin() {}
|
||||||
|
|
||||||
// Copy assignment
|
// Copy assignment
|
||||||
mjCBase& operator=(const mjCBase& other);
|
mjCBase& operator=(const mjCBase& other);
|
||||||
|
|
||||||
@@ -360,6 +363,7 @@ class mjCBody : public mjCBody_, private mjsBody {
|
|||||||
void CopyFromSpec(); // copy spec into attributes
|
void CopyFromSpec(); // copy spec into attributes
|
||||||
void PointToLocal(void);
|
void PointToLocal(void);
|
||||||
void NameSpace_(const mjCModel* m, bool propagate = true);
|
void NameSpace_(const mjCModel* m, bool propagate = true);
|
||||||
|
void CopyPlugin();
|
||||||
|
|
||||||
// copy src list of elements into dst; set body, model and frame
|
// copy src list of elements into dst; set body, model and frame
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -557,6 +561,7 @@ class mjCGeom : public mjCGeom_, private mjsGeom {
|
|||||||
void CopyFromSpec(void);
|
void CopyFromSpec(void);
|
||||||
void PointToLocal(void);
|
void PointToLocal(void);
|
||||||
void NameSpace(const mjCModel* m);
|
void NameSpace(const mjCModel* m);
|
||||||
|
void CopyPlugin();
|
||||||
|
|
||||||
// inherited
|
// inherited
|
||||||
using mjCBase::info;
|
using mjCBase::info;
|
||||||
@@ -935,6 +940,7 @@ class mjCMesh: public mjCMesh_, private mjsMesh {
|
|||||||
void ApplyTransformations(); // apply user transformations
|
void ApplyTransformations(); // apply user transformations
|
||||||
void ComputeFaceCentroid(double[3]); // compute centroid of all faces
|
void ComputeFaceCentroid(double[3]); // compute centroid of all faces
|
||||||
void CheckMesh(mjtGeomInertia type); // check if the mesh is valid
|
void CheckMesh(mjtGeomInertia type); // check if the mesh is valid
|
||||||
|
void CopyPlugin();
|
||||||
|
|
||||||
// mesh data to be copied into mjModel
|
// mesh data to be copied into mjModel
|
||||||
double* center_; // face circumcenter data (3*nface)
|
double* center_; // face circumcenter data (3*nface)
|
||||||
@@ -1503,6 +1509,7 @@ class mjCActuator : public mjCActuator_, private mjsActuator {
|
|||||||
void PointToLocal();
|
void PointToLocal();
|
||||||
void ResolveReferences(const mjCModel* m);
|
void ResolveReferences(const mjCModel* m);
|
||||||
void NameSpace(const mjCModel* m);
|
void NameSpace(const mjCModel* m);
|
||||||
|
void CopyPlugin();
|
||||||
|
|
||||||
// reset keyframe references for allowing self-attach
|
// reset keyframe references for allowing self-attach
|
||||||
void ForgetKeyframes();
|
void ForgetKeyframes();
|
||||||
@@ -1556,6 +1563,7 @@ class mjCSensor : public mjCSensor_, private mjsSensor {
|
|||||||
void PointToLocal();
|
void PointToLocal();
|
||||||
void ResolveReferences(const mjCModel* m);
|
void ResolveReferences(const mjCModel* m);
|
||||||
void NameSpace(const mjCModel* m);
|
void NameSpace(const mjCModel* m);
|
||||||
|
void CopyPlugin();
|
||||||
|
|
||||||
mjCBase* obj; // sensorized object
|
mjCBase* obj; // sensorized object
|
||||||
mjCBase* ref; // sensorized reference
|
mjCBase* ref; // sensorized reference
|
||||||
|
|||||||
@@ -293,6 +293,56 @@ TEST_F(PluginTest, AttachPlugin) {
|
|||||||
mj_deleteSpec(spec_2);
|
mj_deleteSpec(spec_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(PluginTest, AttachExplicitPlugin) {
|
||||||
|
static constexpr char xml_parent[] = R"(
|
||||||
|
<mujoco model="MuJoCo Model">
|
||||||
|
<worldbody>
|
||||||
|
<body name="body"/>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>)";
|
||||||
|
|
||||||
|
static constexpr char xml_child[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<extension>
|
||||||
|
<plugin plugin="mujoco.sensor.touch_grid"/>
|
||||||
|
</extension>
|
||||||
|
<worldbody>
|
||||||
|
<body name="body">
|
||||||
|
<geom type="sphere" size=".1" />
|
||||||
|
<site name="touch2" size="0.001"/>
|
||||||
|
</body>
|
||||||
|
</worldbody>
|
||||||
|
<sensor>
|
||||||
|
<plugin name="touch2" plugin="mujoco.sensor.touch_grid" objtype="site" objname="touch2">
|
||||||
|
<config key="size" value="8 12"/>
|
||||||
|
<config key="fov" value="10 13"/>
|
||||||
|
<config key="gamma" value="0"/>
|
||||||
|
<config key="nchannel" value="1"/>
|
||||||
|
</plugin>
|
||||||
|
</sensor>
|
||||||
|
</mujoco>)";
|
||||||
|
|
||||||
|
std::array<char, 1000> err;
|
||||||
|
mjSpec* parent = mj_parseXMLString(xml_parent, 0, err.data(), err.size());
|
||||||
|
ASSERT_THAT(parent, NotNull()) << err.data();
|
||||||
|
mjSpec* child = mj_parseXMLString(xml_child, 0, err.data(), err.size());
|
||||||
|
ASSERT_THAT(child, NotNull()) << err.data();
|
||||||
|
|
||||||
|
mjsBody* body_parent = mjs_findBody(parent, "body");
|
||||||
|
EXPECT_THAT(body_parent, NotNull());
|
||||||
|
mjsFrame* attachment_frame = mjs_addFrame(body_parent, 0);
|
||||||
|
EXPECT_THAT(attachment_frame, NotNull());
|
||||||
|
|
||||||
|
mjs_attachBody(attachment_frame, mjs_findBody(child, "body"), "child-", "");
|
||||||
|
mjModel* model = mj_compile(parent, nullptr);
|
||||||
|
EXPECT_THAT(model, NotNull());
|
||||||
|
EXPECT_THAT(model->nplugin, 1);
|
||||||
|
|
||||||
|
mj_deleteSpec(parent);
|
||||||
|
mj_deleteSpec(child);
|
||||||
|
mj_deleteModel(model);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(PluginTest, ReplicatePlugin) {
|
TEST_F(PluginTest, ReplicatePlugin) {
|
||||||
static constexpr char xml[] = R"(
|
static constexpr char xml[] = R"(
|
||||||
<mujoco>
|
<mujoco>
|
||||||
@@ -326,6 +376,40 @@ TEST_F(PluginTest, ReplicatePlugin) {
|
|||||||
mj_deleteModel(model);
|
mj_deleteModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(PluginTest, ReplicateExplicitPlugin) {
|
||||||
|
static constexpr char xml[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<extension>
|
||||||
|
<plugin plugin="mujoco.sensor.touch_grid"/>
|
||||||
|
</extension>
|
||||||
|
<worldbody>
|
||||||
|
<body name="tactile_sensor_2">
|
||||||
|
<replicate count="2" offset="0 0.1 0">
|
||||||
|
<geom type="sphere" size=".0008" />
|
||||||
|
</replicate>
|
||||||
|
<site name="touch2" size="0.001"/>
|
||||||
|
</body>
|
||||||
|
</worldbody>
|
||||||
|
<sensor>
|
||||||
|
<plugin name="touch2" plugin="mujoco.sensor.touch_grid" objtype="site" objname="touch2">
|
||||||
|
<config key="size" value="8 12"/>
|
||||||
|
<config key="fov" value="10 13"/>
|
||||||
|
<config key="gamma" value="0"/>
|
||||||
|
<config key="nchannel" value="1"/>
|
||||||
|
</plugin>
|
||||||
|
</sensor>
|
||||||
|
</mujoco>)";
|
||||||
|
|
||||||
|
std::array<char, 1000> err;
|
||||||
|
mjSpec* spec = mj_parseXMLString(xml, 0, err.data(), err.size());
|
||||||
|
ASSERT_THAT(spec, NotNull()) << err.data();
|
||||||
|
mjModel* model = mj_compile(spec, nullptr);
|
||||||
|
EXPECT_THAT(model, NotNull());
|
||||||
|
EXPECT_THAT(model->nplugin, 1);
|
||||||
|
mj_deleteSpec(spec);
|
||||||
|
mj_deleteModel(model);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(MujocoTest, RecompileFails) {
|
TEST_F(MujocoTest, RecompileFails) {
|
||||||
mjSpec* spec = mj_makeSpec();
|
mjSpec* spec = mj_makeSpec();
|
||||||
mjsBody* body = mjs_addBody(mjs_findBody(spec, "world"), 0);
|
mjsBody* body = mjs_addBody(mjs_findBody(spec, "world"), 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user