Allow multiple textures per material in MJCF.

PiperOrigin-RevId: 654795013
Change-Id: I418b72533484d00accaf801a342aa183e602a2d0
This commit is contained in:
Tom Erez
2024-07-22 09:56:58 -07:00
committed by Copybara-Service
parent e79efa60d6
commit 33e5960653
14 changed files with 474 additions and 106 deletions
+121 -1
View File
@@ -1636,6 +1636,11 @@ still be specified here but this functionality is now deprecated and will be rem
:at:`vflip`: :at-val:`[false, true], "false"`
If true, images loaded from file are flipped in the vertical direction. Does not affect procedural textures.
.. _asset-texture-nchannel:
:at:`nchannel`: :at-val:`int, "3"`
The number of channels in the texture image file. This allows loading 4-channel textures (RGBA) or single-channel
textures (e.g., for Physics-Based Rendering properties such as roughness or metallic).
.. _asset-material:
@@ -1666,7 +1671,12 @@ properties are grouped together.
If this attribute is specified, the material has a texture associated with it. Referencing the material from a model
element will cause the texture to be applied to that element. Note that the value of this attribute is the name of a
texture asset, not a texture file name. Textures cannot be loaded in the material definition; instead they must be
loaded explicitly via the :ref:`texture <asset-texture>` element and then referenced here.
loaded explicitly via the :ref:`texture <asset-texture>` element and then referenced here. The texture referenced
here is used for specifying the RGB values. For advanced rendering (e.g., Physics-Based Rendering), more texture
types need to be specified (e.g., roughness, metallic). In this case, this texture attribute should be omitted, and
the texture types should be specified explicitly via the specific role child elements, e.g.,
:ref:`texture <material-orm>`. Note however that the built-in renderer does not support PBR properties, so these
advanced rendering features are only available when using an external renderer.
.. _asset-material-texrepeat:
@@ -1738,6 +1748,116 @@ properties are grouped together.
model element which defines its own local rgba attribute, the local definition has precedence. Note that this "local"
definition could in fact come from a defaults class. The remaining material properties always apply.
.. _material-rgb:
:el-prefix:`material/` |-| **rgb** (?)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This element references a texture asset used to specify base color / albedo values.
.. _material-rgb-texture:
:at:`texture`: :at-val:`string, required`
Name of the texture, expected to have exactly 3 channels.
.. _material-normal:
:el-prefix:`material/` |-| **normal** (?)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This element references a texture asset used to specify the bump map (surface normals).
.. _material-normal-texture:
:at:`texture`: :at-val:`string, required`
Name of the texture, expected to have exactly 3 channels.
.. _material-occlusion:
:el-prefix:`material/` |-| **occlusion** (?)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This element references a texture asset used to specify ambient occlusion.
.. _material-occlusion-texture:
:at:`texture`: :at-val:`string, required`
Name of the texture, expected to have exactly one channel.
.. _material-roughness:
:el-prefix:`material/` |-| **roughness** (?)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This element references a texture asset used to specify the roughness map.
.. _material-roughness-texture:
:at:`texture`: :at-val:`string, required`
Name of the texture, expected to have exactly one channel.
.. _material-metallic:
:el-prefix:`material/` |-| **metallic** (?)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This element references a texture asset used to specify the metallic map.
.. _material-metallic-texture:
:at:`texture`: :at-val:`string, required`
Name of the texture, expected to have exactly one channel.
.. _material-opacity:
:el-prefix:`material/` |-| **opacity** (?)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This element references a texture asset used to specify the opacity map (alpha channel, transparency).
.. _material-opacity-texture:
:at:`texture`: :at-val:`string, required`
Name of the texture, expected to have exactly one channel.
.. _material-emissive:
:el-prefix:`material/` |-| **emissive** (?)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This element references a texture asset used to specify light emission.
.. _material-emissive-texture:
:at:`texture`: :at-val:`string, required`
Name of the texture, expected to have exactly 4 channels.
.. _material-orm:
:el-prefix:`material/` |-| **orm** (?)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This element references a texture asset used to specify a packed ORM map, where occlusion, roughness, and metallic
are joined into the corresponding RGB values of a single texture.
.. _material-orm-texture:
:at:`texture`: :at-val:`string, required`
Name of the texture, expected to have exactly 3 channels.
.. _material-rgba:
:el-prefix:`material/` |-| **rgba** (?)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This element references a texture asset used to specify a packed map where albedo and opacity are joined into the same
4-channel texture.
.. _material-rgba-texture:
:at:`texture`: :at-val:`string, required`
Name of the texture, expected to have exactly 4 channels.
.. _asset-model:
+64 -1
View File
@@ -163,7 +163,7 @@
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`markrgb<asset-texture-markrgb>` | :ref:`random<asset-texture-random>` | :ref:`width<asset-texture-width>` | :ref:`height<asset-texture-height>` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`hflip<asset-texture-hflip>` | :ref:`vflip<asset-texture-vflip>` | | | |
| | | | :ref:`hflip<asset-texture-hflip>` | :ref:`vflip<asset-texture-vflip>` | :ref:`nchannel<asset-texture-nchannel>` | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_| asset |br| |_| |L| | | .. table:: |
@@ -177,6 +177,69 @@
| | | | :ref:`reflectance<asset-material-reflectance>` | :ref:`metallic<asset-material-metallic>` | :ref:`roughness<asset-material-roughness>` | :ref:`rgba<asset-material-rgba>` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_2| material |br| |_2| |L| | | .. table:: |
| :ref:`rgb | ? | :class: mjcf-attributes |
| <material-rgb>` | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`texture<material-rgb-texture>` | | | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_2| material |br| |_2| |L| | | .. table:: |
| :ref:`occlusion | ? | :class: mjcf-attributes |
| <material-occlusion>` | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`texture<material-occlusion-texture>` | | | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_2| material |br| |_2| |L| | | .. table:: |
| :ref:`roughness | ? | :class: mjcf-attributes |
| <material-roughness>` | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`texture<material-roughness-texture>` | | | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_2| material |br| |_2| |L| | | .. table:: |
| :ref:`metallic | ? | :class: mjcf-attributes |
| <material-metallic>` | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`texture<material-metallic-texture>` | | | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_2| material |br| |_2| |L| | | .. table:: |
| :ref:`normal | ? | :class: mjcf-attributes |
| <material-normal>` | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`texture<material-normal-texture>` | | | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_2| material |br| |_2| |L| | | .. table:: |
| :ref:`opacity | ? | :class: mjcf-attributes |
| <material-opacity>` | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`texture<material-opacity-texture>` | | | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_2| material |br| |_2| |L| | | .. table:: |
| :ref:`emissive | ? | :class: mjcf-attributes |
| <material-emissive>` | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`texture<material-emissive-texture>` | | | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_2| material |br| |_2| |L| | | .. table:: |
| :ref:`rgba | ? | :class: mjcf-attributes |
| <material-rgba>` | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`texture<material-rgba-texture>` | | | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_2| material |br| |_2| |L| | | .. table:: |
| :ref:`orm | ? | :class: mjcf-attributes |
| <material-orm>` | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`texture<material-orm-texture>` | | | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_| asset |br| |_| |L| | | .. table:: |
| :ref:`model | \* | :class: mjcf-attributes |
| <asset-model>` | | |
+5 -2
View File
@@ -10,15 +10,18 @@ General
1. Renamed ``mjModel.tex_rbg`` to ``mjModel.tex_data``.
2. Added a new disable flag ``mjDSBL_AUTORESETNAN`` to disable automatic reset when NaNs or infinities are produced.
3. Added sub-elements to the MJCF :ref:`material<asset-material>` element, to allow specification of multiple textures
for rendering (e.g., :ref:`occlusion-roughness-metallic<material-orm>`). Note that the MuJoCo renderer doesn't support
these new features, and they are made available for use with external renderers.
MJX
^^^
3. Added more fields to ``mjx.Model`` and ``mjx.Data`` for further compatibility with the corresponding MuJoCo structs.
4. Added more fields to ``mjx.Model`` and ``mjx.Data`` for further compatibility with the corresponding MuJoCo structs.
Python bindings
^^^^^^^^^^^^^^^
4. Added support for asset dictionary argument in ``mujoco.spec.from_file``, ``mujoco.spec.from_string`` and
5. Added support for asset dictionary argument in ``mujoco.spec.from_file``, ``mujoco.spec.from_string`` and
``mujoco.spec.compile``.
Version 3.2.0 (Jul 15, 2024)
+2 -1
View File
@@ -2015,6 +2015,7 @@ typedef struct mjsTexture_ { // texture specification
double random; // probability of random dots
int height; // height in pixels (square for cube and skybox)
int width; // width in pixels
int nchannel; // number of channels
// method 2: single file
mjString* content_type; // content type of file
@@ -2035,7 +2036,7 @@ typedef struct mjsTexture_ { // texture specification
typedef struct mjsMaterial_ { // material specification
mjsElement* element; // element type
mjString* name; // name
mjString* texture; // name of texture (empty: none)
mjStringVec* textures; // names of textures (empty: none)
mjtByte texuniform; // make texture cube uniform
float texrepeat[2]; // texture repetition for 2D mapping
float emission; // emission
+2 -1
View File
@@ -500,6 +500,7 @@ typedef struct mjsTexture_ { // texture specification
double random; // probability of random dots
int height; // height in pixels (square for cube and skybox)
int width; // width in pixels
int nchannel; // number of channels
// method 2: single file
mjString* content_type; // content type of file
@@ -522,7 +523,7 @@ typedef struct mjsTexture_ { // texture specification
typedef struct mjsMaterial_ { // material specification
mjsElement* element; // element type
mjString* name; // name
mjString* texture; // name of texture (empty: none)
mjStringVec* textures; // names of textures (empty: none)
mjtByte texuniform; // make texture cube uniform
float texrepeat[2]; // texture repetition for 2D mapping
float emission; // emission
+8 -3
View File
@@ -9969,6 +9969,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
type=ValueType(name='int'),
doc='width in pixels',
),
StructFieldDecl(
name='nchannel',
type=ValueType(name='int'),
doc='number of channels',
),
StructFieldDecl(
name='content_type',
type=PointerType(
@@ -10045,11 +10050,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
doc='name',
),
StructFieldDecl(
name='texture',
name='textures',
type=PointerType(
inner_type=ValueType(name='mjString'),
inner_type=ValueType(name='mjStringVec'),
),
doc='name of texture (empty: none)',
doc='names of textures (empty: none)',
),
StructFieldDecl(
name='texuniform',
+3 -2
View File
@@ -1304,7 +1304,7 @@ static void makeMaterial(const mjModel* m, mjrContext* con) {
}
if (m->nmat >= mjMAXMATERIAL-1) {
mju_error("Maximum number of materials is %d", mjMAXMATERIAL);
mju_error("Maximum number of materials is 100, got %d", m->nmat);
}
for (int i=0; i < m->nmat; i++) {
if (m->mat_texid[i*mjNTEXROLE + mjTEXROLE_RGB] >= 0) {
@@ -1320,7 +1320,8 @@ static void makeMaterial(const mjModel* m, mjrContext* con) {
for (int i=0; i < m->ntex; i++) {
if (m->tex_type[i] == mjTEXTURE_SKYBOX) {
if (m->nmat >= mjMAXMATERIAL-2) {
mju_error("With skybox, maximum number of materials is %d", mjMAXMATERIAL);
mju_error("With skybox, maximum number of materials is 99, got %d",
m->nmat);
}
for (int j=0; j < mjNTEXROLE; j++) {
con->mat_texid[mjNTEXROLE * (mjMAXMATERIAL-1) + j] = -1;
+18 -18
View File
@@ -947,13 +947,11 @@ void mjCModel::DeleteMaterial(std::vector<T*>& list, std::string_view name) {
// delete texture with given name or all textures if the name is omitted
// delete all textures
template <class T>
static void DeleteTexture(std::vector<T*>& list, std::string_view name = "") {
static void DeleteAllTextures(std::vector<T*>& list) {
for (T* plist : list) {
if (name.empty() || plist->get_texture() == name) {
plist->del_texture();
}
plist->del_textures();
}
}
@@ -1046,7 +1044,7 @@ void mjCModel::DeleteAll<mjCMaterial>(std::vector<mjCMaterial*>& elements) {
template <>
void mjCModel::DeleteAll<mjCTexture>(std::vector<mjCTexture*>& elements) {
DeleteTexture(materials_);
DeleteAllTextures(materials_);
for (mjCTexture* element : elements) {
delete element;
}
@@ -1202,13 +1200,15 @@ void mjCModel::IndexAssets(bool discard) {
for (int i=0; i<materials_.size(); i++) {
mjCMaterial* material = materials_[i];
// find texture by name
if (!material->texture_.empty()) {
mjCBase* texture = FindObject(mjOBJ_TEXTURE, material->texture_);
if (texture) {
material->texid = texture->id;
} else {
throw mjCError(material, "texture '%s' not found in material %d", material->texture_.c_str(), i);
// find textures by name
for (int j=0; j<mjNTEXROLE; j++) {
if (!material->textures_[j].empty()) {
mjCBase* texture = FindObject(mjOBJ_TEXTURE, material->textures_[j]);
if (texture) {
material->texid[j] = texture->id;
} else {
throw mjCError(material, "texture '%s' not found in material %d", material->textures_[j].c_str(), i);
}
}
}
}
@@ -2504,14 +2504,15 @@ void mjCModel::CopyObjects(mjModel* m) {
m->tex_type[i] = ptex->type;
m->tex_height[i] = ptex->height;
m->tex_width[i] = ptex->width;
m->tex_nchannel[i] = ptex->nchannel;
m->tex_adr[i] = data_adr;
m->tex_nchannel[i] = 3;
// copy rgb data
memcpy(m->tex_data + data_adr, ptex->rgb.data(), 3*ptex->width*ptex->height);
memcpy(m->tex_data + data_adr, ptex->data.data(),
ptex->nchannel * ptex->width * ptex->height);
// advance counter
data_adr += 3*ptex->width*ptex->height;
data_adr += ptex->nchannel * ptex->width * ptex->height;
}
// materials
@@ -2521,9 +2522,8 @@ void mjCModel::CopyObjects(mjModel* m) {
// set fields
for (int j=0; j<mjNTEXROLE; j++) {
m->mat_texid[mjNTEXROLE*i+j] = -1;
m->mat_texid[mjNTEXROLE*i+j] = pmat->texid[j];
}
m->mat_texid[mjNTEXROLE*i+mjTEXROLE_RGB] = pmat->texid;
m->mat_texuniform[i] = pmat->texuniform;
mjuu_copyvec(m->mat_texrepeat+2*i, pmat->texrepeat, 2);
m->mat_emission[i] = pmat->emission;
+86 -64
View File
@@ -3217,7 +3217,7 @@ mjCTexture::mjCTexture(mjCModel* _model) {
spec_cubefiles_.assign(6, "");
// clear internal variables
rgb.clear();
data.clear();
// point to local
PointToLocal();
@@ -3266,14 +3266,14 @@ void mjCTexture::CopyFromSpec() {
cubefiles_ = spec_cubefiles_;
// clear precompiled asset. TODO: use asset cache
rgb.clear();
data.clear();
}
// free data storage allocated by lodepng
mjCTexture::~mjCTexture() {
rgb.clear();
data.clear();
}
@@ -3366,21 +3366,21 @@ void mjCTexture::Builtin2D(void) {
double pos = 2*sqrt(x*x+y*y) - 1;
// interpolate through sigmoid
interp(rgb.data() + 3*(r*width+c), rgb2, rgb1, pos);
interp(data.data() + 3*(r*width+c), rgb2, rgb1, pos);
}
}
}
// checker
else if (builtin==mjBUILTIN_CHECKER) {
checker(rgb.data(), RGB1, RGB2, width, height);
checker(data.data(), RGB1, RGB2, width, height);
}
// flat
else if (builtin==mjBUILTIN_FLAT) {
for (int r=0; r<height; r++) {
for (int c=0; c<width; c++) {
memcpy(rgb.data()+3*(r*width+c), RGB1, 3);
memcpy(data.data()+3*(r*width+c), RGB1, 3);
}
}
}
@@ -3390,28 +3390,28 @@ void mjCTexture::Builtin2D(void) {
// edge
if (mark==mjMARK_EDGE) {
for (int r=0; r<height; r++) {
memcpy(rgb.data()+3*(r*width+0), RGBm, 3);
memcpy(rgb.data()+3*(r*width+width-1), RGBm, 3);
memcpy(data.data()+3*(r*width+0), RGBm, 3);
memcpy(data.data()+3*(r*width+width-1), RGBm, 3);
}
for (int c=0; c<width; c++) {
memcpy(rgb.data()+3*(0*width+c), RGBm, 3);
memcpy(rgb.data()+3*((height-1)*width+c), RGBm, 3);
memcpy(data.data()+3*(0*width+c), RGBm, 3);
memcpy(data.data()+3*((height-1)*width+c), RGBm, 3);
}
}
// cross
else if (mark==mjMARK_CROSS) {
for (int r=0; r<height; r++) {
memcpy(rgb.data()+3*(r*width+width/2), RGBm, 3);
memcpy(data.data()+3*(r*width+width/2), RGBm, 3);
}
for (int c=0; c<width; c++) {
memcpy(rgb.data()+3*(height/2*width+c), RGBm, 3);
memcpy(data.data()+3*(height/2*width+c), RGBm, 3);
}
}
// random dots
else if (mark==mjMARK_RANDOM && random>0) {
randomdot(rgb.data(), markrgb, width, height, random);
randomdot(data.data(), markrgb, width, height, random);
}
}
@@ -3446,26 +3446,26 @@ void mjCTexture::BuiltinCube(void) {
// set sides
interp(RGBi, rgb1, rgb2, elside);
memcpy(rgb.data() + 0 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 0: right
memcpy(rgb.data() + 1 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 1: left
memcpy(rgb.data() + 4 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 4: front
memcpy(rgb.data() + 5 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 5: back
memcpy(data.data() + 0 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 0: right
memcpy(data.data() + 1 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 1: left
memcpy(data.data() + 4 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 4: front
memcpy(data.data() + 5 * 3 * ww + 3 * (r * w + c), RGBi, 3); // 5: back
// set up and down
interp(rgb.data() + 2 * 3 * ww + 3 * (r * w + c), rgb1, rgb2, elup); // 2: up
interp(rgb.data() + 3 * 3 * ww + 3 * (r * w + c), rgb1, rgb2, -elup); // 3: down
interp(data.data() + 2 * 3 * ww + 3 * (r * w + c), rgb1, rgb2, elup); // 2: up
interp(data.data() + 3 * 3 * ww + 3 * (r * w + c), rgb1, rgb2, -elup); // 3: down
}
}
}
// checker
else if (builtin == mjBUILTIN_CHECKER) {
checker(rgb.data() + 0 * 3 * ww, RGB1, RGB2, w, w);
checker(rgb.data() + 1 * 3 * ww, RGB1, RGB2, w, w);
checker(rgb.data() + 2 * 3 * ww, RGB1, RGB2, w, w);
checker(rgb.data() + 3 * 3 * ww, RGB1, RGB2, w, w);
checker(rgb.data() + 4 * 3 * ww, RGB2, RGB1, w, w);
checker(rgb.data() + 5 * 3 * ww, RGB2, RGB1, w, w);
checker(data.data() + 0 * 3 * ww, RGB1, RGB2, w, w);
checker(data.data() + 1 * 3 * ww, RGB1, RGB2, w, w);
checker(data.data() + 2 * 3 * ww, RGB1, RGB2, w, w);
checker(data.data() + 3 * 3 * ww, RGB1, RGB2, w, w);
checker(data.data() + 4 * 3 * ww, RGB2, RGB1, w, w);
checker(data.data() + 5 * 3 * ww, RGB2, RGB1, w, w);
}
// flat
@@ -3473,14 +3473,14 @@ void mjCTexture::BuiltinCube(void) {
for (int r = 0; r < w; r++) {
for (int c = 0; c < w; c++) {
// set sides and up
memcpy(rgb.data() + 0 * 3 * ww + 3 * (r * w + c), RGB1, 3);
memcpy(rgb.data() + 1 * 3 * ww + 3 * (r * w + c), RGB1, 3);
memcpy(rgb.data() + 2 * 3 * ww + 3 * (r * w + c), RGB1, 3);
memcpy(rgb.data() + 4 * 3 * ww + 3 * (r * w + c), RGB1, 3);
memcpy(rgb.data() + 5 * 3 * ww + 3 * (r * w + c), RGB1, 3);
memcpy(data.data() + 0 * 3 * ww + 3 * (r * w + c), RGB1, 3);
memcpy(data.data() + 1 * 3 * ww + 3 * (r * w + c), RGB1, 3);
memcpy(data.data() + 2 * 3 * ww + 3 * (r * w + c), RGB1, 3);
memcpy(data.data() + 4 * 3 * ww + 3 * (r * w + c), RGB1, 3);
memcpy(data.data() + 5 * 3 * ww + 3 * (r * w + c), RGB1, 3);
// set down
memcpy(rgb.data() + 3 * 3 * ww + 3 * (r * w + c), RGB2, 3);
memcpy(data.data() + 3 * 3 * ww + 3 * (r * w + c), RGB2, 3);
}
}
}
@@ -3491,12 +3491,12 @@ void mjCTexture::BuiltinCube(void) {
if (mark == mjMARK_EDGE) {
for (int j = 0; j < 6; j++) {
for (int r = 0; r < w; r++) {
memcpy(rgb.data() + j * 3 * ww + 3 * (r * w + 0), RGBm, 3);
memcpy(rgb.data() + j * 3 * ww + 3 * (r * w + w - 1), RGBm, 3);
memcpy(data.data() + j * 3 * ww + 3 * (r * w + 0), RGBm, 3);
memcpy(data.data() + j * 3 * ww + 3 * (r * w + w - 1), RGBm, 3);
}
for (int c = 0; c < w; c++) {
memcpy(rgb.data() + j * 3 * ww + 3 * (0 * w + c), RGBm, 3);
memcpy(rgb.data() + j * 3 * ww + 3 * ((w - 1) * w + c), RGBm, 3);
memcpy(data.data() + j * 3 * ww + 3 * (0 * w + c), RGBm, 3);
memcpy(data.data() + j * 3 * ww + 3 * ((w - 1) * w + c), RGBm, 3);
}
}
}
@@ -3505,17 +3505,17 @@ void mjCTexture::BuiltinCube(void) {
else if (mark == mjMARK_CROSS) {
for (int j = 0; j < 6; j++) {
for (int r = 0; r < w; r++) {
memcpy(rgb.data() + j * 3 * ww + 3 * (r * w + w / 2), RGBm, 3);
memcpy(data.data() + j * 3 * ww + 3 * (r * w + w / 2), RGBm, 3);
}
for (int c = 0; c < w; c++) {
memcpy(rgb.data() + j * 3 * ww + 3 * (w / 2 * w + c), RGBm, 3);
memcpy(data.data() + j * 3 * ww + 3 * (w / 2 * w + c), RGBm, 3);
}
}
}
// random dots
else if (mark == mjMARK_RANDOM && random > 0) {
randomdot(rgb.data(), markrgb, w, height, random);
randomdot(data.data(), markrgb, w, height, random);
}
}
@@ -3523,14 +3523,23 @@ void mjCTexture::BuiltinCube(void) {
void mjCTexture::LoadPNG(mjResource* resource,
std::vector<unsigned char>& image,
unsigned int& w, unsigned int& h) {
PNGImage png_image = PNGImage::Load(this, resource, LCT_RGB);
LodePNGColorType color_type;
if (nchannel == 4) {
color_type = LCT_RGBA;
} else if (nchannel == 3) {
color_type = LCT_RGB;
} else if (nchannel == 1) {
color_type = LCT_GREY;
} else {
throw mjCError(this, "Unsupported number of channels: %s",
std::to_string(nchannel).c_str());
}
PNGImage png_image = PNGImage::Load(this, resource, color_type);
w = png_image.Width();
h = png_image.Height();
image = png_image.MoveData();
}
// load custom file
void mjCTexture::LoadCustom(mjResource* resource,
std::vector<unsigned char>& image,
@@ -3601,6 +3610,10 @@ void mjCTexture::LoadFlip(std::string filename, const mjVFS* vfs,
// horizontal flip
if (hflip) {
if (nchannel != 3) {
throw mjCError(
this, "currently only 3-channel textures support horizontal flip");
}
for (int r=0; r<h; r++) {
for (int c=0; c<w/2; c++) {
int c1 = w-1-c;
@@ -3623,6 +3636,10 @@ void mjCTexture::LoadFlip(std::string filename, const mjVFS* vfs,
// vertical flip
if (vflip) {
if (nchannel != 3) {
throw mjCError(
this, "currently only 3-channel textures support vertical flip");
}
for (int r=0; r<h/2; r++) {
for (int c=0; c<w; c++) {
int r1 = h-1-r;
@@ -3658,12 +3675,12 @@ void mjCTexture::Load2D(std::string filename, const mjVFS* vfs) {
height = h;
// allocate and copy data
rgb.assign(3*width*height, 0);
if (rgb.empty()) {
data.assign(nchannel*width*height, 0);
if (data.empty()) {
throw mjCError(this, "Could not allocate memory for texture '%s' (id %d)",
(const char*)file_.c_str(), id);
}
memcpy(rgb.data(), image.data(), 3*width*height);
memcpy(data.data(), image.data(), nchannel*width*height);
image.clear();
}
@@ -3697,8 +3714,8 @@ void mjCTexture::LoadCubeSingle(std::string filename, const mjVFS* vfs) {
}
// allocate data
rgb.assign(3*width*height, 0);
if (rgb.empty()) {
data.assign(3*width*height, 0);
if (data.empty()) {
throw mjCError(this,
"Could not allocate memory for texture '%s' (id %d)",
(const char*)file_.c_str(), id);
@@ -3706,7 +3723,7 @@ void mjCTexture::LoadCubeSingle(std::string filename, const mjVFS* vfs) {
// copy: repeated
if (gridsize[0]==1 && gridsize[1]==1) {
memcpy(rgb.data(), image.data(), 3*width*width);
memcpy(data.data(), image.data(), 3*width*width);
}
// copy: grid
@@ -3739,7 +3756,7 @@ void mjCTexture::LoadCubeSingle(std::string filename, const mjVFS* vfs) {
int rstart = width*(k/gridsize[1]);
int cstart = width*(k%gridsize[1]);
for (int j=0; j<width; j++) {
memcpy(rgb.data()+i*3*width*width+j*3*width, image.data()+(j+rstart)*3*w+3*cstart, 3*width);
memcpy(data.data()+i*3*width*width+j*3*width, image.data()+(j+rstart)*3*w+3*cstart, 3*width);
}
// mark as defined
@@ -3753,7 +3770,7 @@ void mjCTexture::LoadCubeSingle(std::string filename, const mjVFS* vfs) {
for (int k=0; k<width; k++) {
for (int s=0; s<width; s++) {
for (int j=0; j<3; j++) {
rgb[i*3*width*width + 3*(k*width+s) + j] = (mjtByte)(255*rgb1[j]);
data[i*3*width*width + 3*(k*width+s) + j] = (mjtByte)(255*rgb1[j]);
}
}
}
@@ -3795,11 +3812,11 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
}
// first file: set size and allocate data
if (rgb.empty()) {
if (data.empty()) {
width = w;
height = 6*width;
rgb.assign(3*width*height, 0);
if (rgb.empty()) {
data.assign(3*width*height, 0);
if (data.empty()) {
throw mjCError(this, "Could not allocate memory for texture");
}
}
@@ -3812,7 +3829,7 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
}
// copy data
memcpy(rgb.data()+i*3*width*width, image.data(), 3*width*width);
memcpy(data.data()+i*3*width*width, image.data(), 3*width*width);
image.clear();
// mark as defined
@@ -3826,7 +3843,7 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
for (int k=0; k<width; k++) {
for (int s=0; s<width; s++) {
for (int j=0; j<3; j++) {
rgb[i*3*width*width + 3*(k*width+s) + j] = (mjtByte)(255*rgb1[j]);
data[i*3*width*width + 3*(k*width+s) + j] = (mjtByte)(255*rgb1[j]);
}
}
}
@@ -3857,8 +3874,8 @@ void mjCTexture::Compile(const mjVFS* vfs) {
}
// allocate data
rgb.assign(3*width*height, 0);
if (rgb.empty()) {
data.assign(nchannel*width*height, 0);
if (data.empty()) {
throw mjCError(this, "Could not allocate memory for texture");
}
@@ -3914,7 +3931,7 @@ void mjCTexture::Compile(const mjVFS* vfs) {
}
// make sure someone allocated data; SHOULD NOT OCCUR
if (rgb.empty()) {
if (data.empty()) {
throw mjCError(this, "texture '%s' (id %d) was not specified", name.c_str(), id);
}
}
@@ -3927,10 +3944,13 @@ void mjCTexture::Compile(const mjVFS* vfs) {
mjCMaterial::mjCMaterial(mjCModel* _model, mjCDef* _def) {
mjs_defaultMaterial(&spec);
elemtype = mjOBJ_MATERIAL;
textures_.assign(mjNTEXROLE, "");
spec_textures_.assign(mjNTEXROLE, "");
// clear internal
spec_texture_.clear();
texid = -1;
for (int i=0; i<mjNTEXROLE; i++) {
texid[i] = -1;
}
// reset to default if given
if (_def) {
@@ -3969,16 +3989,16 @@ mjCMaterial& mjCMaterial::operator=(const mjCMaterial& other) {
void mjCMaterial::PointToLocal() {
spec.element = static_cast<mjsElement*>(this);
spec.name = &name;
spec.texture = &spec_texture_;
spec.textures = &spec_textures_;
spec.info = &info;
texture = nullptr;
textures = nullptr;
}
void mjCMaterial::CopyFromSpec() {
*static_cast<mjsMaterial*>(this) = spec;
texture_ = spec_texture_;
textures_ = spec_textures_;
}
@@ -3987,8 +4007,10 @@ void mjCMaterial::NameSpace(const mjCModel* m) {
if (!name.empty()) {
name = m->prefix + name + m->suffix;
}
if (!spec_texture_.empty() && model != m) {
spec_texture_ = m->prefix + spec_texture_ + m->suffix;
for (int i=0; i<mjNTEXROLE; i++) {
if (!spec_textures_[i].empty() && model != m) {
spec_textures_[i] = m->prefix + spec_textures_[i] + m->suffix;
}
}
}
+6 -6
View File
@@ -1003,7 +1003,7 @@ class mjCHField : public mjCHField_, private mjsHField {
class mjCTexture_ : public mjCBase {
protected:
std::vector<mjtByte> rgb; // rgb data
std::vector<mjtByte> data; // texture data (rgb, roughness, etc.)
std::string file_;
std::string content_type_;
@@ -1063,9 +1063,9 @@ class mjCTexture : public mjCTexture_, private mjsTexture {
class mjCMaterial_ : public mjCBase {
protected:
int texid; // id of material's texture
std::string texture_;
std::string spec_texture_;
int texid[mjNTEXROLE]; // id of material's textures
std::vector<std::string> textures_;
std::vector<std::string> spec_textures_;
};
class mjCMaterial : public mjCMaterial_, private mjsMaterial {
@@ -1086,8 +1086,8 @@ class mjCMaterial : public mjCMaterial_, private mjsMaterial {
void PointToLocal();
void NameSpace(const mjCModel* m);
const std::string& get_texture() const { return texture_; }
void del_texture() { texture_.clear(); }
const std::string& get_texture(int i) const { return textures_[i]; }
void del_textures() { for (auto& t : textures_) t.clear(); }
private:
void Compile(void); // compiler
+47 -3
View File
@@ -239,12 +239,23 @@ const char* MJCF[nMJCF][mjXATTRNUM] = {
{"<"},
{"bone", "*", "5", "body", "bindpos", "bindquat", "vertid", "vertweight"},
{">"},
{"texture", "*", "22", "name", "type", "content_type", "file", "gridsize", "gridlayout",
{"texture", "*", "23", "name", "type", "content_type", "file", "gridsize", "gridlayout",
"fileright", "fileleft", "fileup", "filedown", "filefront", "fileback",
"builtin", "rgb1", "rgb2", "mark", "markrgb", "random", "width", "height",
"hflip", "vflip"},
"hflip", "vflip", "nchannel"},
{"material", "*", "12", "name", "class", "texture", "texrepeat", "texuniform",
"emission", "specular", "shininess", "reflectance", "metallic", "roughness", "rgba"},
{"<"},
{"rgb", "?", "1", "texture"},
{"occlusion", "?", "1", "texture"},
{"roughness", "?", "1", "texture"},
{"metallic", "?", "1", "texture"},
{"normal", "?", "1", "texture"},
{"opacity", "?", "1", "texture"},
{"emissive", "?", "1", "texture"},
{"rgba", "?", "1", "texture"},
{"orm", "?", "1", "texture"},
{">"},
{"model", "*", "2", "name", "file"},
{">"},
@@ -577,6 +588,19 @@ const mjMap camlight_map[camlight_sz] = {
{"targetbodycom", mjCAMLIGHT_TARGETBODYCOM}
};
// texmat role type
const int texrole_sz = mjNTEXROLE - 1;
const mjMap texrole_map[texrole_sz] = {
{"rgb", mjTEXROLE_RGB},
{"occlusion", mjTEXROLE_OCCLUSION},
{"roughness", mjTEXROLE_ROUGHNESS},
{"metallic", mjTEXROLE_METALLIC},
{"normal", mjTEXROLE_NORMAL},
{"opacity", mjTEXROLE_OPACITY},
{"emissive", mjTEXROLE_EMISSIVE},
{"rgba", mjTEXROLE_RGBA},
{"orm", mjTEXROLE_ORM},
};
// integrator type
const int integrator_sz = 4;
@@ -1534,9 +1558,26 @@ void mjXReader::OneMaterial(XMLElement* elem, mjsMaterial* pmat) {
if (ReadAttrTxt(elem, "name", name)) {
mjs_setString(pmat->name, name.c_str());
}
bool tex_attributes_found = false;
if (ReadAttrTxt(elem, "texture", texture)) {
mjs_setString(pmat->texture, texture.c_str());
mjs_setInStringVec(pmat->textures, mjTEXROLE_RGB, texture.c_str());
tex_attributes_found = true;
}
XMLElement* tex_elem = FirstChildElement(elem);
while (tex_elem) {
if (tex_attributes_found) {
throw mjXError(tex_elem, "A material with a texture attribute cannot have texture sub-elements");
}
// texture sub-element
int role = FindKey(texrole_map, texrole_sz, tex_elem->Name());
string texmat;
ReadAttrTxt(tex_elem, "texture", texmat, true);
mjs_setInStringVec(pmat->textures, role, texmat.c_str());
tex_elem = NextSiblingElement(tex_elem);
}
if (MapValue(elem, "texuniform", &n, bool_map, 2)) {
pmat->texuniform = (n==1);
}
@@ -3095,6 +3136,9 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
}
ReadAttrInt(elem, "width", &ptex->width);
ReadAttrInt(elem, "height", &ptex->height);
if (!ReadAttrInt(elem, "nchannel", &ptex->nchannel)) {
ptex->nchannel = 3;
}
ReadAttr(elem, "rgb1", 3, ptex->rgb1, text);
ReadAttr(elem, "rgb2", 3, ptex->rgb2, text);
ReadAttr(elem, "markrgb", 3, ptex->markrgb, text);
+1 -1
View File
@@ -99,7 +99,7 @@ class mjXReader : public mjXBase {
};
// MJCF schema
#define nMJCF 232
#define nMJCF 243
extern const char* MJCF[nMJCF][mjXATTRNUM];
#endif // MUJOCO_SRC_XML_XML_NATIVE_READER_H_
+14 -2
View File
@@ -305,8 +305,20 @@ void mjXWriter::OneMaterial(XMLElement* elem, const mjCMaterial* pmat, mjCDef* d
}
// defaults and regular
if (pmat->texture != def->Material().texture) {
WriteAttrTxt(elem, "texture", pmat->get_texture());
bool has_non_rgb = false;
for (int i=1; i<mjNTEXROLE; i++) {
if (!pmat->textures_[i].empty()) {
if (i != mjTEXROLE_RGB) {
has_non_rgb = true;
}
}
if (pmat->textures_[i] != def->Material().textures_[i]) {
WriteAttrTxt(elem, "texture", pmat->get_texture(i));
}
}
if (has_non_rgb) {
// // TODO elem = InsertEnd(section, "role");
mju_error("mjXWriter: no support for non-RGB textures.");
}
WriteAttrKey(elem, "texuniform", bool_map, 2, pmat->texuniform, def->Material().texuniform);
WriteAttr(elem, "texrepeat", 2, pmat->texrepeat, def->Material().texrepeat);
+97 -1
View File
@@ -782,12 +782,108 @@ TEST_F(XMLReaderTest, FallbackIncludePathTest) {
std::array<char, 1024> error;
mjModel* model = mj_loadXML(modelpath.c_str(), nullptr,
error.data(), error.size());
ASSERT_THAT(model, NotNull());
ASSERT_THAT(model, NotNull()) << error.data();
EXPECT_EQ(mj_name2id(model, mjOBJ_GEOM, "ball"), 2);
EXPECT_EQ(mj_name2id(model, mjOBJ_GEOM, "another_box"), 3);
mj_deleteModel(model);
}
TEST_F(XMLReaderTest, MaterialTextureTest) {
static constexpr char xml[] = R"(
<mujoco>
<asset>
<texture file="tiny0.png" type="2d" name="tiny0"/>
<texture file="tiny1.png" type="2d" name="tiny1"/>
<material name="material">
<occlusion texture="tiny0"/>
<roughness texture="tiny0"/>
<metallic texture="tiny0"/>
<rgb texture="tiny1"/>
</material>
</asset>
<worldbody>
<geom type="plane" material="material" size="4 4 4"/>
</worldbody>
</mujoco>
)";
MockFilesystem fs("MaterialTextureTest");
fs.AddFile("tiny0.png", kTinyPng, sizeof(kTinyPng));
fs.AddFile("tiny1.png", kTinyPng, sizeof(kTinyPng));
fs.AddFile("model.xml", (const unsigned char*) xml, sizeof(xml));
std::string modelpath = fs.FullPath("model.xml");
char error[1024];
mjModel* model = mj_loadXML(modelpath.c_str(), nullptr, error, 1024);
EXPECT_THAT(model, NotNull()) << error;
EXPECT_EQ(model->mat_texid[mjTEXROLE_RGB], 1);
EXPECT_EQ(model->mat_texid[mjTEXROLE_METALLIC], 0);
EXPECT_EQ(model->mat_texid[mjTEXROLE_ROUGHNESS], 0);
EXPECT_EQ(model->mat_texid[mjTEXROLE_OCCLUSION], 0);
mj_deleteModel(model);
}
TEST_F(XMLReaderTest, LegacyMaterialTextureTest) {
static constexpr char xml[] = R"(
<mujoco>
<asset>
<texture file="tiny0.png" type="2d" name="tiny0"/>
<texture file="tiny1.png" type="2d" name="tiny1"/>
<material name="material" texture="tiny1"/>
</asset>
<worldbody>
<geom type="plane" material="material" size="4 4 4"/>
</worldbody>
</mujoco>
)";
MockFilesystem fs("LegacyMaterialTextureTest");
fs.AddFile("tiny0.png", kTinyPng, sizeof(kTinyPng));
fs.AddFile("tiny1.png", kTinyPng, sizeof(kTinyPng));
fs.AddFile("model.xml", (const unsigned char*) xml, sizeof(xml));
std::string modelpath = fs.FullPath("model.xml");
char error[1024];
mjModel* model = mj_loadXML(modelpath.c_str(), nullptr, error, 1024);
EXPECT_THAT(model, NotNull()) << error;
EXPECT_EQ(model->mat_texid[mjTEXROLE_RGB], 1);
mj_deleteModel(model);
}
TEST_F(XMLReaderTest, MaterialTextureFailTest) {
static constexpr char xml[] = R"(
<mujoco>
<asset>
<texture file="tiny0.png" type="2d" name="tiny0"/>
<texture file="tiny1.png" type="2d" name="tiny1"/>
<material name="material" texture="tiny1">
<rgb texture="tiny1"/>
<occlusion texture="tiny0"/>
</material>
</asset>
<worldbody>
<geom type="plane" material="material" size="4 4 4"/>
</worldbody>
</mujoco>
)";
MockFilesystem fs("MaterialTextureFailTest");
fs.AddFile("tiny0.png", kTinyPng, sizeof(kTinyPng));
fs.AddFile("tiny1.png", kTinyPng, sizeof(kTinyPng));
fs.AddFile("model.xml", (const unsigned char*) xml, sizeof(xml));
std::string modelpath = fs.FullPath("model.xml");
std::array<char, 1024> error;
mjModel* m = LoadModelFromString(xml, error.data(), error.size());
EXPECT_THAT(m, IsNull());
EXPECT_THAT(error.data(), HasSubstr("A material with a texture attribute "
"cannot have texture sub-elements"));
}
TEST_F(XMLReaderTest, IncludeAssetsTest) {
static constexpr char xml[] = R"(
<mujoco>