Add mjmHField to C API.

PiperOrigin-RevId: 606933108
Change-Id: I21c895ed3e137dab8f57e2d0bcfb558aae2b971a
This commit is contained in:
Alessio Quaglino
2024-02-14 04:11:29 -08:00
committed by Copybara-Service
parent 2ee8a4dabd
commit c9e7586499
8 changed files with 138 additions and 51 deletions
+9
View File
@@ -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
mjmTexture* mjm_addTexture(void* model) {
mjCModel* modelC = static_cast<mjCModel*>(model);
+19
View File
@@ -346,6 +346,19 @@ typedef struct _mjmMesh { // mesh specification
} 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
mjElement element; // internal, do not modify
mjString name; // name
@@ -623,6 +636,9 @@ MJAPI mjmFlex* mjm_addFlex(void* model);
// Add mesh to model.
MJAPI mjmMesh* mjm_addMesh(void* model, void* defspec);
// Add height field to model.
MJAPI mjmHField* mjm_addHField(void* model);
// Add texture to model.
MJAPI mjmTexture* mjm_addTexture(void* model);
@@ -752,6 +768,9 @@ MJAPI void mjm_defaultFlex(mjmFlex& flex);
// Default mesh attributes.
MJAPI void mjm_defaultMesh(mjmMesh& mesh);
// Default height field attributes.
MJAPI void mjm_defaultHField(mjmHField& hfield);
// Default texture attributes.
MJAPI void mjm_defaultTexture(mjmTexture& texture);
+7
View File
@@ -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
void mjm_defaultTexture(mjmTexture& texture) {
memset(&texture, 0, sizeof(mjmTexture));
+2 -2
View File
@@ -3387,11 +3387,11 @@ bool mjCModel::CopyBack(const mjModel* m) {
mjCHField* phf;
for (int i=0; i<nhfield; i++) {
phf = hfields[i];
int size = phf->userdata().size();
int size = phf->get_userdata().size();
if (size) {
int nrow = m->hfield_nrow[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];
// copy back in reverse row order
for (int j=0; j<nrow; j++) {
+51 -18
View File
@@ -2384,16 +2384,44 @@ void mjCLight::Compile(void) {
// constructor
mjCHField::mjCHField(mjCModel* _model) {
mjm_defaultHField(spec);
// set model pointer
model = _model;
// clear variables
mjuu_setvec(size, 0, 0, 0, 0);
file.clear();
nrow = 0;
ncol = 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) {
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
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
for (int i=0; i<4; i++)
if (size[i]<=0)
@@ -2514,18 +2547,18 @@ void mjCHField::Compile(const mjVFS* vfs) {
// remove path from file if necessary
if (model->strippath) {
file = mjuu_strippath(file);
file_ = mjuu_strippath(file_);
}
// load from file if specified
if (!file.empty()) {
if (!file_.empty()) {
// make sure hfield was not already specified manually
if (nrow || ncol || data) {
throw mjCError(this,
"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
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());
}
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file);
string filename = mjuu_makefullname(model->modelfiledir, model->meshdir, file_);
mjResource* resource = LoadResource(filename, vfs);
try {
@@ -2564,7 +2597,7 @@ void mjCHField::Compile(const mjVFS* vfs) {
emax = mjMAX(emax, data[i]);
}
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++) {
data[i] -= emin;
+18 -13
View File
@@ -816,34 +816,39 @@ class mjCSkin: public mjCBase {
//------------------------- class mjCHField --------------------------------------------------------
// Describes a height field
class mjCHField : public mjCBase {
class mjCHField : public mjCBase, private mjmHField {
friend class mjCGeom;
friend class mjCModel;
friend class mjXWriter;
public:
std::string get_file() const { return file; }
mjmHField spec;
using mjCBase::name;
using mjCBase::info;
std::string content_type; // content type of file
std::string 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
float* data; // elevation data, row-major format
void CopyFromSpec(void);
void PointToLocal(void);
std::string get_file() const { return file_; }
// getter for user data
std::vector<float>& userdata() { return userdata_; }
// setter for user data
void set_userdata(std::optional<std::vector<float>>&& userdata);
std::vector<float>& get_userdata() { return userdata_; }
private:
mjCHField(mjCModel* model); // constructor
~mjCHField(); // destructor
std::vector<float> userdata_; // user-provided elevation data
float* data; // elevation data, row-major format
void Compile(const mjVFS* vfs); // compiler
void LoadCustom(mjResource* resource); // load from custom 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_;
};
+27 -13
View File
@@ -3136,43 +3136,57 @@ void mjXReader::Asset(XMLElement* section) {
// hfield sub-element
else if (name=="hfield") {
// create hfield
mjCHField* phf = model->AddHField();
GetXMLPos(elem, phf);
mjmHField* phf = mjm_addHField(model);
// write error info
mjm_setString(phf->info,
std::string("line = " + std::to_string(elem->GetLineNum()) + ", column = -1").c_str());
// read attributes
ReadAttrTxt(elem, "name", phf->name);
ReadAttrTxt(elem, "content_type", phf->content_type);
ReadAttrTxt(elem, "file", phf->file);
string name, content_type, file;
if (ReadAttrTxt(elem, "name", name)) {
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, "ncol", &phf->ncol);
ReadAttr(elem, "size", 4, phf->size, text, true);
// 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 ncol = phf->ncol;
phf->data = (float*) mju_malloc(nrow*ncol*sizeof(float));
// read user data
phf->set_userdata(ReadAttrVec<float>(elem, "elevation"));
auto userdata = ReadAttrVec<float>(elem, "elevation");
// user data given, copy into data
if (!phf->userdata().empty()) {
if (phf->userdata().size() != nrow*ncol) {
if (userdata.has_value()) {
if (userdata->size() != nrow*ncol) {
throw mjXError(elem, "elevation data length must match nrow*ncol");
}
// 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++) {
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
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());
}
}
}
+5 -5
View File
@@ -1416,15 +1416,15 @@ void mjXWriter::Asset(XMLElement* root) {
// write attributes
WriteAttrTxt(elem, "name", phf->name);
WriteAttr(elem, "size", 4, phf->size);
if (!phf->file.empty()) {
WriteAttrTxt(elem, "content_type", phf->content_type);
WriteAttrTxt(elem, "file", phf->file);
if (!phf->file_.empty()) {
WriteAttrTxt(elem, "content_type", phf->content_type_);
WriteAttrTxt(elem, "file", phf->file_);
} else {
WriteAttrInt(elem, "nrow", phf->nrow);
WriteAttrInt(elem, "ncol", phf->ncol);
if (!phf->userdata().empty()) {
if (!phf->get_userdata().empty()) {
string text;
Vector2String(text, phf->userdata(), phf->ncol);
Vector2String(text, phf->get_userdata(), phf->ncol);
WriteAttrTxt(elem, "elevation", text);
}
}