Do not copy the spec during attach.

Use a reference count for managing the memory.

PiperOrigin-RevId: 716169486
Change-Id: Id270c4858c17b9250115e9544d5ea143584e2d5f
This commit is contained in:
Alessio Quaglino
2025-01-16 03:52:31 -08:00
committed by Copybara-Service
parent 6436055c6c
commit c2138c3fb0
19 changed files with 505 additions and 173 deletions
+11 -2
View File
@@ -208,7 +208,7 @@ int mjs_detachBody(mjSpec* s, mjsBody* b) {
model->SetError(e);
return -1;
}
delete body;
model->Detach(body);
return 0;
}
@@ -254,6 +254,15 @@ int mjs_activatePlugin(mjSpec* s, const char* name) {
// set deep copy flag
int mjs_setDeepCopy(mjSpec* s, int deepcopy) {
mjCModel* model = static_cast<mjCModel*>(s->element);
model->SetDeepCopy(deepcopy);
return 0;
}
// delete object, return 0 if success
int mjs_delete(mjsElement* element) {
mjCBase* object = static_cast<mjCBase*>(element);
@@ -705,7 +714,7 @@ const char* mjs_resolveOrientation(double quat[4], mjtByte degree, const char* s
mjsFrame* mjs_bodyToFrame(mjsBody** body) {
mjCBody* bodyC = static_cast<mjCBody*>((*body)->element);
mjCFrame* frameC = bodyC->ToFrame();
delete bodyC;
bodyC->model->Detach(bodyC);
*body = nullptr;
return &frameC->spec;
}
+3
View File
@@ -63,6 +63,9 @@ MJAPI void mjs_addSpec(mjSpec* s, mjSpec* child);
// Activate plugin, return 0 on success.
MJAPI int mjs_activatePlugin(mjSpec* s, const char* name);
// Turn deep copy on or off attach. Returns 0 on success.
MJAPI int mjs_setDeepCopy(mjSpec* s, int deepcopy);
//---------------------------------- Attachment ----------------------------------------------------
-3
View File
@@ -278,9 +278,6 @@ void mjCMesh::CopyPlugin() {
mjCMesh::~mjCMesh() {
if (center_) mju_free(center_);
if (graph_) mju_free(graph_);
if (spec.plugin.active && spec.plugin.name->empty() && model) {
model->DeleteElement(spec.plugin.element);
}
}
+102 -26
View File
@@ -139,6 +139,7 @@ mjCModel::mjCModel() {
center_auto[0] = center_auto[1] = center_auto[2] = 0;
#endif
deepcopy_ = false;
nplugin = 0;
Clear();
@@ -181,6 +182,7 @@ mjCModel::mjCModel(const mjCModel& other) {
mjCModel& mjCModel::operator=(const mjCModel& other) {
deepcopy_ = true;
if (this != &other) {
this->spec = other.spec;
*static_cast<mjCModel_*>(this) = static_cast<const mjCModel_&>(other);
@@ -210,6 +212,7 @@ mjCModel& mjCModel::operator=(const mjCModel& other) {
ids[i] = other.ids[i];
}
}
deepcopy_ = other.deepcopy_;
return *this;
}
@@ -222,20 +225,29 @@ void mjCModel::CopyList(std::vector<T*>& dest,
// loop over the elements from the other model
int nsource = (int)source.size();
for (int i = 0; i < nsource; i++) {
T* candidate = new T(*source[i]);
T* candidate = deepcopy_ ? new T(*source[i]) : source[i];
try {
// try to find the referenced object in this model
candidate->NameSpace(source[i]->model);
mjCModel* source_model = source[i]->model;
candidate->model = this;
candidate->NameSpace(source_model);
candidate->CopyFromSpec();
candidate->ResolveReferences(this);
} catch (mjCError err) {
// if not present, skip the element
// TODO: do not skip elements that contain user errors
delete candidate;
if (deepcopy_) {
candidate->model = nullptr;
delete candidate;
}
continue;
}
// copy the element from the other model to this model
source[i]->ForgetKeyframes();
if (deepcopy_) {
source[i]->ForgetKeyframes();
} else {
candidate->AddRef();
}
mjSpec* origin = FindSpec(source[i]->compiler);
dest.push_back(candidate);
dest.back()->model = this;
@@ -324,9 +336,12 @@ void mjCModel::CopyExplicitPlugin(T* obj) {
return;
}
mjCPlugin* origin = static_cast<mjCPlugin*>(obj->spec.plugin.element);
mjCPlugin* candidate = new mjCPlugin(*origin);
mjCPlugin* candidate = deepcopy_ ? new mjCPlugin(*origin) : origin;
candidate->id = plugins_.size();
candidate->model = this;
if (!deepcopy_) {
candidate->AddRef();
}
plugins_.push_back(candidate);
obj->spec.plugin.element = candidate;
}
@@ -566,7 +581,7 @@ void deletefromlist(std::vector<T*>* list, mjsElement* element) {
for (int j = 0; j < list->size(); ++j) {
list->at(j)->id = -1;
if (list->at(j) == element) {
delete list->at(j);
list->at(j)->Release();
list->erase(list->begin() + j);
j--;
}
@@ -577,8 +592,9 @@ void deletefromlist(std::vector<T*>* list, mjsElement* element) {
// discard all invalid elements from all lists
void mjCModel::DeleteElement(mjsElement* el) {
mjCBody *world = bodies_[0];
mjCBody *world = nullptr;
if (compiled) {
world = bodies_[0];
ResetTreeLists();
}
@@ -588,8 +604,14 @@ void mjCModel::DeleteElement(mjsElement* el) {
break;
case mjOBJ_GEOM:
deletefromlist(&(static_cast<mjCGeom*>(el)->body->geoms), el);
{
mjCGeom* geom = static_cast<mjCGeom*>(el);
if (geom->plugin.active && geom->plugin.name->empty() && geom->GetRef() == 1) {
DeleteElement(geom->plugin.element);
}
deletefromlist(&(geom->body->geoms), el);
break;
}
case mjOBJ_SITE:
deletefromlist(&(static_cast<mjCSite*>(el)->body->sites), el);
@@ -607,6 +629,36 @@ void mjCModel::DeleteElement(mjsElement* el) {
deletefromlist(&(static_cast<mjCCamera*>(el)->body->cameras), el);
break;
case mjOBJ_MESH:
{
mjCMesh* mesh = static_cast<mjCMesh*>(el);
if (mesh->plugin.active && mesh->plugin.name->empty() && mesh->GetRef() == 1) {
DeleteElement(mesh->plugin.element);
}
deletefromlist(object_lists_[mjOBJ_MESH], el);
break;
}
case mjOBJ_ACTUATOR:
{
mjCActuator* actuator = static_cast<mjCActuator*>(el);
if (actuator->plugin.active && actuator->plugin.name->empty() && actuator->GetRef() == 1) {
DeleteElement(actuator->plugin.element);
}
deletefromlist(object_lists_[mjOBJ_ACTUATOR], el);
break;
}
case mjOBJ_SENSOR:
{
mjCSensor* sensor = static_cast<mjCSensor*>(el);
if (sensor->plugin.active && sensor->plugin.name->empty() && sensor->GetRef() == 1) {
DeleteElement(sensor->plugin.element);
}
deletefromlist(object_lists_[mjOBJ_SENSOR], el);
break;
}
default:
deletefromlist(object_lists_[el->elemtype], el);
break;
@@ -621,6 +673,29 @@ void mjCModel::DeleteElement(mjsElement* el) {
// recursively delete all plugins in the subtree
void deletesubtreeplugin(mjCBody* subtree, mjCModel* model) {
mjsPlugin* plugin = &(subtree->spec.plugin);
if (plugin->active && plugin->name->empty()) {
model->DeleteElement(plugin->element);
}
for (auto* body : subtree->Bodies()) {
deletesubtreeplugin(body, model);
}
}
// deletes all plugins in the subtree and then the subtree itself
void mjCModel::Detach(mjCBody* subtree) {
if (subtree->GetRef() == 1) {
deletesubtreeplugin(subtree, this);
}
subtree->Release();
}
// TODO: we should not use C-type casting with multiple C++ inheritance
void mjCModel::CreateObjectLists() {
for (int i = 0; i < mjNOBJECT; ++i) {
@@ -688,28 +763,28 @@ mjCModel::~mjCModel() {
compiled = false;
// delete kinematic tree and all objects allocated in it
delete bodies_[0];
bodies_[0]->Release();
// delete objects allocated in mjCModel
for (int i=0; i<flexes_.size(); i++) delete flexes_[i];
for (int i=0; i<meshes_.size(); i++) delete meshes_[i];
for (int i=0; i<skins_.size(); i++) delete skins_[i];
for (int i=0; i<hfields_.size(); i++) delete hfields_[i];
for (int i=0; i<textures_.size(); i++) delete textures_[i];
for (int i=0; i<materials_.size(); i++) delete materials_[i];
for (int i=0; i<pairs_.size(); i++) delete pairs_[i];
for (int i=0; i<excludes_.size(); i++) delete excludes_[i];
for (int i=0; i<equalities_.size(); i++) delete equalities_[i];
for (int i=0; i<tendons_.size(); i++) delete tendons_[i]; // also deletes wraps
for (int i=0; i<actuators_.size(); i++) delete actuators_[i];
for (int i=0; i<sensors_.size(); i++) delete sensors_[i];
for (int i=0; i<numerics_.size(); i++) delete numerics_[i];
for (int i=0; i<texts_.size(); i++) delete texts_[i];
for (int i=0; i<tuples_.size(); i++) delete tuples_[i];
for (int i=0; i<keys_.size(); i++) delete keys_[i];
for (int i=0; i<flexes_.size(); i++) flexes_[i]->Release();
for (int i=0; i<meshes_.size(); i++) meshes_[i]->Release();
for (int i=0; i<skins_.size(); i++) skins_[i]->Release();
for (int i=0; i<hfields_.size(); i++) hfields_[i]->Release();
for (int i=0; i<textures_.size(); i++) textures_[i]->Release();
for (int i=0; i<materials_.size(); i++) materials_[i]->Release();
for (int i=0; i<pairs_.size(); i++) pairs_[i]->Release();
for (int i=0; i<excludes_.size(); i++) excludes_[i]->Release();
for (int i=0; i<equalities_.size(); i++) equalities_[i]->Release();
for (int i=0; i<tendons_.size(); i++) tendons_[i]->Release(); // also deletes wraps
for (int i=0; i<actuators_.size(); i++) actuators_[i]->Release();
for (int i=0; i<sensors_.size(); i++) sensors_[i]->Release();
for (int i=0; i<numerics_.size(); i++) numerics_[i]->Release();
for (int i=0; i<texts_.size(); i++) texts_[i]->Release();
for (int i=0; i<tuples_.size(); i++) tuples_[i]->Release();
for (int i=0; i<keys_.size(); i++) keys_[i]->Release();
for (int i=0; i<defaults_.size(); i++) delete defaults_[i];
for (int i=0; i<specs_.size(); i++) mj_deleteSpec(specs_[i]);
for (int i=0; i<plugins_.size(); i++) delete plugins_[i];
for (int i=0; i<plugins_.size(); i++) plugins_[i]->Release();
// clear sizes and pointer lists created in Compile
Clear();
@@ -933,6 +1008,7 @@ mjCPlugin* mjCModel::AddPlugin() {
// append spec to spec
void mjCModel::AppendSpec(mjSpec* spec) {
// TODO: check if the spec is already in the list
specs_.push_back(spec);
}
+8 -1
View File
@@ -221,6 +221,9 @@ class mjCModel : public mjCModel_, private mjSpec {
// delete object from the corresponding list
void DeleteElement(mjsElement* el);
// detach subtree from model
void Detach(mjCBody* subtree);
// API for access to model elements (outside tree)
int NumObjects(mjtObj type); // number of objects in specified list
mjCBase* GetObject(mjtObj type, int id); // pointer to specified object
@@ -307,6 +310,9 @@ class mjCModel : public mjCModel_, private mjSpec {
// get the spec from which this model was created
mjSpec* GetSourceSpec() const;
// set deepcopy flag
void SetDeepCopy(bool deepcopy) { deepcopy_ = deepcopy; }
private:
// settings for each defaults class
std::vector<mjCDef*> defaults_;
@@ -351,7 +357,7 @@ class mjCModel : public mjCModel_, private mjSpec {
std::vector<mjCTuple*> tuples_; // list of tuple fields
std::vector<mjCKey*> keys_; // list of keyframe fields
std::vector<mjCPlugin*> plugins_; // list of plugin instances
std::vector<mjSpec*> specs_; // list of specs
std::vector<mjSpec*> specs_; // list of attached specs
// pointers to objects created inside kinematic tree
std::vector<mjCBody*> bodies_; // list of bodies
@@ -410,5 +416,6 @@ class mjCModel : public mjCModel_, private mjSpec {
mjListKeyMap ids; // map from object names to ids
mjCError errInfo; // last error info
std::vector<mjKeyInfo> key_pending_; // attached keyframes
bool deepcopy_; // copy objects when attaching
};
#endif // MUJOCO_SRC_USER_USER_MODEL_H_
+136 -72
View File
@@ -763,6 +763,7 @@ mjCBody::mjCBody(mjCModel* _model) {
model = _model;
if (_model) compiler = &_model->spec.compiler;
refcount = 1;
mjs_defaultBody(&spec);
elemtype = mjOBJ_BODY;
parent = nullptr;
@@ -872,22 +873,28 @@ mjCBody& mjCBody::operator+=(const mjCFrame& other) {
other.model->prefix = other.prefix;
other.model->suffix = other.suffix;
other.model->StoreKeyframes(model);
mjCModel* other_model = other.model;
// attach defaults
if (other.model != model) {
mjCDef* subdef = new mjCDef(*other.model->Default());
subdef->NameSpace(other.model);
if (other_model != model) {
mjCDef* subdef = new mjCDef(*other_model->Default());
subdef->NameSpace(other_model);
*model += *subdef;
}
// copy input frame
mjSpec* origin = model->FindSpec(other.compiler);
frames.push_back(new mjCFrame(other));
mjCFrame* newframe(model->deepcopy_ ? new mjCFrame(other) : (mjCFrame*)&other);
frames.push_back(newframe);
frames.back()->body = this;
frames.back()->model = model;
frames.back()->compiler = origin ? &origin->compiler : &model->spec.compiler;
frames.back()->frame = other.frame;
frames.back()->NameSpace(other.model);
if (model->deepcopy_) {
frames.back()->NameSpace(other_model);
} else {
frames.back()->AddRef();
}
int i = frames.size();
last_attached = &frames.back()->spec;
@@ -909,30 +916,43 @@ mjCBody& mjCBody::operator+=(const mjCFrame& other) {
CopyList(cameras, subtree->cameras, fmap, &other);
CopyList(lights, subtree->lights, fmap, &other);
if (!model->deepcopy_) {
subtree->SetModel(model);
subtree->NameSpace(other_model);
}
int nbodies = (int)subtree->bodies.size();
for (int i=0; i<nbodies; i++) {
if (!other.IsAncestor(subtree->bodies[i]->frame)) {
continue;
}
bodies.push_back(new mjCBody(*subtree->bodies[i], model)); // triggers recursive call
if (model->deepcopy_) {
mjCBody* newbody(new mjCBody(*subtree->bodies[i], model)); // triggers recursive call
bodies.push_back(newbody);
subtree->bodies[i]->ForgetKeyframes();
bodies.back()->NameSpace_(other_model, /*propagate=*/ false);
} else {
bodies.push_back(subtree->bodies[i]);
bodies.back()->SetModel(model);
bodies.back()->ResetId();
bodies.back()->AddRef();
}
bodies.back()->parent = this;
bodies.back()->frame =
subtree->bodies[i]->frame ? frames[fmap[subtree->bodies[i]->frame]] : nullptr;
bodies.back()->NameSpace_(other.model, /*propagate=*/ false);
subtree->bodies[i]->ForgetKeyframes();
}
// attach referencing elements
*model += *other.model;
*model += *other_model;
// leave the source model in a clean state
if (other.model != model) {
other.model->key_pending_.clear();
if (other_model != model) {
other_model->key_pending_.clear();
}
// clear namespace and return body
other.model->prefix.clear();
other.model->suffix.clear();
other_model->prefix.clear();
other_model->suffix.clear();
return *this;
}
@@ -948,7 +968,8 @@ void mjCBody::CopyList(std::vector<T*>& dst, const std::vector<T*>& src,
continue; // skip if the element is not inside pframe
}
mjSpec* origin = model->FindSpec(src[i]->compiler);
dst.push_back(new T(*src[i]));
T* new_obj = model->deepcopy_ ? new T(*src[i]) : src[i];
dst.push_back(new_obj);
dst.back()->body = this;
dst.back()->model = model;
dst.back()->compiler = origin ? &origin->compiler : &model->spec.compiler;
@@ -956,6 +977,11 @@ void mjCBody::CopyList(std::vector<T*>& dst, const std::vector<T*>& src,
dst.back()->CopyPlugin();
dst.back()->classname = src[i]->classname;
// increment refcount if shallow copy is made
if (!model->deepcopy_) {
dst.back()->AddRef();
}
// assign dst frame to src frame
dst.back()->frame = src[i]->frame ? frames[fmap[src[i]->frame]] : nullptr;
@@ -981,6 +1007,73 @@ mjCBody& mjCBody::operator-=(const mjCBody& subtree) {
// set model of this body and its subtree
void mjCBody::SetModel(mjCModel* _model) {
model = _model;
mjSpec* origin = model->FindSpec(mjs_getString(model->spec.modelname));
compiler = origin ? &origin->compiler : &model->spec.compiler;
for (auto& body : bodies) {
body->SetModel(_model);
}
for (auto& frame : frames) {
frame->model = _model;
frame->compiler = compiler;
}
for (auto& geom : geoms) {
geom->model = _model;
geom->compiler = compiler;
}
for (auto& joint : joints) {
joint->model = _model;
joint->compiler = compiler;
}
for (auto& site : sites) {
site->model = _model;
site->compiler = compiler;
}
for (auto& camera : cameras) {
camera->model = _model;
camera->compiler = compiler;
}
for (auto& light : lights) {
light->model = _model;
light->compiler = compiler;
}
}
// reset ids of all objects in this body
void mjCBody::ResetId() {
id = -1;
for (auto& body : bodies) {
body->ResetId();
}
for (auto& frame : frames) {
frame->id = -1;
}
for (auto& geom : geoms) {
geom->id = -1;
}
for (auto& joint : joints) {
joint->id = -1;
joint->qposadr_ = -1;
joint->dofadr_ = -1;
}
for (auto& site : sites) {
site->id = -1;
}
for (auto& camera : cameras) {
camera->id = -1;
}
for (auto& light : lights) {
light->id = -1;
}
}
void mjCBody::PointToLocal() {
spec.element = static_cast<mjsElement*>(this);
spec.name = &name;
@@ -1012,26 +1105,13 @@ void mjCBody::CopyPlugin() {
// destructor
mjCBody::~mjCBody() {
// delete objects allocated here
for (int i=0; i<bodies.size(); i++) delete bodies[i];
for (int i=0; i<geoms.size(); i++) delete geoms[i];
for (int i=0; i<frames.size(); i++) delete frames[i];
for (int i=0; i<joints.size(); i++) delete joints[i];
for (int i=0; i<sites.size(); i++) delete sites[i];
for (int i=0; i<cameras.size(); i++) delete cameras[i];
for (int i=0; i<lights.size(); i++) delete lights[i];
bodies.clear();
geoms.clear();
frames.clear();
joints.clear();
sites.clear();
cameras.clear();
lights.clear();
if (spec.plugin.active && spec.plugin.name->empty() && model) {
model->DeleteElement(spec.plugin.element);
}
for (int i=0; i<bodies.size(); i++) bodies[i]->Release();
for (int i=0; i<geoms.size(); i++) geoms[i]->Release();
for (int i=0; i<frames.size(); i++) frames[i]->Release();
for (int i=0; i<joints.size(); i++) joints[i]->Release();
for (int i=0; i<sites.size(); i++) sites[i]->Release();
for (int i=0; i<cameras.size(); i++) cameras[i]->Release();
for (int i=0; i<lights.size(); i++) lights[i]->Release();
}
@@ -1840,19 +1920,27 @@ mjCFrame& mjCFrame::operator+=(const mjCBody& other) {
other.model->StoreKeyframes(model);
other.model->prefix = "";
other.model->suffix = "";
mjCModel* other_model = other.model;
mjCBody* subtree = new mjCBody(other, model);
other.ForgetKeyframes();
other.model->prefix = subtree->prefix;
other.model->suffix = subtree->suffix;
// attach or copy the subtree
mjCBody* subtree = model->deepcopy_ ? new mjCBody(other, model) : (mjCBody*)&other;
if (model->deepcopy_) {
other.ForgetKeyframes();
} else {
subtree->SetModel(model);
subtree->ResetId();
subtree->AddRef();
}
other_model->prefix = subtree->prefix;
other_model->suffix = subtree->suffix;
subtree->SetParent(body);
subtree->SetFrame(this);
subtree->NameSpace(other.model);
subtree->NameSpace(other_model);
// attach defaults
if (other.model != model) {
mjCDef* subdef = new mjCDef(*other.model->Default());
subdef->NameSpace(other.model);
if (other_model != model) {
mjCDef* subdef = new mjCDef(*other_model->Default());
subdef->NameSpace(other_model);
*model += *subdef;
}
@@ -1861,16 +1949,16 @@ mjCFrame& mjCFrame::operator+=(const mjCBody& other) {
last_attached = &body->bodies.back()->spec;
// attach referencing elements
*model += *other.model;
*model += *other_model;
// leave the source model in a clean state
if (other.model != model) {
other.model->key_pending_.clear();
if (other_model != model) {
other_model->key_pending_.clear();
}
// clear suffixes and return
other.model->suffix.clear();
other.model->prefix.clear();
other_model->suffix.clear();
other_model->prefix.clear();
return *this;
}
@@ -2221,14 +2309,6 @@ mjCGeom::mjCGeom(const mjCGeom& other) {
mjCGeom::~mjCGeom() {
if (spec.plugin.active && spec.plugin.name->empty() && model) {
model->DeleteElement(spec.plugin.element);
}
}
mjCGeom& mjCGeom::operator=(const mjCGeom& other) {
if (this != &other) {
this->spec = other.spec;
@@ -4591,7 +4671,7 @@ void mjCMaterial::CopyFromSpec() {
void mjCMaterial::NameSpace(const mjCModel* m) {
mjCBase::NameSpace(m);
for (int i=0; i<mjNTEXROLE; i++) {
if (!spec_textures_[i].empty() && model != m) {
if (!spec_textures_[i].empty()) {
spec_textures_[i] = m->prefix + spec_textures_[i] + m->suffix;
}
}
@@ -5657,14 +5737,6 @@ mjCActuator::mjCActuator(const mjCActuator& other) {
mjCActuator::~mjCActuator() {
if (spec.plugin.active && spec.plugin.name->empty() && model) {
model->DeleteElement(spec.plugin.element);
}
}
mjCActuator& mjCActuator::operator=(const mjCActuator& other) {
if (this != &other) {
this->spec = other.spec;
@@ -6033,14 +6105,6 @@ mjCSensor::mjCSensor(const mjCSensor& other) {
mjCSensor::~mjCSensor() {
if (spec.plugin.active && spec.plugin.name->empty() && model) {
model->DeleteElement(spec.plugin.element);
}
}
mjCSensor& mjCSensor::operator=(const mjCSensor& other) {
if (this != &other) {
this->spec = other.spec;
+23 -3
View File
@@ -224,9 +224,23 @@ class mjCBase : public mjCBase_ {
virtual void ForgetKeyframes() {}
virtual void ForgetKeyframes() const {}
// increment and decrement reference count
// release uses the argument to delete the plugin
// which may be still owned by the source spec during shallow attach
virtual void AddRef() { ++refcount; }
virtual int GetRef() { return refcount; }
virtual void Release() {
if (--refcount == 0) {
delete this;
}
}
protected:
mjCBase(); // constructor
mjCBase(const mjCBase& other); // copy constructor
// reference count for allowing deleting an attached object
int refcount = 1;
};
@@ -349,6 +363,15 @@ class mjCBody : public mjCBody_, private mjsBody {
void SetParent(mjCBody* _body) { parent = _body; }
mjCBody* GetParent() const { return parent; }
// set model of this body
void SetModel(mjCModel* _model);
// reset ids of all objects in this body
void ResetId();
// getters
std::vector<mjCBody*> Bodies() const { return bodies; }
private:
mjCBody(const mjCBody& other, mjCModel* _model); // copy constructor
mjCBody& operator=(const mjCBody& other); // copy assignment
@@ -537,7 +560,6 @@ class mjCGeom : public mjCGeom_, private mjsGeom {
mjCGeom(mjCModel* = nullptr, mjCDef* = nullptr);
mjCGeom(const mjCGeom& other);
mjCGeom& operator=(const mjCGeom& other);
~mjCGeom();
using mjCBase::name;
mjsGeom spec; // variables set by user
@@ -1505,7 +1527,6 @@ class mjCActuator : public mjCActuator_, private mjsActuator {
mjCActuator(mjCModel* = nullptr, mjCDef* = nullptr);
mjCActuator(const mjCActuator& other);
mjCActuator& operator=(const mjCActuator& other);
~mjCActuator();
mjsActuator spec;
using mjCBase::name;
@@ -1567,7 +1588,6 @@ class mjCSensor : public mjCSensor_, private mjsSensor {
mjCSensor(mjCModel*);
mjCSensor(const mjCSensor& other);
mjCSensor& operator=(const mjCSensor& other);
~mjCSensor();
mjsSensor spec;
using mjCBase::name;
+6
View File
@@ -945,10 +945,16 @@ void mjXReader::Parse(XMLElement* root, const mjVFS* vfs) {
Keyframe(section);
}
// set deepcopy flag to true to copy child specs during attach calls
mjs_setDeepCopy(spec, true);
for (XMLElement* section = FirstChildElement(root, "worldbody"); section;
section = NextSiblingElement(section, "worldbody")) {
Body(section, mjs_findBody(spec, "world"), nullptr, vfs);
}
// set deepcopy flag to false to disable copying during attach in all future calls
mjs_setDeepCopy(spec, false);
}