Add mjmTexture to C API.

PiperOrigin-RevId: 606543622
Change-Id: I2db743c1c40116f0503329fb1d004d9b041e83e7
This commit is contained in:
Alessio Quaglino
2024-02-13 02:08:31 -08:00
committed by Copybara-Service
parent 47bb4a828e
commit 06505f49f9
8 changed files with 202 additions and 101 deletions
+22
View File
@@ -137,6 +137,15 @@ mjmFrame* mjm_addFrame(mjmBody* bodyspec, mjmFrame* parentframe) {
// add texture to model
mjmTexture* mjm_addTexture(void* model) {
mjCModel* modelC = static_cast<mjCModel*>(model);
mjCTexture* texture = modelC->AddTexture();
return &texture->spec;
}
// add material to model
mjmMaterial* mjm_addMaterial(void* model, void* defspec) {
mjCModel* modelC = static_cast<mjCModel*>(model);
@@ -312,6 +321,19 @@ void mjm_setString(mjString dest, const char* text) {
// Set specific entry in destination string vector.
mjtByte mjm_setInStringVec(mjStringVec dest, int i, const char* text) {
std::vector<std::string>* v = reinterpret_cast<std::vector<std::string>*>(dest);
if (v->size() <= i) {
mju_error("Requested index in mjm_setInStringVec is out of bounds");
return 0;
}
v->at(i) = std::string(text);
return 1;
}
// split text and copy into string array
void mjm_setStringVec(mjStringVec dest, const char* text) {
std::vector<std::string>* v = reinterpret_cast<std::vector<std::string>*>(dest);
+62 -3
View File
@@ -38,12 +38,28 @@ typedef struct _mjFloatVec* mjFloatVec;
//---------------------------------- enum types (mjt) ----------------------------------------------
typedef enum _mjtGeomInertia { // type of inertia inference
mjINERTIA_VOLUME, // mass distributed in the volume
mjINERTIA_SHELL, // mass distributed on the surface
typedef enum _mjtGeomInertia { // type of inertia inference
mjINERTIA_VOLUME, // mass distributed in the volume
mjINERTIA_SHELL, // mass distributed on the surface
} mjtGeomInertia;
typedef enum _mjtBuiltin { // type of built-in procedural texture
mjBUILTIN_NONE = 0, // no built-in texture
mjBUILTIN_GRADIENT, // gradient: rgb1->rgb2
mjBUILTIN_CHECKER, // checker pattern: rgb1, rgb2
mjBUILTIN_FLAT // 2d: rgb1; cube: rgb1-up, rgb2-side, rgb3-down
} mjtBuiltin;
typedef enum _mjtMark { // mark type for procedural textures
mjMARK_NONE = 0, // no mark
mjMARK_EDGE, // edges
mjMARK_CROSS, // cross
mjMARK_RANDOM // random dots
} mjtMark;
//---------------------------------- attribute structs (mjm) ---------------------------------------
typedef struct _mjmOrientation { // alternative orientation specifiers
@@ -309,6 +325,40 @@ typedef struct _mjmFlex {
} mjmFlex;
typedef struct _mjmTexture { // texture specification
mjElement element; // internal, do not modify
mjString name; // name
mjString classname; // class name
mjtTexture type; // texture type
// method 1: builtin
int builtin; // builtin type (mjtBuiltin)
int mark; // mark type (mjtMark)
double rgb1[3]; // first color for builtin
double rgb2[3]; // second color for builtin
double markrgb[3]; // mark color
double random; // probability of random dots
int height; // height in pixels (square for cube and skybox)
int width; // width in pixels
// method 2: single file
mjString content_type; // content type of file
mjString file; // png file to load; use for all sides of cube
int gridsize[2]; // size of grid for composite file; (1,1)-repeat
char gridlayout[13]; // row-major: L,R,F,B,U,D for faces; . for unused
// method 3: separate files
mjStringVec cubefiles; // different file for each side of the cube
// flip options
mjtByte hflip; // horizontal flip
mjtByte vflip; // vertical flip
// other
mjString info; // message appended to compiler errors
} mjmTexture;
typedef struct _mjmMaterial { // material specification
mjElement element; // internal, do not modify
mjString name; // name
@@ -508,6 +558,9 @@ MJAPI mjmFrame* mjm_addFrame(mjmBody* body, mjmFrame* parentframe);
// Add flex to model.
MJAPI mjmFlex* mjm_addFlex(void* model);
// Add texture to model.
MJAPI mjmTexture* mjm_addTexture(void* model);
// Add material to model.
MJAPI mjmMaterial* mjm_addMaterial(void* model, void* defspec);
@@ -562,6 +615,9 @@ MJAPI void mjm_setString(mjString dest, const char* text);
// Split text to entries and copy to destination string vector.
MJAPI void mjm_setStringVec(mjStringVec dest, const char* text);
// Set specific entry in destination string vector.
MJAPI mjtByte mjm_setInStringVec(mjStringVec dest, int i, const char* text);
// Add text entry to destination string vector.
MJAPI void mjm_addToStringVec(mjStringVec dest, const char* text);
@@ -616,6 +672,9 @@ MJAPI void mjm_defaultLight(mjmLight& light);
// Default flex attributes.
MJAPI void mjm_defaultFlex(mjmFlex& flex);
// Default texture attributes.
MJAPI void mjm_defaultTexture(mjmTexture& texture);
// Default material attributes.
MJAPI void mjm_defaultMaterial(mjmMaterial& material);
+15
View File
@@ -15,6 +15,7 @@
#include <cstring>
#include <mujoco/mjmodel.h>
#include <mujoco/mujoco.h>
#include "cc/array_safety.h"
#include "user/user_api.h"
#include "user/user_util.h"
@@ -191,6 +192,20 @@ void mjm_defaultFlex(mjmFlex& flex) {
// default texture attributes
void mjm_defaultTexture(mjmTexture& texture) {
memset(&texture, 0, sizeof(mjmTexture));
texture.type = mjTEXTURE_CUBE;
mjuu_setvec(texture.rgb1, 0.8, 0.8, 0.8);
mjuu_setvec(texture.rgb2, 0.5, 0.5, 0.5);
mjuu_setvec(texture.markrgb, 0, 0, 0);
texture.random = 0.01;
texture.gridsize[0] = texture.gridsize[1] = 1;
mujoco::util::strcpy_arr(texture.gridlayout, "............");
}
// default material attributes
void mjm_defaultMaterial(mjmMaterial& material) {
memset(&material, 0, sizeof(mjmMaterial));
+1
View File
@@ -904,6 +904,7 @@ void mjCModel::SetDefaultNames(std::vector<T*>& assets) {
// use filename if name is missing
for (int i=0; i<assets.size(); i++) {
assets[i]->CopyFromSpec();
if (assets[i]->name.empty()) {
stripped = mjuu_strippath(assets[i]->get_file());
assets[i]->name = mjuu_stripext(stripped);
+52 -35
View File
@@ -2579,35 +2579,50 @@ void mjCHField::Compile(const mjVFS* vfs) {
// initialize defaults
mjCTexture::mjCTexture(mjCModel* _model) {
mjm_defaultTexture(spec);
// set model pointer
model = _model;
// clear user settings: builtin
type = mjTEXTURE_CUBE;
builtin = mjBUILTIN_NONE;
mark = mjMARK_NONE;
mjuu_setvec(rgb1, 0.8, 0.8, 0.8);
mjuu_setvec(rgb2, 0.5, 0.5, 0.5);
mjuu_setvec(markrgb, 0, 0, 0);
random = 0.01;
height = width = 0;
// clear user settings: single file
file.clear();
gridsize[0] = gridsize[1] = 1;
mju::strcpy_arr(gridlayout, "............");
spec_file_.clear();
spec_content_type_.clear();
// clear user settings: separate file
for (int i=0; i<6; i++) {
cubefiles[i].clear();
}
// clear flip options
hflip = false;
vflip = false;
spec_cubefiles_.assign(6, "");
// clear internal variables
rgb = 0;
// point to local (needs to be after defaults)
PointToLocal();
// in case this camera is not compiled
CopyFromSpec();
}
void mjCTexture::PointToLocal() {
spec.element = (mjElement)this;
spec.name = (mjString)&name;
spec.classname = (mjString)&classname;
spec.file = (mjString)&spec_file_;
spec.content_type = (mjString)&spec_content_type_;
spec.cubefiles = (mjStringVec)&spec_cubefiles_;
spec.info = (mjString)&info;
}
void mjCTexture::CopyFromSpec() {
*static_cast<mjmTexture*>(this) = spec;
file_ = spec_file_;
content_type_ = spec_content_type_;
cubefiles_ = spec_cubefiles_;
file = (mjString)&file_;
content_type = (mjString)&content_type_;
cubefiles = (mjStringVec)&cubefiles_;
}
@@ -2930,7 +2945,7 @@ void mjCTexture::LoadCustom(mjResource* resource,
void mjCTexture::LoadFlip(string filename, const mjVFS* vfs,
std::vector<unsigned char>& image,
unsigned int& w, unsigned int& h) {
std::string asset_type = GetAssetContentType(filename, content_type);
std::string asset_type = GetAssetContentType(filename, content_type_);
// fallback to custom
if (asset_type.empty()) {
@@ -3017,7 +3032,7 @@ void mjCTexture::Load2D(string filename, const mjVFS* vfs) {
rgb = (mjtByte*) mju_malloc(3*width*height);
if (!rgb) {
throw mjCError(this, "Could not allocate memory for texture '%s' (id %d)",
(const char*)file.c_str(), id);
(const char*)file_.c_str(), id);
}
memcpy(rgb, image.data(), 3*width*height);
image.clear();
@@ -3043,7 +3058,7 @@ void mjCTexture::LoadCubeSingle(string filename, const mjVFS* vfs) {
if (w/gridsize[1]!=h/gridsize[0] || (w%gridsize[1]) || (h%gridsize[0])) {
throw mjCError(this,
"PNG size must be integer multiple of gridsize in texture '%s' (id %d)",
(const char*)file.c_str(), id);
(const char*)file_.c_str(), id);
}
// assign size: repeated or full
@@ -3059,7 +3074,7 @@ void mjCTexture::LoadCubeSingle(string filename, const mjVFS* vfs) {
if (!rgb) {
throw mjCError(this,
"Could not allocate memory for texture '%s' (id %d)",
(const char*)file.c_str(), id);
(const char*)file_.c_str(), id);
}
// copy: repeated
@@ -3090,7 +3105,7 @@ void mjCTexture::LoadCubeSingle(string filename, const mjVFS* vfs) {
i = 5;
} else if (gridlayout[k]!='.')
throw mjCError(this, "gridlayout symbol is not among '.RLUDFB' in texture '%s' (id %d)",
(const char*)file.c_str(), id);
(const char*)file_.c_str(), id);
// load if specified
if (i>=0) {
@@ -3132,14 +3147,14 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
// process nonempty files
for (int i=0; i<6; i++) {
if (!cubefiles[i].empty()) {
if (!cubefiles_[i].empty()) {
// remove path from file if necessary
if (model->strippath) {
cubefiles[i] = mjuu_strippath(cubefiles[i]);
cubefiles_[i] = mjuu_strippath(cubefiles_[i]);
}
// make filename
string filename = mjuu_makefullname(model->modelfiledir, model->texturedir, cubefiles[i]);
string filename = mjuu_makefullname(model->modelfiledir, model->texturedir, cubefiles_[i]);
// load PNG or custom
unsigned int w, h;
@@ -3150,7 +3165,7 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
if (w!=h) {
throw mjCError(this,
"Non-square PNG file '%s' in cube or skybox id %d",
(const char*)cubefiles[i].c_str(), id);
(const char*)cubefiles_[i].c_str(), id);
}
// first file: set size and allocate data
@@ -3169,7 +3184,7 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
else if (width!=w) {
throw mjCError(this,
"PNG file '%s' has incompatible size in texture id %d",
(const char*)cubefiles[i].c_str(), id);
(const char*)cubefiles_[i].c_str(), id);
}
// copy data
@@ -3199,6 +3214,8 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
// compiler
void mjCTexture::Compile(const mjVFS* vfs) {
CopyFromSpec();
// builtin
if (builtin!=mjBUILTIN_NONE) {
// check size
@@ -3230,14 +3247,14 @@ void mjCTexture::Compile(const mjVFS* vfs) {
}
// single file
else if (!file.empty()) {
else if (!file_.empty()) {
// remove path from file if necessary
if (model->strippath) {
file = mjuu_strippath(file);
file_ = mjuu_strippath(file_);
}
// make filename
string filename = mjuu_makefullname(model->modelfiledir, model->texturedir, file);
string filename = mjuu_makefullname(model->modelfiledir, model->texturedir, file_);
// dispatch
if (type==mjTEXTURE_2D) {
@@ -3259,14 +3276,14 @@ void mjCTexture::Compile(const mjVFS* vfs) {
// at least one cubefile must be defined
bool defined = false;
for (int i=0; i<6; i++) {
if (!cubefiles[i].empty()) {
if (!cubefiles_[i].empty()) {
defined = true;
break;
}
}
if (!defined) {
throw mjCError(this,
"No cubefiles defined in cube or skybox texture '%s' (id %d)",
"No cubefiles_ defined in cube or skybox texture '%s' (id %d)",
(const char*)name.c_str(), id);
}
+17 -43
View File
@@ -68,24 +68,6 @@ class mjXURDF; // defined in xml_urdf
const int mjGEOMINFO[mjNGEOMTYPES] = {3, 0, 1, 2, 3, 2, 3, 0};
// builtin type for procedural textures
typedef enum _mjtBuiltin {
mjBUILTIN_NONE = 0, // no builtin
mjBUILTIN_GRADIENT, // circular gradient: rgb1->rgb2->rgb3
mjBUILTIN_CHECKER, // checker pattern: rgb1, rgb2
mjBUILTIN_FLAT // 2d: rgb1; cube: rgb1-up, rgb2-side, rgb3-down
} mjtBuiltin;
// mark type for procedural textures
typedef enum _mjtMark {
mjMARK_NONE = 0, // no mark
mjMARK_EDGE, // paint edges
mjMARK_CROSS, // paint cross
mjMARK_RANDOM // paint random dots
} mjtMark;
// error information
class [[nodiscard]] mjCError {
public:
@@ -871,7 +853,7 @@ class mjCHField : public mjCBase {
//------------------------- class mjCTexture -------------------------------------------------------
// Describes a texture
class mjCTexture : public mjCBase {
class mjCTexture : public mjCBase, private mjmTexture {
friend class mjCModel;
friend class mjXReader;
friend class mjXWriter;
@@ -879,32 +861,17 @@ class mjCTexture : public mjCBase {
public:
~mjCTexture(); // destructor
std::string get_file() const { return file; }
mjmTexture spec;
using mjCBase::name;
using mjCBase::classname;
using mjCBase::info;
mjtTexture type; // texture type
void CopyFromSpec(void);
void PointToLocal(void);
// method 1: builtin
mjtBuiltin builtin; // builtin type
mjtMark mark; // mark type
double rgb1[3]; // first color for builtin
double rgb2[3]; // second color for builtin
double markrgb[3]; // mark color
double random; // probability of random dots
int height; // height in pixels (square for cube and skybox)
int width; // width in pixels
// method 2: single file
std::string content_type; // content type of file
std::string file; // png file to load; use for all sides of cube
int gridsize[2]; // size of grid for composite file; (1,1)-repeat
char gridlayout[13]; // row-major: L,R,F,B,U,D for faces; . for unused
// method 3: separate files
std::string cubefiles[6]; // different file for each side of the cube
// flip options
bool hflip; // horizontal flip
bool vflip; // vertical flip
std::string get_file() const { return file_; }
std::string get_content_type() const { return content_type_; }
std::vector<std::string> get_cubefiles() const { return cubefiles_; }
private:
mjCTexture(mjCModel*); // constructor
@@ -928,6 +895,13 @@ class mjCTexture : public mjCBase {
unsigned int& w, unsigned int& h);
mjtByte* rgb; // rgb data
std::string file_;
std::string content_type_;
std::vector<std::string> cubefiles_;
std::string spec_file_;
std::string spec_content_type_;
std::vector<std::string> spec_cubefiles_;
};
+25 -12
View File
@@ -2980,7 +2980,7 @@ void mjXReader::Visual(XMLElement* section) {
// asset section parser
void mjXReader::Asset(XMLElement* section) {
int n;
string text, name;
string text, name, texname, content_type, file;
XMLElement* elem;
// iterate over child elements
@@ -2998,16 +2998,25 @@ void mjXReader::Asset(XMLElement* section) {
// texture sub-element
if (name=="texture") {
// create texture
mjCTexture* ptex = model->AddTexture();
GetXMLPos(elem, ptex);
mjmTexture* ptex = mjm_addTexture(model);
// write error info
mjm_setString(ptex->info,
std::string("line = " + std::to_string(elem->GetLineNum()) + ", column = -1").c_str());
// read attributes
if (MapValue(elem, "type", &n, texture_map, texture_sz)) {
ptex->type = (mjtTexture)n;
}
ReadAttrTxt(elem, "name", ptex->name);
ReadAttrTxt(elem, "content_type", ptex->content_type);
ReadAttrTxt(elem, "file", ptex->file);
if (ReadAttrTxt(elem, "name", texname)) {
mjm_setString(ptex->name, texname.c_str());
}
if (ReadAttrTxt(elem, "content_type", content_type)) {
mjm_setString(ptex->content_type, content_type.c_str());
}
if (ReadAttrTxt(elem, "file", file)) {
mjm_setString(ptex->file, file.c_str());
}
ReadAttrInt(elem, "width", &ptex->width);
ReadAttrInt(elem, "height", &ptex->height);
ReadAttr(elem, "rgb1", 3, ptex->rgb1, text);
@@ -3042,12 +3051,16 @@ void mjXReader::Asset(XMLElement* section) {
}
// separate files
ReadAttrTxt(elem, "fileright", ptex->cubefiles[0]);
ReadAttrTxt(elem, "fileleft", ptex->cubefiles[1]);
ReadAttrTxt(elem, "fileup", ptex->cubefiles[2]);
ReadAttrTxt(elem, "filedown", ptex->cubefiles[3]);
ReadAttrTxt(elem, "filefront", ptex->cubefiles[4]);
ReadAttrTxt(elem, "fileback", ptex->cubefiles[5]);
std::vector<string> cubefiles(6);
ReadAttrTxt(elem, "fileright", cubefiles[0]);
ReadAttrTxt(elem, "fileleft", cubefiles[1]);
ReadAttrTxt(elem, "fileup", cubefiles[2]);
ReadAttrTxt(elem, "filedown", cubefiles[3]);
ReadAttrTxt(elem, "filefront", cubefiles[4]);
ReadAttrTxt(elem, "fileback", cubefiles[5]);
for (int i = 0; i < cubefiles.size(); i++) {
mjm_setInStringVec(ptex->cubefiles, i, cubefiles[i].c_str());
}
}
// material sub-element
+8 -8
View File
@@ -1358,16 +1358,16 @@ void mjXWriter::Asset(XMLElement* root) {
// write textures loaded from files
else {
// write single file
WriteAttrTxt(elem, "content_type", ptex->content_type);
WriteAttrTxt(elem, "file", ptex->file);
WriteAttrTxt(elem, "content_type", ptex->get_content_type());
WriteAttrTxt(elem, "file", ptex->get_file());
// write separate files
WriteAttrTxt(elem, "fileright", ptex->cubefiles[0]);
WriteAttrTxt(elem, "fileleft", ptex->cubefiles[1]);
WriteAttrTxt(elem, "fileup", ptex->cubefiles[2]);
WriteAttrTxt(elem, "filedown", ptex->cubefiles[3]);
WriteAttrTxt(elem, "filefront", ptex->cubefiles[4]);
WriteAttrTxt(elem, "fileback", ptex->cubefiles[5]);
WriteAttrTxt(elem, "fileright", ptex->get_cubefiles()[0]);
WriteAttrTxt(elem, "fileleft", ptex->get_cubefiles()[1]);
WriteAttrTxt(elem, "fileup", ptex->get_cubefiles()[2]);
WriteAttrTxt(elem, "filedown", ptex->get_cubefiles()[3]);
WriteAttrTxt(elem, "filefront", ptex->get_cubefiles()[4]);
WriteAttrTxt(elem, "fileback", ptex->get_cubefiles()[5]);
if (ptex->hflip) {
WriteAttrKey(elem, "hflip", bool_map, 2, 1);
}