Add mjm_attachBody to the C API.
PiperOrigin-RevId: 617207000 Change-Id: I1fe59ba0d79938a4a383b115997c31dd6c71b8e5
This commit is contained in:
committed by
Copybara-Service
parent
91192393a0
commit
cc85a38f3c
+49
-12
@@ -19,6 +19,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -28,6 +29,25 @@
|
|||||||
#include "user/user_cache.h"
|
#include "user/user_cache.h"
|
||||||
#include "xml/xml_util.h"
|
#include "xml/xml_util.h"
|
||||||
|
|
||||||
|
|
||||||
|
// prepend prefix
|
||||||
|
template <typename T>
|
||||||
|
static T& operator+(std::string_view prefix, T& base) {
|
||||||
|
base.prefix = std::string(prefix);
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// append suffix
|
||||||
|
template <typename T>
|
||||||
|
static T& operator+(T& base, std::string_view suffix) {
|
||||||
|
base.suffix = std::string(suffix);
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// create model
|
// create model
|
||||||
mjSpec* mjm_createSpec() {
|
mjSpec* mjm_createSpec() {
|
||||||
mjCModel* modelC = new mjCModel;
|
mjCModel* modelC = new mjCModel;
|
||||||
@@ -60,6 +80,17 @@ mjModel* mjm_compile(mjSpec* s, const mjVFS* vfs) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// attach body to a frame of the parent
|
||||||
|
int mjm_attachBody(mjmFrame* parent, const mjmBody* child,
|
||||||
|
const char* prefix, const char* suffix) {
|
||||||
|
mjCFrame* frame_parent = reinterpret_cast<mjCFrame*>(parent->element);
|
||||||
|
mjCBody* child_body = reinterpret_cast<mjCBody*>(child->element);
|
||||||
|
*frame_parent += std::string(prefix) + *child_body + std::string(suffix);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// get error message from model
|
// get error message from model
|
||||||
const char* mjm_getError(mjSpec* s) {
|
const char* mjm_getError(mjSpec* s) {
|
||||||
mjCModel* modelC = reinterpret_cast<mjCModel*>(s->element);
|
mjCModel* modelC = reinterpret_cast<mjCModel*>(s->element);
|
||||||
@@ -169,6 +200,7 @@ mjmFrame* mjm_addFrame(mjmBody* bodyspec, mjmFrame* parentframe) {
|
|||||||
}
|
}
|
||||||
mjCBody* body = reinterpret_cast<mjCBody*>(bodyspec->element);
|
mjCBody* body = reinterpret_cast<mjCBody*>(bodyspec->element);
|
||||||
mjCFrame* frameC = body->AddFrame(parentframeC);
|
mjCFrame* frameC = body->AddFrame(parentframeC);
|
||||||
|
frameC->SetParent(body);
|
||||||
return &frameC->spec;
|
return &frameC->spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,11 +448,13 @@ mjmDefault* mjm_getSpecDefault(mjSpec* s) {
|
|||||||
// find body in model by name
|
// find body in model by name
|
||||||
mjmBody* mjm_findBody(mjSpec* s, const char* name) {
|
mjmBody* mjm_findBody(mjSpec* s, const char* name) {
|
||||||
mjCModel* model = reinterpret_cast<mjCModel*>(s->element);
|
mjCModel* model = reinterpret_cast<mjCModel*>(s->element);
|
||||||
mjCBase* body = model->FindObject(mjOBJ_BODY, std::string(name));
|
mjCBase* body = 0;
|
||||||
if (!body) {
|
if (model->IsCompiled()) {
|
||||||
return 0;
|
body = model->FindObject(mjOBJ_BODY, std::string(name)); // fast lookup
|
||||||
|
} else {
|
||||||
|
body = model->FindBody(model->GetWorld(), std::string(name)); // recursive search
|
||||||
}
|
}
|
||||||
return &(static_cast<mjCBody*>(body)->spec);
|
return body ? &(static_cast<mjCBody*>(body)->spec) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -429,10 +463,7 @@ mjmBody* mjm_findBody(mjSpec* s, const char* name) {
|
|||||||
mjmBody* mjm_findChild(mjmBody* bodyspec, const char* name) {
|
mjmBody* mjm_findChild(mjmBody* bodyspec, const char* name) {
|
||||||
mjCBody* body = reinterpret_cast<mjCBody*>(bodyspec->element);
|
mjCBody* body = reinterpret_cast<mjCBody*>(bodyspec->element);
|
||||||
mjCBase* child = body->FindObject(mjOBJ_BODY, std::string(name));
|
mjCBase* child = body->FindObject(mjOBJ_BODY, std::string(name));
|
||||||
if (!child) {
|
return child ? &(static_cast<mjCBody*>(child)->spec) : nullptr;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return &(static_cast<mjCBody*>(child)->spec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -441,10 +472,16 @@ mjmBody* mjm_findChild(mjmBody* bodyspec, const char* name) {
|
|||||||
mjmMesh* mjm_findMesh(mjSpec* s, const char* name) {
|
mjmMesh* mjm_findMesh(mjSpec* s, const char* name) {
|
||||||
mjCModel* model = reinterpret_cast<mjCModel*>(s->element);
|
mjCModel* model = reinterpret_cast<mjCModel*>(s->element);
|
||||||
mjCMesh* mesh = (mjCMesh*)model->FindObject(mjOBJ_MESH, std::string(name));
|
mjCMesh* mesh = (mjCMesh*)model->FindObject(mjOBJ_MESH, std::string(name));
|
||||||
if (!mesh) {
|
return mesh ? &(static_cast<mjCMesh*>(mesh)->spec) : nullptr;
|
||||||
return nullptr;
|
}
|
||||||
}
|
|
||||||
return &(static_cast<mjCMesh*>(mesh)->spec);
|
|
||||||
|
|
||||||
|
// find frame by name
|
||||||
|
mjmFrame* mjm_findFrame(mjSpec* s, const char* name) {
|
||||||
|
mjCModel* model = reinterpret_cast<mjCModel*>(s->element);
|
||||||
|
mjCFrame* frame = (mjCFrame*)model->FindFrame(model->GetWorld(), std::string(name));
|
||||||
|
return frame ? &(static_cast<mjCFrame*>(frame)->spec) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -740,6 +740,10 @@ MJAPI void mjm_copyBack(mjSpec* s, const mjModel* m);
|
|||||||
// Compile model.
|
// Compile model.
|
||||||
MJAPI mjModel* mjm_compile(mjSpec* s, const mjVFS* vfs);
|
MJAPI mjModel* mjm_compile(mjSpec* s, const mjVFS* vfs);
|
||||||
|
|
||||||
|
// Attach child body to a frame of the parent, return 0 if success
|
||||||
|
MJAPI int mjm_attachBody(mjmFrame* parent, const mjmBody* child,
|
||||||
|
const char* prefix, const char* suffix);
|
||||||
|
|
||||||
// Get error message from model.
|
// Get error message from model.
|
||||||
MJAPI const char* mjm_getError(mjSpec* s);
|
MJAPI const char* mjm_getError(mjSpec* s);
|
||||||
|
|
||||||
@@ -860,6 +864,9 @@ MJAPI mjmBody* mjm_findChild(mjmBody* body, const char* name);
|
|||||||
// Find mesh by name.
|
// Find mesh by name.
|
||||||
MJAPI mjmMesh* mjm_findMesh(mjSpec* s, const char* name);
|
MJAPI mjmMesh* mjm_findMesh(mjSpec* s, const char* name);
|
||||||
|
|
||||||
|
// Find frame by name.
|
||||||
|
MJAPI mjmFrame* mjm_findFrame(mjSpec* s, const char* name);
|
||||||
|
|
||||||
// Get element id.
|
// Get element id.
|
||||||
MJAPI int mjm_getId(mjElement element);
|
MJAPI int mjm_getId(mjElement element);
|
||||||
|
|
||||||
|
|||||||
+59
-9
@@ -162,6 +162,26 @@ mjCModel& mjCModel::operator=(const mjCModel& other) {
|
|||||||
this->spec = other.spec;
|
this->spec = other.spec;
|
||||||
*static_cast<mjCModel_*>(this) = static_cast<const mjCModel_&>(other);
|
*static_cast<mjCModel_*>(this) = static_cast<const mjCModel_&>(other);
|
||||||
*static_cast<mjSpec*>(this) = static_cast<const mjSpec&>(other);
|
*static_cast<mjSpec*>(this) = static_cast<const mjSpec&>(other);
|
||||||
|
|
||||||
|
// the world copy constructor takes care of copying the tree
|
||||||
|
mjCBody* world = new mjCBody(*other.bodies[0], this);
|
||||||
|
bodies.push_back(world);
|
||||||
|
|
||||||
|
// add everything else
|
||||||
|
*this += other;
|
||||||
|
|
||||||
|
// copy name maps
|
||||||
|
for (int i=0; i<mjNOBJECT; i++) {
|
||||||
|
ids[i] = other.ids[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mjCModel& mjCModel::operator+=(const mjCModel& other) {
|
||||||
|
if (this != &other) {
|
||||||
std::map<mjCDef*, int> def_map;
|
std::map<mjCDef*, int> def_map;
|
||||||
for (int i = 0; i < other.defaults.size(); i++) {
|
for (int i = 0; i < other.defaults.size(); i++) {
|
||||||
defaults.push_back(new mjCDef(*other.defaults[i]));
|
defaults.push_back(new mjCDef(*other.defaults[i]));
|
||||||
@@ -252,11 +272,7 @@ mjCModel& mjCModel::operator=(const mjCModel& other) {
|
|||||||
plugins = other.plugins;
|
plugins = other.plugins;
|
||||||
active_plugins = other.active_plugins;
|
active_plugins = other.active_plugins;
|
||||||
|
|
||||||
// the world copy constructor takes care of copying the tree
|
|
||||||
mjCBody* world = new mjCBody(*other.bodies[0], this);
|
|
||||||
|
|
||||||
// create global lists
|
// create global lists
|
||||||
bodies.push_back(world);
|
|
||||||
MakeLists(bodies[0]);
|
MakeLists(bodies[0]);
|
||||||
|
|
||||||
// update defaults for the copied objects
|
// update defaults for the copied objects
|
||||||
@@ -279,16 +295,12 @@ mjCModel& mjCModel::operator=(const mjCModel& other) {
|
|||||||
lights[i]->def = defaults[def_map[other.lights[i]->def]];
|
lights[i]->def = defaults[def_map[other.lights[i]->def]];
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy name maps
|
|
||||||
for (int i=0; i<mjNOBJECT; i++) {
|
|
||||||
ids[i] = other.ids[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// cast children to mjCBase
|
// cast children to mjCBase
|
||||||
CreateObjectLists();
|
CreateObjectLists();
|
||||||
|
|
||||||
// restore to the same state as other
|
// restore to the same state as other
|
||||||
if (!compiled) {
|
if (!compiled) {
|
||||||
|
mjCBody* world = bodies[0];
|
||||||
bodies.clear();
|
bodies.clear();
|
||||||
frames.clear();
|
frames.clear();
|
||||||
joints.clear();
|
joints.clear();
|
||||||
@@ -751,6 +763,44 @@ mjCBase* mjCModel::FindObject(mjtObj type, string name) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// find body by name
|
||||||
|
mjCBody* mjCModel::FindBody(mjCBody* body, std::string name) {
|
||||||
|
if (body->name == name) {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto child : body->bodies) {
|
||||||
|
auto candidate = FindBody(child, name);
|
||||||
|
if (candidate) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// find frame by name
|
||||||
|
mjCFrame* mjCModel::FindFrame(mjCBody* body, std::string name) {
|
||||||
|
for (auto frame : body->frames) {
|
||||||
|
if (frame->name == name) {
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto body : body->bodies) {
|
||||||
|
auto candidate = FindFrame(body, name);
|
||||||
|
if (candidate) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// detect null pose
|
// detect null pose
|
||||||
bool mjCModel::IsNullPose(const mjtNum* pos, const mjtNum* quat) {
|
bool mjCModel::IsNullPose(const mjtNum* pos, const mjtNum* quat) {
|
||||||
bool result = true;
|
bool result = true;
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ class mjCModel : public mjCModel_, private mjSpec {
|
|||||||
mjCModel();
|
mjCModel();
|
||||||
mjCModel(const mjCModel& other);
|
mjCModel(const mjCModel& other);
|
||||||
mjCModel& operator=(const mjCModel& other);
|
mjCModel& operator=(const mjCModel& other);
|
||||||
|
mjCModel& operator+=(const mjCModel& other);
|
||||||
~mjCModel();
|
~mjCModel();
|
||||||
void CopyFromSpec(); // copy spec to private attributes
|
void CopyFromSpec(); // copy spec to private attributes
|
||||||
void PointToLocal();
|
void PointToLocal();
|
||||||
@@ -202,6 +203,8 @@ class mjCModel : public mjCModel_, private mjSpec {
|
|||||||
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
|
||||||
mjCBase* FindObject(mjtObj type, std::string name); // find object given type and name
|
mjCBase* FindObject(mjtObj type, std::string name); // find object given type and name
|
||||||
|
mjCBody* FindBody(mjCBody* body, std::string name); // find body given name
|
||||||
|
mjCFrame* FindFrame(mjCBody* body, std::string name); // find frame given name
|
||||||
bool IsNullPose(const mjtNum* pos, const mjtNum* quat); // detect null pose
|
bool IsNullPose(const mjtNum* pos, const mjtNum* quat); // detect null pose
|
||||||
|
|
||||||
// accessors
|
// accessors
|
||||||
|
|||||||
@@ -683,6 +683,7 @@ mjCBody& mjCBody::operator=(const mjCBody& other) {
|
|||||||
for (int i=0; i<other.frames.size(); i++) {
|
for (int i=0; i<other.frames.size(); i++) {
|
||||||
frames.push_back(new mjCFrame(*other.frames[i]));
|
frames.push_back(new mjCFrame(*other.frames[i]));
|
||||||
frames.back()->model = model;
|
frames.back()->model = model;
|
||||||
|
frames.back()->body = this;
|
||||||
fmap[other.frames[i]] = i;
|
fmap[other.frames[i]] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -731,6 +732,13 @@ mjCBody& mjCBody::operator=(const mjCBody& other) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mjCBody& mjCBody::operator+=(mjCBody& other) {
|
||||||
|
bodies.push_back(&other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void mjCBody::PointToLocal() {
|
void mjCBody::PointToLocal() {
|
||||||
spec.element = (mjElement)this;
|
spec.element = (mjElement)this;
|
||||||
spec.name = (mjString)&name;
|
spec.name = (mjString)&name;
|
||||||
@@ -784,6 +792,21 @@ mjCBody::~mjCBody() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// apply prefix and suffix, propagate to children
|
||||||
|
void mjCBody::SetNameSpace() {
|
||||||
|
if (!name.empty()) {
|
||||||
|
name = prefix + name + suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& body : bodies) {
|
||||||
|
body->prefix = prefix;
|
||||||
|
body->suffix = suffix;
|
||||||
|
body->SetNameSpace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// create child body and add it to body
|
// create child body and add it to body
|
||||||
mjCBody* mjCBody::AddBody(mjCDef* _def) {
|
mjCBody* mjCBody::AddBody(mjCDef* _def) {
|
||||||
// create body
|
// create body
|
||||||
@@ -1317,6 +1340,7 @@ mjCFrame::mjCFrame(mjCModel* _model, mjCFrame* _frame) {
|
|||||||
mjm_defaultFrame(spec);
|
mjm_defaultFrame(spec);
|
||||||
compiled = false;
|
compiled = false;
|
||||||
model = _model;
|
model = _model;
|
||||||
|
body = NULL;
|
||||||
frame = _frame ? _frame : NULL;
|
frame = _frame ? _frame : NULL;
|
||||||
PointToLocal();
|
PointToLocal();
|
||||||
CopyFromSpec();
|
CopyFromSpec();
|
||||||
@@ -1341,6 +1365,27 @@ mjCFrame& mjCFrame::operator=(const mjCFrame& other) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// attach body to frame
|
||||||
|
mjCFrame& mjCFrame::operator+=(const mjCBody& other) {
|
||||||
|
mjCBody* subtree = new mjCBody(other, model);
|
||||||
|
subtree->SetFrame(this);
|
||||||
|
subtree->SetNameSpace();
|
||||||
|
|
||||||
|
// add to tree
|
||||||
|
*body += *subtree;
|
||||||
|
|
||||||
|
// TODO: needs to attach only referencing elements
|
||||||
|
*model += *other.model;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void mjCFrame::SetParent(mjCBody* _body) {
|
||||||
|
body = _body;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void mjCFrame::PointToLocal() {
|
void mjCFrame::PointToLocal() {
|
||||||
spec.element = (mjElement)this;
|
spec.element = (mjElement)this;
|
||||||
|
|||||||
+19
-4
@@ -173,10 +173,12 @@ class mjCBoundingVolumeHierarchy : public mjCBoundingVolumeHierarchy_ {
|
|||||||
|
|
||||||
class mjCBase_ {
|
class mjCBase_ {
|
||||||
public:
|
public:
|
||||||
std::string name; // object name
|
int id; // object id
|
||||||
std::string classname; // defaults class name
|
std::string name; // object name
|
||||||
int id; // object id
|
std::string classname; // defaults class name
|
||||||
std::string info; // error message info set by the user
|
std::string info; // error message info set by the user
|
||||||
|
std::string prefix; // prefix for model operations
|
||||||
|
std::string suffix; // suffix for model operations
|
||||||
};
|
};
|
||||||
|
|
||||||
class mjCBase : public mjCBase_ {
|
class mjCBase : public mjCBase_ {
|
||||||
@@ -251,6 +253,7 @@ class mjCBody : public mjCBody_, private mjmBody {
|
|||||||
friend class mjCSite;
|
friend class mjCSite;
|
||||||
friend class mjCCamera;
|
friend class mjCCamera;
|
||||||
friend class mjCComposite;
|
friend class mjCComposite;
|
||||||
|
friend class mjCFrame;
|
||||||
friend class mjCLight;
|
friend class mjCLight;
|
||||||
friend class mjCFlex;
|
friend class mjCFlex;
|
||||||
friend class mjCFlexcomp;
|
friend class mjCFlexcomp;
|
||||||
@@ -272,11 +275,17 @@ class mjCBody : public mjCBody_, private mjmBody {
|
|||||||
mjCCamera* AddCamera(mjCDef* = 0);
|
mjCCamera* AddCamera(mjCDef* = 0);
|
||||||
mjCLight* AddLight(mjCDef* = 0);
|
mjCLight* AddLight(mjCDef* = 0);
|
||||||
|
|
||||||
|
// API for adding existing objects to body
|
||||||
|
mjCBody& operator+=(mjCBody& other);
|
||||||
|
|
||||||
// API for accessing objects
|
// API for accessing objects
|
||||||
int NumObjects(mjtObj type);
|
int NumObjects(mjtObj type);
|
||||||
mjCBase* GetObject(mjtObj type, int id);
|
mjCBase* GetObject(mjtObj type, int id);
|
||||||
mjCBase* FindObject(mjtObj type, std::string name, bool recursive = true);
|
mjCBase* FindObject(mjtObj type, std::string name, bool recursive = true);
|
||||||
|
|
||||||
|
// Propagate suffix and prefix to the whole tree
|
||||||
|
void SetNameSpace();
|
||||||
|
|
||||||
// set explicitinertial to true
|
// set explicitinertial to true
|
||||||
void MakeInertialExplicit();
|
void MakeInertialExplicit();
|
||||||
|
|
||||||
@@ -346,12 +355,18 @@ class mjCFrame : public mjCFrame_, private mjmFrame {
|
|||||||
|
|
||||||
void CopyFromSpec(void);
|
void CopyFromSpec(void);
|
||||||
void PointToLocal(void);
|
void PointToLocal(void);
|
||||||
|
void SetParent(mjCBody* _body);
|
||||||
|
|
||||||
|
mjCFrame& operator+=(const mjCBody& other);
|
||||||
|
mjCFrame& operator+=(const mjCFrame& other);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mjCFrame(mjCModel* = 0, mjCFrame* = 0); // constructor
|
mjCFrame(mjCModel* = 0, mjCFrame* = 0); // constructor
|
||||||
mjCFrame(const mjCFrame& other); // copy constructor
|
mjCFrame(const mjCFrame& other); // copy constructor
|
||||||
mjCFrame& operator=(const mjCFrame& other); // copy assignment
|
mjCFrame& operator=(const mjCFrame& other); // copy assignment
|
||||||
void Compile(void); // compiler
|
void Compile(void); // compiler
|
||||||
|
|
||||||
|
mjCBody* body; // body that owns the frame
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+32
-1
@@ -15,7 +15,7 @@
|
|||||||
#include "xml/xml.h"
|
#include "xml/xml.h"
|
||||||
|
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include "user/user_api.h"
|
#include <cstring>
|
||||||
|
|
||||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||||
#include <xlocale.h>
|
#include <xlocale.h>
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include "tinyxml2.h"
|
#include "tinyxml2.h"
|
||||||
|
|
||||||
|
#include <mujoco/mujoco.h>
|
||||||
#include <mujoco/mjmodel.h>
|
#include <mujoco/mjmodel.h>
|
||||||
#include <mujoco/mjplugin.h>
|
#include <mujoco/mjplugin.h>
|
||||||
#include "cc/array_safety.h"
|
#include "cc/array_safety.h"
|
||||||
@@ -399,3 +400,33 @@ mjSpec* mjParseXML(const char* filename, const mjVFS* vfs,
|
|||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void RegisterResourceProvider() {
|
||||||
|
// register string resource provider if not registered before
|
||||||
|
if (mjp_getResourceProvider("LoadModelFromString:") == nullptr) {
|
||||||
|
mjpResourceProvider resourceProvider;
|
||||||
|
mjp_defaultResourceProvider(&resourceProvider);
|
||||||
|
resourceProvider.prefix = "LoadModelFromString";
|
||||||
|
resourceProvider.open = +[](mjResource* resource) {
|
||||||
|
resource->data = &(resource->name[strlen("LoadModelFromString:")]);
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
resourceProvider.read =
|
||||||
|
+[](mjResource* resource, const void** buffer) {
|
||||||
|
*buffer = resource->data;
|
||||||
|
return (int) strlen((const char*) resource->data);
|
||||||
|
};
|
||||||
|
resourceProvider.close = +[](mjResource* resource) {};
|
||||||
|
mjp_registerResourceProvider(&resourceProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mjSpec* ParseSpecFromString(std::string_view xml, char* error,
|
||||||
|
int error_size, mjVFS* vfs) {
|
||||||
|
RegisterResourceProvider();
|
||||||
|
std::string xml2 = {xml.begin(), xml.end()};
|
||||||
|
std::string str = "LoadModelFromString:" + xml2;
|
||||||
|
return mjParseXML(str.c_str(), vfs, error, error_size);
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,5 +29,10 @@ std::string mjWriteXML(mjSpec* model, char* error, int error_sz);
|
|||||||
// Main parser function
|
// Main parser function
|
||||||
MJAPI mjSpec* mjParseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz);
|
MJAPI mjSpec* mjParseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz);
|
||||||
|
|
||||||
|
// Returns a newly-allocated mjSpec, loaded from the contents of xml.
|
||||||
|
// On failure returns nullptr and populates the error array if present.
|
||||||
|
MJAPI mjSpec* ParseSpecFromString(std::string_view xml, char* error = nullptr,
|
||||||
|
int error_size = 0, mjVFS* vfs = nullptr);
|
||||||
|
|
||||||
|
|
||||||
#endif // MUJOCO_SRC_XML_XML_H_
|
#endif // MUJOCO_SRC_XML_XML_H_
|
||||||
|
|||||||
@@ -195,5 +195,96 @@ TEST_F(PluginTest, RecompileCompareCache) {
|
|||||||
mj_deleteVFS(vfs.get());
|
mj_deleteVFS(vfs.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------- test attach -------------------------------
|
||||||
|
TEST_F(MujocoTest, Attach) {
|
||||||
|
std::array<char, 1000> er;
|
||||||
|
mjtNum tol = 0;
|
||||||
|
std::string field = "";
|
||||||
|
|
||||||
|
static constexpr char xml_parent[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<worldbody>
|
||||||
|
<body name="sphere">
|
||||||
|
<freejoint/>
|
||||||
|
<geom size=".1"/>
|
||||||
|
<frame name="frame" pos=".1 0 0" euler="0 90 0"/>
|
||||||
|
</body>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>)";
|
||||||
|
|
||||||
|
static constexpr char xml_child[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<worldbody>
|
||||||
|
<body name="cylinder">
|
||||||
|
<joint type="hinge"/>
|
||||||
|
<geom type="cylinder" size=".1 1 0"/>
|
||||||
|
<body name="named"/>
|
||||||
|
<body/>
|
||||||
|
</body>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>)";
|
||||||
|
|
||||||
|
static constexpr char xml_result[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<worldbody>
|
||||||
|
<body name="sphere">
|
||||||
|
<freejoint/>
|
||||||
|
<geom size=".1"/>
|
||||||
|
<frame name="frame" pos=".1 0 0" euler="0 90 0">
|
||||||
|
<body name="attached-cylinder-1">
|
||||||
|
<joint type="hinge"/>
|
||||||
|
<geom type="cylinder" size=".1 1 0"/>
|
||||||
|
<body name="attached-named-1"/>
|
||||||
|
<body/>
|
||||||
|
</body>
|
||||||
|
</frame>
|
||||||
|
</body>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>)";
|
||||||
|
|
||||||
|
// model with one free sphere and a frame
|
||||||
|
mjSpec* parent = ParseSpecFromString(xml_parent, er.data(), er.size());
|
||||||
|
EXPECT_THAT(parent, NotNull()) << er.data();
|
||||||
|
|
||||||
|
// get frame
|
||||||
|
mjmFrame* frame = mjm_findFrame(parent, "frame");
|
||||||
|
EXPECT_THAT(frame, NotNull());
|
||||||
|
|
||||||
|
// model with one cylinder and a hinge
|
||||||
|
mjSpec* child = ParseSpecFromString(xml_child, er.data(), er.size());
|
||||||
|
EXPECT_THAT(child, NotNull()) << er.data();
|
||||||
|
|
||||||
|
// get subtree
|
||||||
|
mjmBody* body = mjm_findBody(child, "cylinder");
|
||||||
|
EXPECT_THAT(body, NotNull());
|
||||||
|
|
||||||
|
// attach child to parent frame
|
||||||
|
EXPECT_THAT(
|
||||||
|
mjm_attachBody(frame, body, /*prefix=*/"attached-", /*suffix=*/"-1"), 0);
|
||||||
|
|
||||||
|
// compile new model
|
||||||
|
mjModel* m_attached = mjm_compile(parent, 0);
|
||||||
|
EXPECT_THAT(m_attached, NotNull());
|
||||||
|
|
||||||
|
// check full name stored in mjModel
|
||||||
|
EXPECT_STREQ(mj_id2name(m_attached, mjOBJ_BODY, 2), "attached-cylinder-1");
|
||||||
|
|
||||||
|
// check body 2 is attached to body 1
|
||||||
|
EXPECT_THAT(m_attached->body_parentid[2], 1);
|
||||||
|
|
||||||
|
// compare with expected XML
|
||||||
|
mjModel* m_expected = LoadModelFromString(xml_result, er.data(), er.size());
|
||||||
|
EXPECT_THAT(m_expected, NotNull()) << er.data();
|
||||||
|
EXPECT_LE(CompareModel(m_attached, m_expected, field), tol)
|
||||||
|
<< "Expected and attached models are different!\n"
|
||||||
|
<< "Different field: " << field << '\n';;
|
||||||
|
|
||||||
|
// destroy everything
|
||||||
|
mjm_deleteSpec(parent);
|
||||||
|
mjm_deleteSpec(child);
|
||||||
|
mj_deleteModel(m_attached);
|
||||||
|
mj_deleteModel(m_expected);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
|
|||||||
Reference in New Issue
Block a user