diff --git a/src/user/user_api.cc b/src/user/user_api.cc index 18055a80..41a86adb 100644 --- a/src/user/user_api.cc +++ b/src/user/user_api.cc @@ -28,12 +28,44 @@ // create model mjmModel* mjm_createModel() { - mjCModel* modelC = new mjCModel(); + mjCModel* modelC = new mjCModel; return &modelC->spec; } +// copy back model +void mjm_copyBack(mjmModel* model, const mjModel* m) { + mjCModel* modelC = reinterpret_cast(model->element); + modelC->CopyBack(m); +} + + + +// compile model +mjModel* mjm_compileModel(mjmModel* model, const mjVFS* vfs) { + mjCModel* modelC = reinterpret_cast(model->element); + return modelC->Compile(vfs); +} + + + +// get error message from model +const char* mjm_getError(mjmModel* model) { + mjCModel* modelC = reinterpret_cast(model->element); + return modelC->GetError().message; +} + + + +// check if model has warnings +int mjm_isWarning(mjmModel* model) { + mjCModel* modelC = reinterpret_cast(model->element); + return modelC->GetError().warning; +} + + + // delete model void mjm_deleteModel(mjmModel* modelspec) { mjCModel* model = reinterpret_cast(modelspec->element); @@ -320,8 +352,9 @@ mjmPlugin* mjm_addPlugin(mjmModel* model) { // add default to model -mjmDefault* mjm_addDefault(mjmModel* model, const char* classname, int parentid) { +mjmDefault* mjm_addDefault(mjmModel* model, const char* classname, int parentid, int* id) { mjCModel* modelC = reinterpret_cast(model->element); + *id = (int)modelC->defaults.size(); mjCDef* def = modelC->AddDef(classname, parentid); if (def) { return &def->spec; @@ -346,10 +379,10 @@ mjmDefault* mjm_getDefault(mjElement element) { -// find default in model by class name -mjmDefault* mjm_findDefault(mjmModel* modelspec, const char* classname) { - mjCModel* model = reinterpret_cast(modelspec->element); - mjCDef* cdef = model->FindDef(classname); +// Find default with given name in model. +mjmDefault* mjm_findDefault(mjmModel* model, const char* classname) { + mjCModel* modelC = reinterpret_cast(model->element); + mjCDef* cdef = modelC->FindDef(classname); if (!cdef) { return nullptr; } @@ -358,6 +391,18 @@ mjmDefault* mjm_findDefault(mjmModel* modelspec, const char* classname) { +// get default[0] from model +mjmDefault* mjm_getModelDefault(mjmModel* model) { + mjCModel* modelC = reinterpret_cast(model->element); + mjCDef* def = modelC->defaults[0]; + if (!def) { + return nullptr; + } + return &def->spec; +} + + + // find body in model by name mjmBody* mjm_findBody(mjmModel* modelspec, const char* name) { mjCModel* model = reinterpret_cast(modelspec->element); @@ -539,6 +584,16 @@ void mjm_setPluginAttributes(mjmPlugin* plugin, void* attributes) { +// Set active plugins. +void mjm_setActivePlugins(mjmModel* model, void* activeplugins) { + mjCModel* modelC = reinterpret_cast(model->element); + std::vector>* active_plugins = + reinterpret_cast>*>(activeplugins); + modelC->active_plugins = std::move(*active_plugins); +} + + + // compute full inertia const char* mjm_setFullInertia(mjmBody* bodyspec, double quat[4], double inertia[3]) { mjCBody* body = reinterpret_cast(bodyspec->element); diff --git a/src/user/user_api.h b/src/user/user_api.h index 81484e91..18aa2cb1 100644 --- a/src/user/user_api.h +++ b/src/user/user_api.h @@ -729,6 +729,18 @@ typedef struct _mjmDefault { // default specification // Create model. MJAPI mjmModel* mjm_createModel(); +// Copy back model. +MJAPI void mjm_copyBack(mjmModel* model, const mjModel* m); + +// Compile model. +MJAPI mjModel* mjm_compileModel(mjmModel* model, const mjVFS* vfs); + +// Get error message from model. +MJAPI const char* mjm_getError(mjmModel* model); + +// Return 1 if model has warnings. +MJAPI int mjm_isWarning(mjmModel* model); + // Delete model. MJAPI void mjm_deleteModel(mjmModel* modelspec); @@ -820,7 +832,7 @@ MJAPI mjmKey* mjm_addKey(mjmModel* model); MJAPI mjmPlugin* mjm_addPlugin(mjmModel* model); // Add default to model. -MJAPI mjmDefault* mjm_addDefault(mjmModel* model, const char* classname, int parentid); +MJAPI mjmDefault* mjm_addDefault(mjmModel* model, const char* classname, int parentid, int* id); // Get model from body. MJAPI mjmModel* mjm_getModel(mjmBody* body); @@ -831,6 +843,9 @@ MJAPI mjmDefault* mjm_getDefault(mjElement element); // Find default in model by class name. MJAPI mjmDefault* mjm_findDefault(mjmModel* model, const char* classname); +// Get global default from model. +MJAPI mjmDefault* mjm_getModelDefault(mjmModel* model); + // Find body in model by name. MJAPI mjmBody* mjm_findBody(mjmModel* model, const char* name); @@ -879,6 +894,9 @@ MJAPI const double* mjm_getDouble(mjDoubleVec source, int* size); // Set plugin attributes. MJAPI void mjm_setPluginAttributes(mjmPlugin* plugin, void* attributes); +// Set active plugins. +MJAPI void mjm_setActivePlugins(mjmModel* model, void* activeplugins); + // Set default. MJAPI void mjm_setDefault(mjElement element, mjmDefault* def); diff --git a/src/user/user_composite.cc b/src/user/user_composite.cc index efecce9c..ad48db64 100644 --- a/src/user/user_composite.cc +++ b/src/user/user_composite.cc @@ -223,7 +223,9 @@ void mjCComposite::SetDefault(void) { // make composite object -bool mjCComposite::Make(mjCModel* model, mjmBody* body, char* error, int error_sz) { +bool mjCComposite::Make(mjmModel* modelspec, mjmBody* body, char* error, int error_sz) { + mjCModel* model = (mjCModel*)modelspec->element; + // check geom type if ((def[0].spec.geom->type!=mjGEOM_SPHERE && def[0].spec.geom->type!=mjGEOM_CAPSULE && diff --git a/src/user/user_composite.h b/src/user/user_composite.h index 34f88fa1..e2f4c350 100644 --- a/src/user/user_composite.h +++ b/src/user/user_composite.h @@ -69,7 +69,7 @@ class mjCComposite { bool AddDefaultJoint(char* error = NULL, int error_sz = 0); void AdjustSoft(mjtNum* solref, mjtNum* solimp, int level); - bool Make(mjCModel* model, mjmBody* body, char* error, int error_sz); + bool Make(mjmModel* modelspec, mjmBody* body, char* error, int error_sz); bool MakeParticle(mjCModel* model, mjmBody* body, char* error, int error_sz); bool MakeGrid(mjCModel* model, mjmBody* body, char* error, int error_sz); diff --git a/src/user/user_flexcomp.cc b/src/user/user_flexcomp.cc index 94fb3b25..641631b1 100644 --- a/src/user/user_flexcomp.cc +++ b/src/user/user_flexcomp.cc @@ -82,7 +82,8 @@ mjCFlexcomp::mjCFlexcomp(void) { // make flexcomp object -bool mjCFlexcomp::Make(mjCModel* model, mjmBody* body, char* error, int error_sz) { +bool mjCFlexcomp::Make(mjmModel* modelspec, mjmBody* body, char* error, int error_sz) { + mjCModel* model = (mjCModel*)modelspec->element; mjmFlex* dflex = def.spec.flex; int dim = dflex->dim; bool radial = (type==mjFCOMPTYPE_BOX || diff --git a/src/user/user_flexcomp.h b/src/user/user_flexcomp.h index 896a55a5..c8f07a4a 100644 --- a/src/user/user_flexcomp.h +++ b/src/user/user_flexcomp.h @@ -42,7 +42,7 @@ typedef enum _mjtFcompType { class mjCFlexcomp { public: mjCFlexcomp(void); - bool Make(mjCModel* model, mjmBody* body, char* error, int error_sz); + bool Make(mjmModel* modelspec, mjmBody* body, char* error, int error_sz); bool MakeGrid(char* error, int error_sz); bool MakeBox(char* error, int error_sz); diff --git a/src/user/user_model.cc b/src/user/user_model.cc index 8b32bc37..9f954991 100644 --- a/src/user/user_model.cc +++ b/src/user/user_model.cc @@ -175,6 +175,13 @@ mjCModel::mjCModel() { +mjCModel::mjCModel(const mjCModel& other) { + *this = other; + PointToLocal(); +} + + + void mjCModel::PointToLocal() { spec.element = (mjElement)this; spec.comment = (mjString)&spec_comment_; @@ -504,8 +511,8 @@ int mjCModel::GetFixed(void) { -// copy of error object -mjCError mjCModel::GetError(void) { +// get reference of error object +const mjCError& mjCModel::GetError(void) { return errInfo; } diff --git a/src/user/user_model.h b/src/user/user_model.h index 730c9dd1..c23cb38d 100644 --- a/src/user/user_model.h +++ b/src/user/user_model.h @@ -61,6 +61,7 @@ class mjCModel : private mjmModel { public: mjCModel(); + mjCModel(const mjCModel& other); ~mjCModel(); void CopyFromSpec(); // copy spec to private attributes void PointToLocal(); @@ -105,7 +106,7 @@ class mjCModel : private mjmModel { // API for access to other variables bool IsCompiled(); // is model already compiled int GetFixed(); // number of fixed massless bodies - mjCError GetError(); // copy of error object + const mjCError& GetError(void); // get reference of error object mjCBody* GetWorld(); // pointer to world body mjCDef* FindDef(std::string name); // find default class name mjCDef* AddDef(std::string name, int parentid); // add default class to array diff --git a/src/xml/xml.cc b/src/xml/xml.cc index 95b7f124..f5dd73fb 100644 --- a/src/xml/xml.cc +++ b/src/xml/xml.cc @@ -35,7 +35,7 @@ #include "engine/engine_crossplatform.h" #include "engine/engine_resource.h" #include "engine/engine_vfs.h" -#include "user/user_model.h" +#include "user/user_api.h" #include "user/user_util.h" #include "xml/xml_native_reader.h" #include "xml/xml_native_writer.h" @@ -99,7 +99,7 @@ class LocaleOverride { } // namespace // Main writer function - calls mjXWrite -std::string mjWriteXML(mjCModel* model, char* error, int error_sz) { +std::string mjWriteXML(mjmModel* model, char* error, int error_sz) { LocaleOverride locale_override; // check for empty model @@ -272,7 +272,7 @@ static void mjIncludeXML(mjXReader& reader, XMLElement* elem, // Main parser function -mjCModel* mjParseXML(const char* filename, const mjVFS* vfs, +mjmModel* mjParseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz) { LocaleOverride locale_override; @@ -285,7 +285,7 @@ mjCModel* mjParseXML(const char* filename, const mjVFS* vfs, } // clear - mjCModel* model = 0; + mjmModel* model = nullptr; if (error) { error[0] = '\0'; } @@ -344,14 +344,14 @@ mjCModel* mjParseXML(const char* filename, const mjVFS* vfs, } // create model, set filedir - model = new mjCModel; + model = mjm_createModel(); const char* dir; int ndir = 0; mju_getResourceDir(resource, &dir, &ndir); if (dir != nullptr) { - mjm_setString(model->spec.modelfiledir, std::string(dir, ndir).c_str()); + mjm_setString(model->modelfiledir, std::string(dir, ndir).c_str()); } else { - mjm_setString(model->spec.modelfiledir, ""); + mjm_setString(model->modelfiledir, ""); } // close resource @@ -363,8 +363,8 @@ mjCModel* mjParseXML(const char* filename, const mjVFS* vfs, // find include elements, replace them with subtree from xml file std::unordered_set included = {filename}; mjXReader parser; - parser.SetModelFileDir(mjm_getString(model->spec.modelfiledir)); - mjIncludeXML(parser, root, mjm_getString(model->spec.modelfiledir), vfs, included); + parser.SetModelFileDir(mjm_getString(model->modelfiledir)); + mjIncludeXML(parser, root, mjm_getString(model->modelfiledir), vfs, included); // parse MuJoCo model parser.SetModel(model); @@ -377,9 +377,9 @@ mjCModel* mjParseXML(const char* filename, const mjVFS* vfs, // set reasonable default for parsing a URDF // this is separate from the Parser to allow multiple URDFs to be loaded. - model->spec.strippath = true; - model->spec.fusestatic = true; - model->spec.discardvisual = true; + model->strippath = true; + model->fusestatic = true; + model->discardvisual = true; parser.SetModel(model); parser.Parse(root); @@ -393,7 +393,7 @@ mjCModel* mjParseXML(const char* filename, const mjVFS* vfs, // catch known errors catch (mjXError err) { mjCopyError(error, err.message, error_sz); - delete model; + mjm_deleteModel(model); return nullptr; } diff --git a/src/xml/xml.h b/src/xml/xml.h index 84ef9ae3..3afb5aab 100644 --- a/src/xml/xml.h +++ b/src/xml/xml.h @@ -18,15 +18,15 @@ #include #include -#include "user/user_model.h" +#include "user/user_api.h" // Top level API // Main writer function -std::string mjWriteXML(mjCModel* model, char* error, int error_sz); +std::string mjWriteXML(mjmModel* model, char* error, int error_sz); // Main parser function -mjCModel* mjParseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz); +mjmModel* mjParseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz); #endif // MUJOCO_SRC_XML_XML_H_ diff --git a/src/xml/xml_api.cc b/src/xml/xml_api.cc index 85925df6..8cc68422 100644 --- a/src/xml/xml_api.cc +++ b/src/xml/xml_api.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -25,7 +26,7 @@ #include #include -#include "user/user_model.h" +#include "user/user_api.h" #include "xml/xml.h" #include "xml/xml_native_reader.h" #include "xml/xml_util.h" @@ -36,7 +37,7 @@ class GlobalModel { public: // deletes current model and takes ownership of model - void Set(mjCModel* model = nullptr); + void Set(mjmModel* model = nullptr); // writes XML to string std::optional ToXML(const mjModel* m, char* error, @@ -45,7 +46,7 @@ class GlobalModel { private: // using raw pointers as GlobalModel needs to be trivially destructible std::mutex* mutex_ = new std::mutex(); - mjCModel* model_ = nullptr; + mjmModel* model_ = nullptr; }; std::optional GlobalModel::ToXML(const mjModel* m, char* error, @@ -55,7 +56,7 @@ std::optional GlobalModel::ToXML(const mjModel* m, char* error, mjCopyError(error, "No XML model loaded", error_sz); return std::nullopt; } - model_->CopyBack(m); + mjm_copyBack(model_, m); std::string result = mjWriteXML(model_, error, error_sz); if (result.empty()) { return std::nullopt; @@ -63,10 +64,10 @@ std::optional GlobalModel::ToXML(const mjModel* m, char* error, return result; } -void GlobalModel::Set(mjCModel* model) { +void GlobalModel::Set(mjmModel* model) { std::lock_guard lock(*mutex_); if (model_ != nullptr) { - delete model_; + mjm_deleteModel(model_); } model_ = model; } @@ -90,21 +91,23 @@ mjModel* mj_loadXML(const char* filename, const mjVFS* vfs, char* error, int error_sz) { // parse new model - std::unique_ptr model(mjParseXML(filename, vfs, error, error_sz)); + std::unique_ptr> model( + mjParseXML(filename, vfs, error, error_sz), + [](mjmModel* m) { mjm_deleteModel(m); }); if (!model) { return nullptr; } // compile new model - mjModel* m = model->Compile(vfs); + mjModel* m = mjm_compileModel(model.get(), vfs); if (!m) { - mjCopyError(error, model->GetError().message, error_sz); + mjCopyError(error, mjm_getError(model.get()), error_sz); return nullptr; } // handle compile warning - if (model->GetError().warning) { - mjCopyError(error, model->GetError().message, error_sz); + if (mjm_isWarning(model.get())) { + mjCopyError(error, mjm_getError(model.get()), error_sz); } else if (error) { error[0] = '\0'; } diff --git a/src/xml/xml_base.cc b/src/xml/xml_base.cc index 2fd255bb..ae21fd8d 100644 --- a/src/xml/xml_base.cc +++ b/src/xml/xml_base.cc @@ -44,7 +44,7 @@ mjXBase::mjXBase() { // set model field -void mjXBase::SetModel(mjCModel* _model) { +void mjXBase::SetModel(mjmModel* _model) { model = _model; } diff --git a/src/xml/xml_base.h b/src/xml/xml_base.h index 7ac718dc..bf9d9f3f 100644 --- a/src/xml/xml_base.h +++ b/src/xml/xml_base.h @@ -19,9 +19,7 @@ #include #include "tinyxml2.h" -#include -#include "user/user_model.h" -#include "user/user_objects.h" +#include "user/user_api.h" #include "xml/xml_util.h" @@ -89,13 +87,13 @@ class mjXBase : public mjXUtil { }; // set the model allocated externally - void SetModel(mjCModel*); + virtual void SetModel(mjmModel*); // read alternative orientation specification static int ReadAlternative(tinyxml2::XMLElement* elem, mjmOrientation& alt); protected: - mjCModel* model; // internally-allocated mjCModel object + mjmModel* model; // internally-allocated model }; #endif // MUJOCO_SRC_XML_XML_BASE_H_ diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc index 774df796..e87c95be 100644 --- a/src/xml/xml_native_reader.cc +++ b/src/xml/xml_native_reader.cc @@ -808,31 +808,31 @@ void mjXReader::Parse(XMLElement* root) { // get model name string modelname; if (ReadAttrTxt(root, "model", modelname)) { - mjm_setString(model->spec.modelname, modelname.c_str()); + mjm_setString(model->modelname, modelname.c_str()); } // get comment if (root->FirstChild() && root->FirstChild()->ToComment()) { - mjm_setString(model->spec.comment, root->FirstChild()->Value()); + mjm_setString(model->comment, root->FirstChild()->Value()); } else { - mjm_setString(model->spec.comment, ""); + mjm_setString(model->comment, ""); } //------------------- parse MuJoCo sections embedded in all XML formats for (XMLElement* section = FirstChildElement(root, "compiler"); section; section = NextSiblingElement(section, "compiler")) { - Compiler(section, &model->spec); + Compiler(section, model); } for (XMLElement* section = FirstChildElement(root, "option"); section; section = NextSiblingElement(section, "option")) { - Option(section, &model->spec.option); + Option(section, &model->option); } for (XMLElement* section = FirstChildElement(root, "size"); section; section = NextSiblingElement(section, "size")) { - Size(section, &model->spec); + Size(section, model); } //------------------ parse MJCF-specific sections @@ -871,7 +871,7 @@ void mjXReader::Parse(XMLElement* root) { for (XMLElement* section = FirstChildElement(root, "worldbody"); section; section = NextSiblingElement(section, "worldbody")) { - Body(section, &model->GetWorld()->spec, nullptr); + Body(section, mjm_findBody(model, "world"), nullptr); } for (XMLElement* section = FirstChildElement(root, "contact"); section; @@ -1261,15 +1261,14 @@ void mjXReader::Statistic(XMLElement* section) { string text; // read statistics - mjmModel* pmodel = &model->spec; - ReadAttr(section, "meaninertia", 1, &pmodel->stat.meaninertia, text); - ReadAttr(section, "meanmass", 1, &pmodel->stat.meanmass, text); - ReadAttr(section, "meansize", 1, &pmodel->stat.meansize, text); - ReadAttr(section, "extent", 1, &pmodel->stat.extent, text); - if (mjuu_defined(pmodel->stat.extent) && pmodel->stat.extent<=0) { + ReadAttr(section, "meaninertia", 1, &model->stat.meaninertia, text); + ReadAttr(section, "meanmass", 1, &model->stat.meanmass, text); + ReadAttr(section, "meansize", 1, &model->stat.meansize, text); + ReadAttr(section, "extent", 1, &model->stat.extent, text); + if (mjuu_defined(model->stat.extent) && model->stat.extent<=0) { throw mjXError(section, "extent must be strictly positive"); } - ReadAttr(section, "center", 3, pmodel->stat.center, text); + ReadAttr(section, "center", 3, model->stat.center, text); } @@ -2617,7 +2616,7 @@ void mjXReader::OnePlugin(XMLElement* elem, mjmPlugin* plugin) { mjm_setString(plugin->name, name.c_str()); mjm_setString(plugin->instance_name, instance_name.c_str()); if (instance_name.empty()) { - plugin->instance = mjm_addPlugin(&model->spec)->instance; + plugin->instance = mjm_addPlugin(model)->instance; ReadPluginConfigs(elem, plugin); } else { model->hasImplicitPluginElem = true; @@ -2646,14 +2645,13 @@ void mjXReader::Default(XMLElement* section, int parentid) { } } if (parentid>=0) { - thisid = (int)model->defaults.size(); - def = mjm_addDefault(&model->spec, text.c_str(), parentid); + def = mjm_addDefault(model, text.c_str(), parentid, &thisid); if (!def) { throw mjXError(section, "repeated default class name"); } } else { thisid = 0; - def = &model->defaults[0]->spec; + def = mjm_getModelDefault(model); mjm_setString(def->name, text.c_str()); } @@ -2731,6 +2729,8 @@ void mjXReader::Default(XMLElement* section, int parentid) { // extension section parser void mjXReader::Extension(XMLElement* section) { XMLElement* elem = FirstChildElement(section); + std::vector> active_plugins; + while (elem) { // get sub-element name std::string_view name = elem->Value(); @@ -2745,14 +2745,14 @@ void mjXReader::Extension(XMLElement* section) { } bool already_declared = false; - for (const auto& [existing_plugin, existing_slot] : model->active_plugins) { + for (const auto& [existing_plugin, existing_slot] : active_plugins) { if (plugin == existing_plugin) { already_declared = true; break; } } if (!already_declared) { - model->active_plugins.emplace_back(std::make_pair(plugin, plugin_slot)); + active_plugins.emplace_back(std::make_pair(plugin, plugin_slot)); } XMLElement* child = FirstChildElement(elem); @@ -2763,7 +2763,7 @@ void mjXReader::Extension(XMLElement* section) { child, "explicit plugin instance must appear before implicit plugin elements"); } string name; - mjmPlugin* p = mjm_addPlugin(&model->spec); + mjmPlugin* p = mjm_addPlugin(model); mjm_setString(p->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); ReadAttrTxt(child, "name", name, /* required = */ true); mjm_setString(p->name, name.c_str()); @@ -2780,6 +2780,8 @@ void mjXReader::Extension(XMLElement* section) { // advance to next element elem = NextSiblingElement(elem); } + + mjm_setActivePlugins(model, &active_plugins); } @@ -2800,7 +2802,7 @@ void mjXReader::Custom(XMLElement* section) { // numeric if (name=="numeric") { // create custom - mjmNumeric* pnum = mjm_addNumeric(&model->spec); + mjmNumeric* pnum = mjm_addNumeric(model); // write error info mjm_setString(pnum->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); @@ -2831,7 +2833,7 @@ void mjXReader::Custom(XMLElement* section) { // text else if (name=="text") { // create custom - mjmText* pte = mjm_addText(&model->spec); + mjmText* pte = mjm_addText(model); // write error info mjm_setString(pte->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); @@ -2851,7 +2853,7 @@ void mjXReader::Custom(XMLElement* section) { // tuple else if (name=="tuple") { // create custom - mjmTuple* ptu = mjm_addTuple(&model->spec); + mjmTuple* ptu = mjm_addTuple(model); // write error info mjm_setString(ptu->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); @@ -2910,7 +2912,7 @@ void mjXReader::Custom(XMLElement* section) { void mjXReader::Visual(XMLElement* section) { string text, name; XMLElement* elem; - mjVisual* vis = &model->spec.visual; + mjVisual* vis = &model->visual; // iterate over child elements elem = FirstChildElement(section); @@ -3052,13 +3054,13 @@ void mjXReader::Asset(XMLElement* section) { // get class if specified, otherwise use default0 mjmDefault* def = GetClass(elem); if (!def) { - def = &model->defaults[0]->spec; + def = mjm_getModelDefault(model); } // texture sub-element if (name=="texture") { // create texture - mjmTexture* ptex = mjm_addTexture(&model->spec); + mjmTexture* ptex = mjm_addTexture(model); // write error info mjm_setString(ptex->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); @@ -3126,28 +3128,28 @@ void mjXReader::Asset(XMLElement* section) { // material sub-element else if (name=="material") { // create material and parse - mjmMaterial* pmat = mjm_addMaterial(&model->spec, def); + mjmMaterial* pmat = mjm_addMaterial(model, def); OneMaterial(elem, pmat); } // mesh sub-element else if (name=="mesh") { // create mesh and parse - mjmMesh* pmesh = mjm_addMesh(&model->spec, def); + mjmMesh* pmesh = mjm_addMesh(model, def); OneMesh(elem, pmesh); } // skin sub-element... deprecate ??? else if (name=="skin") { // create skin and parse - mjmSkin* pskin = mjm_addSkin(&model->spec); + mjmSkin* pskin = mjm_addSkin(model); OneSkin(elem, pskin); } // hfield sub-element else if (name=="hfield") { // create hfield - mjmHField* phf = mjm_addHField(&model->spec); + mjmHField* phf = mjm_addHField(model); // write error info mjm_setString(phf->info, ("line = " + std::to_string(elem->GetLineNum())).c_str()); @@ -3356,7 +3358,8 @@ void mjXReader::Body(XMLElement* section, mjmBody* pbody, mjmFrame* frame) { // read childdef mjmDefault* childdef = 0; if (ReadAttrTxt(elem, "childclass", text)) { - childdef = &model->FindDef(text)->spec; + childdef = mjm_findDefault(model, text.c_str()); + mjm_findDefault(model, text.c_str()); if (!childdef) { throw mjXError(elem, "unknown default childclass"); } @@ -3423,19 +3426,19 @@ void mjXReader::Contact(XMLElement* section) { // get class if specified, otherwise use default0 mjmDefault* def = GetClass(elem); if (!def) { - def = &model->defaults[0]->spec; + def = mjm_getModelDefault(model); } // geom pair to include if (name=="pair") { // create pair and parse - mjmPair* ppair = mjm_addPair(&model->spec, def); + mjmPair* ppair = mjm_addPair(model, def); OnePair(elem, ppair); } // body pair to exclude else if (name=="exclude") { - mjmExclude* pexclude = mjm_addExclude(&model->spec); + mjmExclude* pexclude = mjm_addExclude(model); string exname, exbody1, exbody2; // write error info @@ -3468,11 +3471,11 @@ void mjXReader::Equality(XMLElement* section) { // get class if specified, otherwise use default0 mjmDefault* def = GetClass(elem); if (!def) { - def = &model->defaults[0]->spec; + def = mjm_getModelDefault(model); } // create equality constraint and parse - mjmEquality* pequality = mjm_addEquality(&model->spec, def); + mjmEquality* pequality = mjm_addEquality(model, def); OneEquality(elem, pequality); // advance to next element @@ -3496,20 +3499,20 @@ void mjXReader::Deformable(XMLElement* section) { // get class if specified, otherwise use default0 mjmDefault* def = GetClass(elem); if (!def) { - def = &model->defaults[0]->spec; + def = mjm_getModelDefault(model); } // flex sub-element if (name=="flex") { // create flex and parse - mjmFlex* pflex = mjm_addFlex(&model->spec); + mjmFlex* pflex = mjm_addFlex(model); OneFlex(elem, pflex); } // skin sub-element else if (name=="skin") { // create skin and parse - mjmSkin* pskin = mjm_addSkin(&model->spec); + mjmSkin* pskin = mjm_addSkin(model); OneSkin(elem, pskin); } @@ -3532,11 +3535,11 @@ void mjXReader::Tendon(XMLElement* section) { // get class if specified, otherwise use default0 mjmDefault* def = GetClass(elem); if (!def) { - def = &model->defaults[0]->spec; + def = mjm_getModelDefault(model); } // create equality constraint and parse - mjmTendon* pten = mjm_addTendon(&model->spec, def); + mjmTendon* pten = mjm_addTendon(model, def); OneTendon(elem, pten); // process wrap sub-elements @@ -3598,11 +3601,11 @@ void mjXReader::Actuator(XMLElement* section) { // get class if specified, otherwise use default0 mjmDefault* def = GetClass(elem); if (!def) { - def = &model->defaults[0]->spec; + def = mjm_getModelDefault(model); } // create actuator and parse - mjmActuator* pact = mjm_addActuator(&model->spec, def); + mjmActuator* pact = mjm_addActuator(model, def); OneActuator(elem, pact); // advance to next element @@ -3618,7 +3621,7 @@ void mjXReader::Sensor(XMLElement* section) { XMLElement* elem = FirstChildElement(section); while (elem) { // create sensor, get string type - mjmSensor* psen = mjm_addSensor(&model->spec); + mjmSensor* psen = mjm_addSensor(model); string type = elem->Value(); string text, name, objname, refname; std::vector userdata; @@ -3935,7 +3938,7 @@ void mjXReader::Keyframe(XMLElement* section) { string text, name = ""; // add keyframe - mjmKey* pk = mjm_addKey(&model->spec); + mjmKey* pk = mjm_addKey(model); // read name, time ReadAttrTxt(elem, "name", name); @@ -3991,7 +3994,7 @@ mjmDefault* mjXReader::GetClass(XMLElement* section) { mjmDefault* def = nullptr; if (ReadAttrTxt(section, "class", text)) { - def = mjm_findDefault(&model->spec, text.c_str()); + def = mjm_findDefault(model, text.c_str()); if (!def) { throw mjXError( section, diff --git a/src/xml/xml_native_writer.cc b/src/xml/xml_native_writer.cc index 8f9b7ff2..edc12d83 100644 --- a/src/xml/xml_native_writer.cc +++ b/src/xml/xml_native_writer.cc @@ -764,6 +764,14 @@ mjXWriter::mjXWriter(void) { } +// cast model +void mjXWriter::SetModel(mjmModel* modelspec) { + if (modelspec) { + model = (mjCModel*)modelspec->element; + } +} + + // save existing model in MJCF canonical format, must be compiled string mjXWriter::Write(char *error, size_t error_sz) { // check model diff --git a/src/xml/xml_native_writer.h b/src/xml/xml_native_writer.h index 684a6e0a..8a05e0a6 100644 --- a/src/xml/xml_native_writer.h +++ b/src/xml/xml_native_writer.h @@ -18,6 +18,8 @@ #include #include +#include "user/user_api.h" +#include "user/user_objects.h" #include "xml/xml_base.h" #include "tinyxml2.h" @@ -25,6 +27,7 @@ class mjXWriter : public mjXBase { public: mjXWriter(); // constructor virtual ~mjXWriter() = default; // destructor + void SetModel(mjmModel* modelspec); // write XML document to string std::string Write(char *error, std::size_t error_sz); @@ -33,6 +36,9 @@ class mjXWriter : public mjXBase { // insert end child with given name, return child tinyxml2::XMLElement* InsertEnd(tinyxml2::XMLElement* parent, const char* name); + // compiled model + mjCModel* model = 0; + // XML section writers void Compiler(tinyxml2::XMLElement* root); // compiler section void Option(tinyxml2::XMLElement* root); // option section diff --git a/src/xml/xml_urdf.cc b/src/xml/xml_urdf.cc index 872b0155..23bd2647 100644 --- a/src/xml/xml_urdf.cc +++ b/src/xml/xml_urdf.cc @@ -20,6 +20,7 @@ #include #include "user/user_api.h" +#include "user/user_objects.h" #include "user/user_util.h" #include "xml/xml_native_reader.h" #include "xml/xml_urdf.h" @@ -93,25 +94,25 @@ void mjXURDF::Parse( if (mjc) { XMLElement *section; if ((section = FindSubElem(mjc, "compiler"))) { - mjXReader::Compiler(section, &model->spec); + mjXReader::Compiler(section, model); } if ((section = FindSubElem(mjc, "option"))) { - mjXReader::Option(section, &model->spec.option); + mjXReader::Option(section, &model->option); } if ((section = FindSubElem(mjc, "size"))) { - mjXReader::Size(section, &model->spec); + mjXReader::Size(section, model); } } // enforce required compiler defaults for URDF - model->spec.degree = false; + model->degree = false; // get model name std::string modelname; if (ReadAttrTxt(root, "name", modelname)) { - mjm_setString(model->spec.modelname, modelname.c_str()); + mjm_setString(model->modelname, modelname.c_str()); } // find and register all materials @@ -205,7 +206,7 @@ void mjXURDF::Parse( // override the pose for the base link and add a free joint for (int i = 0; i < (int)urName.size(); i++) { if (urParent[i] < 0) { - mjmBody* world = mjm_findBody(&model->spec, "world"); + mjmBody* world = mjm_findBody(model, "world"); mjmBody* pbody = mjm_findChild(world, urName[i].c_str()); mjuu_copyvec(pbody->pos, pos, 3); mjuu_copyvec(pbody->quat, quat, 4); @@ -231,7 +232,7 @@ void mjXURDF::Body(XMLElement* body_elem) { // get body name and pointer to mjmBody ReadAttrTxt(body_elem, "name", name, true); name = GetPrefixedName(name); - world = mjm_findBody(&model->spec, "world"); + world = mjm_findBody(model, "world"); pbody = mjm_findChild(world, name.c_str()); if (!pbody) { throw mjXError(body_elem, "URDF body not found"); // SHOULD NOT OCCUR @@ -313,7 +314,7 @@ void mjXURDF::Body(XMLElement* body_elem) { } } // create geom if not discarded - if (!model->spec.discardvisual) { + if (!model->discardvisual) { pgeom = Geom(elem, pbody, false); // save color @@ -389,7 +390,7 @@ void mjXURDF::Joint(XMLElement* joint_elem) { elem = FindSubElem(joint_elem, "parent", true); ReadAttrTxt(elem, "link", name, true); name = GetPrefixedName(name); - world = mjm_findBody(&model->spec, "world"); + world = mjm_findBody(model, "world"); parent = mjm_findChild(world, name.c_str()); if (!parent) { // SHOULD NOT OCCUR throw mjXError(elem, "invalid parent name in URDF joint definition"); @@ -399,7 +400,7 @@ void mjXURDF::Joint(XMLElement* joint_elem) { elem = FindSubElem(joint_elem, "child", true); ReadAttrTxt(elem, "link", name, true); name = GetPrefixedName(name); - world = mjm_findBody(&model->spec, "world"); + world = mjm_findBody(model, "world"); pbody = mjm_findChild(world, name.c_str()); if (!pbody) { // SHOULD NOT OCCUR throw mjXError(elem, "invalid child name in URDF joint definition"); @@ -566,7 +567,7 @@ mjmGeom* mjXURDF::Geom(XMLElement* geom_elem, mjmBody* pbody, bool collision) { .value_or(default_meshscale); // strip file name if necessary - if (model->spec.strippath) { + if (model->strippath) { meshfile = mjuu_strippath(meshfile); } @@ -575,19 +576,19 @@ mjmGeom* mjXURDF::Geom(XMLElement* geom_elem, mjmBody* pbody, bool collision) { meshname = mjuu_stripext(meshname); // look for existing mesh - mjmMesh* mesh = mjm_findMesh(&model->spec, meshname.c_str()); + mjmMesh* mesh = mjm_findMesh(model, meshname.c_str()); mjmMesh* pmesh = 0; // does not exist: create if (!mesh) { - pmesh = mjm_addMesh(&model->spec, 0); + pmesh = mjm_addMesh(model, 0); } // exists with different scale: append name with '1', create else if (mesh->scale[0]!=meshscale[0] || mesh->scale[1]!=meshscale[1] || mesh->scale[2]!=meshscale[2]) { - pmesh = mjm_addMesh(&model->spec, 0); + pmesh = mjm_addMesh(model, 0); meshname = meshname + "1"; } @@ -684,14 +685,14 @@ void mjXURDF::AddToTree(int n) { // get pointer to parent in mjCModel tree mjmBody *parent = 0, *child = 0, *world = 0; if (urParent[n]>=0) { - world = mjm_findBody(&model->spec, "world"); + world = mjm_findBody(model, "world"); parent = mjm_findChild(world, urName[urParent[n]].c_str()); if (!parent) throw mjXError(0, "URDF body parent should already be in tree: %s", urName[urParent[n]].c_str()); // SHOULD NOT OCCUR } else { - parent = &model->GetWorld()->spec; + parent = mjm_findBody(model, "world"); } // add this body diff --git a/src/xml/xml_urdf.h b/src/xml/xml_urdf.h index c47aedcd..79a76dd4 100644 --- a/src/xml/xml_urdf.h +++ b/src/xml/xml_urdf.h @@ -20,7 +20,6 @@ #include #include "user/user_api.h" -#include "user/user_model.h" #include "xml/xml_base.h" #include "tinyxml2.h"