Add mjmHField to C API.
PiperOrigin-RevId: 606933108 Change-Id: I21c895ed3e137dab8f57e2d0bcfb558aae2b971a
This commit is contained in:
committed by
Copybara-Service
parent
2ee8a4dabd
commit
c9e7586499
@@ -147,6 +147,15 @@ mjmMesh* mjm_addMesh(void* model, void* defspec) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// add height field to model
|
||||||
|
mjmHField* mjm_addHField(void* model) {
|
||||||
|
mjCModel* modelC = static_cast<mjCModel*>(model);
|
||||||
|
mjCHField* heightField = modelC->AddHField();
|
||||||
|
return &heightField->spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// add texture to model
|
// add texture to model
|
||||||
mjmTexture* mjm_addTexture(void* model) {
|
mjmTexture* mjm_addTexture(void* model) {
|
||||||
mjCModel* modelC = static_cast<mjCModel*>(model);
|
mjCModel* modelC = static_cast<mjCModel*>(model);
|
||||||
|
|||||||
@@ -346,6 +346,19 @@ typedef struct _mjmMesh { // mesh specification
|
|||||||
} mjmMesh;
|
} mjmMesh;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _mjmHField { // height field specification
|
||||||
|
mjElement element; // internal, do not modify
|
||||||
|
mjString name; // name
|
||||||
|
mjString content_type; // content type of file
|
||||||
|
mjString file; // file: (nrow, ncol, [elevation data])
|
||||||
|
double size[4]; // hfield size (ignore referencing geom size)
|
||||||
|
int nrow; // number of rows
|
||||||
|
int ncol; // number of columns
|
||||||
|
mjFloatVec userdata; // user-provided elevation data
|
||||||
|
mjString info; // message appended to compiler errors
|
||||||
|
} mjmHField;
|
||||||
|
|
||||||
|
|
||||||
typedef struct _mjmTexture { // texture specification
|
typedef struct _mjmTexture { // texture specification
|
||||||
mjElement element; // internal, do not modify
|
mjElement element; // internal, do not modify
|
||||||
mjString name; // name
|
mjString name; // name
|
||||||
@@ -623,6 +636,9 @@ MJAPI mjmFlex* mjm_addFlex(void* model);
|
|||||||
// Add mesh to model.
|
// Add mesh to model.
|
||||||
MJAPI mjmMesh* mjm_addMesh(void* model, void* defspec);
|
MJAPI mjmMesh* mjm_addMesh(void* model, void* defspec);
|
||||||
|
|
||||||
|
// Add height field to model.
|
||||||
|
MJAPI mjmHField* mjm_addHField(void* model);
|
||||||
|
|
||||||
// Add texture to model.
|
// Add texture to model.
|
||||||
MJAPI mjmTexture* mjm_addTexture(void* model);
|
MJAPI mjmTexture* mjm_addTexture(void* model);
|
||||||
|
|
||||||
@@ -752,6 +768,9 @@ MJAPI void mjm_defaultFlex(mjmFlex& flex);
|
|||||||
// Default mesh attributes.
|
// Default mesh attributes.
|
||||||
MJAPI void mjm_defaultMesh(mjmMesh& mesh);
|
MJAPI void mjm_defaultMesh(mjmMesh& mesh);
|
||||||
|
|
||||||
|
// Default height field attributes.
|
||||||
|
MJAPI void mjm_defaultHField(mjmHField& hfield);
|
||||||
|
|
||||||
// Default texture attributes.
|
// Default texture attributes.
|
||||||
MJAPI void mjm_defaultTexture(mjmTexture& texture);
|
MJAPI void mjm_defaultTexture(mjmTexture& texture);
|
||||||
|
|
||||||
|
|||||||
@@ -203,6 +203,13 @@ void mjm_defaultMesh(mjmMesh& mesh) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// default height field attributes
|
||||||
|
void mjm_defaultHField(mjmHField& hfield) {
|
||||||
|
memset(&hfield, 0, sizeof(mjmHField));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// default texture attributes
|
// default texture attributes
|
||||||
void mjm_defaultTexture(mjmTexture& texture) {
|
void mjm_defaultTexture(mjmTexture& texture) {
|
||||||
memset(&texture, 0, sizeof(mjmTexture));
|
memset(&texture, 0, sizeof(mjmTexture));
|
||||||
|
|||||||
@@ -3387,11 +3387,11 @@ bool mjCModel::CopyBack(const mjModel* m) {
|
|||||||
mjCHField* phf;
|
mjCHField* phf;
|
||||||
for (int i=0; i<nhfield; i++) {
|
for (int i=0; i<nhfield; i++) {
|
||||||
phf = hfields[i];
|
phf = hfields[i];
|
||||||
int size = phf->userdata().size();
|
int size = phf->get_userdata().size();
|
||||||
if (size) {
|
if (size) {
|
||||||
int nrow = m->hfield_nrow[i];
|
int nrow = m->hfield_nrow[i];
|
||||||
int ncol = m->hfield_ncol[i];
|
int ncol = m->hfield_ncol[i];
|
||||||
float* userdata = phf->userdata().data();
|
float* userdata = phf->get_userdata().data();
|
||||||
float* modeldata = m->hfield_data + m->hfield_adr[i];
|
float* modeldata = m->hfield_data + m->hfield_adr[i];
|
||||||
// copy back in reverse row order
|
// copy back in reverse row order
|
||||||
for (int j=0; j<nrow; j++) {
|
for (int j=0; j<nrow; j++) {
|
||||||
|
|||||||
+51
-18
@@ -2384,16 +2384,44 @@ void mjCLight::Compile(void) {
|
|||||||
|
|
||||||
// constructor
|
// constructor
|
||||||
mjCHField::mjCHField(mjCModel* _model) {
|
mjCHField::mjCHField(mjCModel* _model) {
|
||||||
|
mjm_defaultHField(spec);
|
||||||
|
|
||||||
// set model pointer
|
// set model pointer
|
||||||
model = _model;
|
model = _model;
|
||||||
|
|
||||||
// clear variables
|
// clear variables
|
||||||
mjuu_setvec(size, 0, 0, 0, 0);
|
|
||||||
file.clear();
|
|
||||||
nrow = 0;
|
|
||||||
ncol = 0;
|
|
||||||
data = 0;
|
data = 0;
|
||||||
userdata_.clear();
|
spec_file_.clear();
|
||||||
|
spec_userdata_.clear();
|
||||||
|
|
||||||
|
// point to local
|
||||||
|
PointToLocal();
|
||||||
|
|
||||||
|
// copy from spec
|
||||||
|
CopyFromSpec();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void mjCHField::PointToLocal() {
|
||||||
|
spec.element = (mjElement)this;
|
||||||
|
spec.name = (mjString)&name;
|
||||||
|
spec.file = (mjString)&spec_file_;
|
||||||
|
spec.content_type = (mjString)&spec_content_type_;
|
||||||
|
spec.userdata = (mjFloatVec)&spec_userdata_;
|
||||||
|
spec.info = (mjString)&info;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void mjCHField::CopyFromSpec() {
|
||||||
|
*static_cast<mjmHField*>(this) = spec;
|
||||||
|
file_ = spec_file_;
|
||||||
|
content_type_ = spec_content_type_;
|
||||||
|
userdata_ = spec_userdata_;
|
||||||
|
file = (mjString)&file_;
|
||||||
|
content_type = (mjString)&content_type_;
|
||||||
|
userdata = (mjFloatVec)&userdata_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2403,6 +2431,8 @@ mjCHField::~mjCHField() {
|
|||||||
if (data) {
|
if (data) {
|
||||||
mju_free(data);
|
mju_free(data);
|
||||||
}
|
}
|
||||||
|
userdata_.clear();
|
||||||
|
spec_userdata_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2496,16 +2526,19 @@ void mjCHField::LoadPNG(mjResource* resource) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// user data setter
|
|
||||||
void mjCHField::set_userdata(std::optional<std::vector<float>>&& userdata) {
|
|
||||||
if (userdata.has_value()) {
|
|
||||||
userdata_ = std::move(userdata.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// compiler
|
// compiler
|
||||||
void mjCHField::Compile(const mjVFS* vfs) {
|
void mjCHField::Compile(const mjVFS* vfs) {
|
||||||
|
CopyFromSpec();
|
||||||
|
|
||||||
|
// copy userdata into data
|
||||||
|
if (!userdata_.empty()) {
|
||||||
|
data = (float*) mju_malloc(nrow*ncol*sizeof(float));
|
||||||
|
if (!data) {
|
||||||
|
throw mjCError(this, "could not allocate buffers in hfield");
|
||||||
|
}
|
||||||
|
memcpy(data, userdata_.data(), nrow*ncol*sizeof(float));
|
||||||
|
}
|
||||||
|
|
||||||
// check size parameters
|
// check size parameters
|
||||||
for (int i=0; i<4; i++)
|
for (int i=0; i<4; i++)
|
||||||
if (size[i]<=0)
|
if (size[i]<=0)
|
||||||
@@ -2514,18 +2547,18 @@ void mjCHField::Compile(const mjVFS* vfs) {
|
|||||||
|
|
||||||
// remove path from file if necessary
|
// remove path from file if necessary
|
||||||
if (model->strippath) {
|
if (model->strippath) {
|
||||||
file = mjuu_strippath(file);
|
file_ = mjuu_strippath(file_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// load from file if specified
|
// load from file if specified
|
||||||
if (!file.empty()) {
|
if (!file_.empty()) {
|
||||||
// make sure hfield was not already specified manually
|
// make sure hfield was not already specified manually
|
||||||
if (nrow || ncol || data) {
|
if (nrow || ncol || data) {
|
||||||
throw mjCError(this,
|
throw mjCError(this,
|
||||||
"hfield '%s' (id = %d) specified from file and manually", name.c_str(), id);
|
"hfield '%s' (id = %d) specified from file and manually", name.c_str(), id);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string asset_type = GetAssetContentType(file, content_type);
|
std::string asset_type = GetAssetContentType(file_, content_type_);
|
||||||
|
|
||||||
// fallback to custom
|
// fallback to custom
|
||||||
if (asset_type.empty()) {
|
if (asset_type.empty()) {
|
||||||
@@ -2536,7 +2569,7 @@ void mjCHField::Compile(const mjVFS* vfs) {
|
|||||||
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
|
throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file);
|
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file_);
|
||||||
mjResource* resource = LoadResource(filename, vfs);
|
mjResource* resource = LoadResource(filename, vfs);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -2564,7 +2597,7 @@ void mjCHField::Compile(const mjVFS* vfs) {
|
|||||||
emax = mjMAX(emax, data[i]);
|
emax = mjMAX(emax, data[i]);
|
||||||
}
|
}
|
||||||
if (emin>emax) {
|
if (emin>emax) {
|
||||||
throw mjCError(this, "invalid data range in hfield '%s'", file.c_str());
|
throw mjCError(this, "invalid data range in hfield '%s'", file_.c_str());
|
||||||
}
|
}
|
||||||
for (int i=0; i<nrow*ncol; i++) {
|
for (int i=0; i<nrow*ncol; i++) {
|
||||||
data[i] -= emin;
|
data[i] -= emin;
|
||||||
|
|||||||
+18
-13
@@ -816,34 +816,39 @@ class mjCSkin: public mjCBase {
|
|||||||
//------------------------- class mjCHField --------------------------------------------------------
|
//------------------------- class mjCHField --------------------------------------------------------
|
||||||
// Describes a height field
|
// Describes a height field
|
||||||
|
|
||||||
class mjCHField : public mjCBase {
|
class mjCHField : public mjCBase, private mjmHField {
|
||||||
|
friend class mjCGeom;
|
||||||
friend class mjCModel;
|
friend class mjCModel;
|
||||||
friend class mjXWriter;
|
friend class mjXWriter;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::string get_file() const { return file; }
|
mjmHField spec;
|
||||||
|
using mjCBase::name;
|
||||||
|
using mjCBase::info;
|
||||||
|
|
||||||
std::string content_type; // content type of file
|
void CopyFromSpec(void);
|
||||||
std::string file; // file: (nrow, ncol, [elevation data])
|
void PointToLocal(void);
|
||||||
double size[4]; // hfield size (ignore referencing geom size)
|
|
||||||
int nrow; // number of rows
|
std::string get_file() const { return file_; }
|
||||||
int ncol; // number of columns
|
|
||||||
float* data; // elevation data, row-major format
|
|
||||||
|
|
||||||
// getter for user data
|
// getter for user data
|
||||||
std::vector<float>& userdata() { return userdata_; }
|
std::vector<float>& get_userdata() { return userdata_; }
|
||||||
|
|
||||||
// setter for user data
|
|
||||||
void set_userdata(std::optional<std::vector<float>>&& userdata);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mjCHField(mjCModel* model); // constructor
|
mjCHField(mjCModel* model); // constructor
|
||||||
~mjCHField(); // destructor
|
~mjCHField(); // destructor
|
||||||
std::vector<float> userdata_; // user-provided elevation data
|
float* data; // elevation data, row-major format
|
||||||
void Compile(const mjVFS* vfs); // compiler
|
void Compile(const mjVFS* vfs); // compiler
|
||||||
|
|
||||||
void LoadCustom(mjResource* resource); // load from custom format
|
void LoadCustom(mjResource* resource); // load from custom format
|
||||||
void LoadPNG(mjResource* resource); // load from PNG format
|
void LoadPNG(mjResource* resource); // load from PNG format
|
||||||
|
|
||||||
|
std::string file_;
|
||||||
|
std::string content_type_;
|
||||||
|
std::vector<float> userdata_;
|
||||||
|
std::string spec_file_;
|
||||||
|
std::string spec_content_type_;
|
||||||
|
std::vector<float> spec_userdata_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3136,43 +3136,57 @@ void mjXReader::Asset(XMLElement* section) {
|
|||||||
// hfield sub-element
|
// hfield sub-element
|
||||||
else if (name=="hfield") {
|
else if (name=="hfield") {
|
||||||
// create hfield
|
// create hfield
|
||||||
mjCHField* phf = model->AddHField();
|
mjmHField* phf = mjm_addHField(model);
|
||||||
GetXMLPos(elem, phf);
|
|
||||||
|
// write error info
|
||||||
|
mjm_setString(phf->info,
|
||||||
|
std::string("line = " + std::to_string(elem->GetLineNum()) + ", column = -1").c_str());
|
||||||
|
|
||||||
// read attributes
|
// read attributes
|
||||||
ReadAttrTxt(elem, "name", phf->name);
|
string name, content_type, file;
|
||||||
ReadAttrTxt(elem, "content_type", phf->content_type);
|
if (ReadAttrTxt(elem, "name", name)) {
|
||||||
ReadAttrTxt(elem, "file", phf->file);
|
mjm_setString(phf->name, name.c_str());
|
||||||
|
}
|
||||||
|
if (ReadAttrTxt(elem, "content_type", content_type)) {
|
||||||
|
mjm_setString(phf->content_type, content_type.c_str());
|
||||||
|
}
|
||||||
|
if (ReadAttrTxt(elem, "file", file)) {
|
||||||
|
mjm_setString(phf->file, file.c_str());
|
||||||
|
}
|
||||||
ReadAttrInt(elem, "nrow", &phf->nrow);
|
ReadAttrInt(elem, "nrow", &phf->nrow);
|
||||||
ReadAttrInt(elem, "ncol", &phf->ncol);
|
ReadAttrInt(elem, "ncol", &phf->ncol);
|
||||||
ReadAttr(elem, "size", 4, phf->size, text, true);
|
ReadAttr(elem, "size", 4, phf->size, text, true);
|
||||||
|
|
||||||
// allocate buffer for dynamic hfield, copy user data if given
|
// allocate buffer for dynamic hfield, copy user data if given
|
||||||
if (phf->file.empty() && phf->nrow>0 && phf->ncol>0) {
|
if (file.empty() && phf->nrow>0 && phf->ncol>0) {
|
||||||
int nrow = phf->nrow;
|
int nrow = phf->nrow;
|
||||||
int ncol = phf->ncol;
|
int ncol = phf->ncol;
|
||||||
phf->data = (float*) mju_malloc(nrow*ncol*sizeof(float));
|
|
||||||
|
|
||||||
// read user data
|
// read user data
|
||||||
phf->set_userdata(ReadAttrVec<float>(elem, "elevation"));
|
auto userdata = ReadAttrVec<float>(elem, "elevation");
|
||||||
|
|
||||||
// user data given, copy into data
|
// user data given, copy into data
|
||||||
if (!phf->userdata().empty()) {
|
if (userdata.has_value()) {
|
||||||
if (phf->userdata().size() != nrow*ncol) {
|
if (userdata->size() != nrow*ncol) {
|
||||||
throw mjXError(elem, "elevation data length must match nrow*ncol");
|
throw mjXError(elem, "elevation data length must match nrow*ncol");
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy in reverse row order, so XML string is top-to-bottom
|
// copy in reverse row order, so XML string is top-to-bottom
|
||||||
const float* userdata = phf->userdata().data();
|
std::vector<float> flipped(nrow*ncol);
|
||||||
for (int i = 0; i < nrow; i++) {
|
for (int i = 0; i < nrow; i++) {
|
||||||
int flip = nrow-1-i;
|
int flip = nrow-1-i;
|
||||||
memcpy(phf->data + flip*ncol, userdata + i*ncol, ncol*sizeof(float));
|
for (int j = 0; j < ncol; j++) {
|
||||||
|
flipped[flip*ncol + j] = userdata->data()[i*ncol + j];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mjm_setFloat(phf->userdata, flipped.data(), flipped.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// user data not given, set to 0
|
// user data not given, set to 0
|
||||||
else {
|
else {
|
||||||
memset(phf->data, 0, phf->nrow*phf->ncol*sizeof(float));
|
std::vector<float> zero(nrow*ncol);
|
||||||
|
mjm_setFloat(phf->userdata, zero.data(), zero.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1416,15 +1416,15 @@ void mjXWriter::Asset(XMLElement* root) {
|
|||||||
// write attributes
|
// write attributes
|
||||||
WriteAttrTxt(elem, "name", phf->name);
|
WriteAttrTxt(elem, "name", phf->name);
|
||||||
WriteAttr(elem, "size", 4, phf->size);
|
WriteAttr(elem, "size", 4, phf->size);
|
||||||
if (!phf->file.empty()) {
|
if (!phf->file_.empty()) {
|
||||||
WriteAttrTxt(elem, "content_type", phf->content_type);
|
WriteAttrTxt(elem, "content_type", phf->content_type_);
|
||||||
WriteAttrTxt(elem, "file", phf->file);
|
WriteAttrTxt(elem, "file", phf->file_);
|
||||||
} else {
|
} else {
|
||||||
WriteAttrInt(elem, "nrow", phf->nrow);
|
WriteAttrInt(elem, "nrow", phf->nrow);
|
||||||
WriteAttrInt(elem, "ncol", phf->ncol);
|
WriteAttrInt(elem, "ncol", phf->ncol);
|
||||||
if (!phf->userdata().empty()) {
|
if (!phf->get_userdata().empty()) {
|
||||||
string text;
|
string text;
|
||||||
Vector2String(text, phf->userdata(), phf->ncol);
|
Vector2String(text, phf->get_userdata(), phf->ncol);
|
||||||
WriteAttrTxt(elem, "elevation", text);
|
WriteAttrTxt(elem, "elevation", text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user