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
|
||||
mjmTexture* mjm_addTexture(void* model) {
|
||||
mjCModel* modelC = static_cast<mjCModel*>(model);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
@@ -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
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user