Replace mjCModel with mjmModel in mjXBase.

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