Adds color space to textures.

PiperOrigin-RevId: 761907261
Change-Id: Iec546d1d5907a15e57ef809ec31850640fc52915
This commit is contained in:
Google DeepMind
2025-05-22 04:32:04 -07:00
committed by Copybara-Service
parent 62a9d5b98d
commit 3e9bc79b54
17 changed files with 123 additions and 23 deletions
+7 -2
View File
@@ -1387,15 +1387,20 @@ void mjr_uploadTexture(const mjModel* m, const mjrContext* con, int texid) {
// assign data
int type = 0;
int internaltype = 0;
if (m->tex_nchannel[texid] == 3) {
type = GL_RGB;
internaltype = (m->tex_colorspace[texid] == mjCOLORSPACE_SRGB) ? GL_SRGB8_EXT : GL_RGB;
} else if (m->tex_nchannel[texid] == 4) {
type = GL_RGBA;
internaltype = (m->tex_colorspace[texid] == mjCOLORSPACE_SRGB) ? GL_SRGB8_ALPHA8_EXT : GL_RGBA;
} else {
mju_error("Number of channels not supported: %d", m->tex_nchannel[texid]);
}
glTexImage2D(GL_TEXTURE_2D, 0, type, m->tex_width[texid], m->tex_height[texid], 0,
type, GL_UNSIGNED_BYTE, m->tex_data + m->tex_adr[texid]);
glTexImage2D(GL_TEXTURE_2D, 0, internaltype, m->tex_width[texid],
m->tex_height[texid], 0, type, GL_UNSIGNED_BYTE,
m->tex_data + m->tex_adr[texid]);
// generate mipmaps
glGenerateMipmap(GL_TEXTURE_2D);
+1
View File
@@ -273,6 +273,7 @@ void mjs_defaultSkin(mjsSkin* skin) {
void mjs_defaultTexture(mjsTexture* texture) {
memset(texture, 0, sizeof(mjsTexture));
texture->type = mjTEXTURE_CUBE;
texture->colorspace = mjCOLORSPACE_AUTO;
texture->rgb1[0] = texture->rgb1[1] = texture->rgb1[2] = 0.8;
texture->rgb2[0] = texture->rgb2[1] = texture->rgb2[2] = 0.5;
texture->random = 0.01;
+1
View File
@@ -3285,6 +3285,7 @@ void mjCModel::CopyObjects(mjModel* m) {
// set fields
m->tex_type[i] = ptex->type;
m->tex_colorspace[i] = ptex->colorspace;
m->tex_height[i] = ptex->height;
m->tex_width[i] = ptex->width;
m->tex_nchannel[i] = ptex->nchannel;
+36 -10
View File
@@ -55,6 +55,8 @@ class PNGImage {
LodePNGColorType color_type);
int Width() const { return width_; }
int Height() const { return height_; }
bool IsSRGB() const { return is_srgb_; }
uint8_t operator[] (int i) const { return data_[i]; }
std::vector<unsigned char>& MoveData() { return data_; }
@@ -65,6 +67,7 @@ class PNGImage {
int width_;
int height_;
bool is_srgb_;
LodePNGColorType color_type_;
std::vector<uint8_t> data_;
};
@@ -104,8 +107,11 @@ PNGImage PNGImage::Load(const mjCBase* obj, mjResource* resource,
// decode PNG from buffer
unsigned int w, h;
unsigned err = lodepng::decode(image.data_, w, h,
buffer, nbuffer, image.color_type_, 8);
lodepng::State state;
state.info_raw.colortype = image.color_type_;
state.info_raw.bitdepth = 8;
unsigned err = lodepng::decode(image.data_, w, h, state, buffer, nbuffer);
// check for errors
if (err) {
@@ -116,6 +122,7 @@ PNGImage PNGImage::Load(const mjCBase* obj, mjResource* resource,
image.width_ = w;
image.height_ = h;
image.is_srgb_ = (state.info_png.srgb_defined == 1);
if (image.width_ <= 0 || image.height_ < 0) {
std::stringstream ss;
@@ -4257,7 +4264,7 @@ void mjCTexture::BuiltinCube(void) {
// load PNG file
void mjCTexture::LoadPNG(mjResource* resource,
std::vector<unsigned char>& image,
unsigned int& w, unsigned int& h) {
unsigned int& w, unsigned int& h, bool& is_srgb) {
LodePNGColorType color_type;
if (nchannel == 4) {
color_type = LCT_RGBA;
@@ -4272,13 +4279,14 @@ void mjCTexture::LoadPNG(mjResource* resource,
PNGImage png_image = PNGImage::Load(this, resource, color_type);
w = png_image.Width();
h = png_image.Height();
is_srgb = png_image.IsSRGB();
image = png_image.MoveData();
}
// load custom file
void mjCTexture::LoadCustom(mjResource* resource,
std::vector<unsigned char>& image,
unsigned int& w, unsigned int& h) {
unsigned int& w, unsigned int& h, bool& is_srgb) {
const void* buffer = 0;
int buffer_sz = mju_readResource(resource, &buffer);
@@ -4295,6 +4303,9 @@ void mjCTexture::LoadCustom(mjResource* resource,
w = pint[0];
h = pint[1];
// assume linear color space
is_srgb = false;
// check dimensions
if (w < 1 || h < 1) {
throw mjCError(this, "Non-PNG texture, assuming custom binary file format,\n"
@@ -4317,7 +4328,7 @@ void mjCTexture::LoadCustom(mjResource* resource,
// load from PNG or custom file, flip if specified
void mjCTexture::LoadFlip(std::string filename, const mjVFS* vfs,
std::vector<unsigned char>& image,
unsigned int& w, unsigned int& h) {
unsigned int& w, unsigned int& h, bool& is_srgb) {
std::string asset_type = GetAssetContentType(filename, content_type_);
// fallback to custom
@@ -4333,9 +4344,9 @@ void mjCTexture::LoadFlip(std::string filename, const mjVFS* vfs,
try {
if (asset_type == "image/png") {
LoadPNG(resource, image, w, h);
LoadPNG(resource, image, w, h, is_srgb);
} else {
LoadCustom(resource, image, w, h);
LoadCustom(resource, image, w, h, is_srgb);
}
mju_closeResource(resource);
} catch(mjCError err) {
@@ -4402,12 +4413,16 @@ void mjCTexture::LoadFlip(std::string filename, const mjVFS* vfs,
void mjCTexture::Load2D(std::string filename, const mjVFS* vfs) {
// load PNG or custom
unsigned int w, h;
bool is_srgb;
std::vector<unsigned char> image;
LoadFlip(filename, vfs, image, w, h);
LoadFlip(filename, vfs, image, w, h, is_srgb);
// assign size
width = w;
height = h;
if (colorspace == mjCOLORSPACE_AUTO) {
colorspace = is_srgb ? mjCOLORSPACE_SRGB : mjCOLORSPACE_LINEAR;
}
// allocate and copy data
std::int64_t size = static_cast<std::int64_t>(width)*height;
@@ -4435,8 +4450,13 @@ void mjCTexture::LoadCubeSingle(std::string filename, const mjVFS* vfs) {
// load PNG or custom
unsigned int w, h;
bool is_srgb;
std::vector<unsigned char> image;
LoadFlip(filename, vfs, image, w, h);
LoadFlip(filename, vfs, image, w, h, is_srgb);
if (colorspace == mjCOLORSPACE_AUTO) {
colorspace = is_srgb ? mjCOLORSPACE_SRGB : mjCOLORSPACE_LINEAR;
}
// check gridsize for compatibility
if (w/gridsize[1] != h/gridsize[0] || (w%gridsize[1]) || (h%gridsize[0])) {
@@ -4549,8 +4569,14 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
// load PNG or custom
unsigned int w, h;
bool is_srgb;
std::vector<unsigned char> image;
LoadFlip(filename.Str(), vfs, image, w, h);
LoadFlip(filename.Str(), vfs, image, w, h, is_srgb);
// assume all faces have the same colorspace
if (colorspace == mjCOLORSPACE_AUTO) {
colorspace = is_srgb ? mjCOLORSPACE_SRGB : mjCOLORSPACE_LINEAR;
}
// PNG must be square
if (w != h) {
+3 -3
View File
@@ -1328,14 +1328,14 @@ class mjCTexture : public mjCTexture_, private mjsTexture {
void LoadFlip(std::string filename, const mjVFS* vfs, // load and flip
std::vector<unsigned char>& image,
unsigned int& w, unsigned int& h);
unsigned int& w, unsigned int& h, bool& is_srgb);
void LoadPNG(mjResource* resource,
std::vector<unsigned char>& image,
unsigned int& w, unsigned int& h);
unsigned int& w, unsigned int& h, bool& is_srgb);
void LoadCustom(mjResource* resource,
std::vector<unsigned char>& image,
unsigned int& w, unsigned int& h);
unsigned int& w, unsigned int& h, bool& is_srgb);
bool clear_data_; // if true, data_ is empty and should be filled by Compile
};
+2
View File
@@ -35,6 +35,7 @@ extern const int jac_sz;
extern const int solver_sz;
extern const int equality_sz;
extern const int texture_sz;
extern const int colorspace_sz;
extern const int builtin_sz;
extern const int mark_sz;
extern const int dyn_sz;
@@ -60,6 +61,7 @@ extern const mjMap jac_map[];
extern const mjMap solver_map[];
extern const mjMap equality_map[];
extern const mjMap texture_map[];
extern const mjMap colorspace_map[];
extern const mjMap texrole_map[];
extern const mjMap builtin_map[];
extern const mjMap mark_map[];
+14 -2
View File
@@ -241,8 +241,8 @@ const char* MJCF[nMJCF][mjXATTRNUM] = {
{"<"},
{"bone", "*", "5", "body", "bindpos", "bindquat", "vertid", "vertweight"},
{">"},
{"texture", "*", "23", "name", "type", "content_type", "file", "gridsize", "gridlayout",
"fileright", "fileleft", "fileup", "filedown", "filefront", "fileback",
{"texture", "*", "24", "name", "type", "colorspace", "content_type", "file", "gridsize",
"gridlayout", "fileright", "fileleft", "fileup", "filedown", "filefront", "fileback",
"builtin", "rgb1", "rgb2", "mark", "markrgb", "random", "width", "height",
"hflip", "vflip", "nchannel"},
{"material", "*", "12", "name", "class", "texture", "texrepeat", "texuniform",
@@ -650,6 +650,15 @@ const mjMap texture_map[texture_sz] = {
};
// colorspace for texture
const int colorspace_sz = 3;
const mjMap colorspace_map[colorspace_sz] = {
{"auto", mjCOLORSPACE_AUTO},
{"linear", mjCOLORSPACE_LINEAR},
{"sRGB", mjCOLORSPACE_SRGB}
};
// builtin type for texture
const int builtin_sz = 4;
const mjMap builtin_map[builtin_sz] = {
@@ -3194,6 +3203,9 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
if (MapValue(elem, "type", &n, texture_map, texture_sz)) {
texture->type = (mjtTexture)n;
}
if (MapValue(elem, "colorspace", &n, colorspace_map, colorspace_sz)) {
texture->colorspace = (mjtColorSpace)n;
}
if (ReadAttrTxt(elem, "name", texname)) {
mjs_setString(texture->name, texname.c_str());
}
+1
View File
@@ -1488,6 +1488,7 @@ void mjXWriter::Asset(XMLElement* root) {
// write common attributes
WriteAttrKey(elem, "type", texture_map, texture_sz, texture->type);
WriteAttrKey(elem, "colorspace", colorspace_map, colorspace_sz, texture->colorspace);
WriteAttrTxt(elem, "name", texture->name);
// write builtin