From 373b4c04378b1b6f502711ef1e5bf0e5af376d7d Mon Sep 17 00:00:00 2001 From: Google DeepMind Date: Thu, 22 May 2025 05:46:49 -0700 Subject: [PATCH] Support image/ktx format for textures. PiperOrigin-RevId: 761926074 Change-Id: I6a312634b5bdc706ea5166bc3b2be273e14c1c8a --- doc/XMLreference.rst | 37 ++++++++++++++++++++----------------- src/engine/engine_io.c | 2 +- src/user/user_objects.cc | 29 ++++++++++++++++++++++++++++- src/user/user_objects.h | 3 +++ 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/doc/XMLreference.rst b/doc/XMLreference.rst index b2ce9370..104a92ef 100644 --- a/doc/XMLreference.rst +++ b/doc/XMLreference.rst @@ -1464,16 +1464,19 @@ still be specified here but this functionality is now deprecated and will be rem :el-prefix:`asset/` |-| **texture** (*) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -| This element creates a texture asset, which is then referenced from a :ref:`material ` asset, which is - finally referenced from a model element that needs to be textured. MuJoCo provides access to the texture mapping - mechanism in OpenGL. Texture coordinates are generated automatically in GL_OBJECT_PLANE mode, using either 2D or cube - mapping. MIP maps are always enabled in GL_LINEAR_MIPMAP_LINEAR mode. The texture color is combined with the object - color in GL_MODULATE mode. The texture data can be loaded from PNG files, with provisions for loading cube and skybox - textures. Alternatively the data can be generated by the compiler as a procedural texture. Because different texture - types require different parameters, only a subset of the attributes below are used for any given texture. -| A second file format is supported for loading textures, in addition to PNG. If the file name extension is - different from .png or .PNG, or if the ``content_type`` attribute is set to ``image/vnd.mujoco.texture``, then MuJoCo - assumes that the texture is in this format. This is a custom binary file format, containing the following data: + This element creates a texture asset, which is then referenced from a :ref:`material ` asset, which + is finally referenced from a model element that needs to be textured. + + The texture data can be loaded from files or can be generated by the compiler as a procedural texture. Because + different texture types require different parameters, only a subset of the attributes below are used for any given + texture. Provisions are provided for loading cube and skybox textures from individual image files. + + Currently, three file formats are supported for loading textures: PNG, KTX, and a custom MuJoCo texture format. The + loader will use the extension of the file name to determine which format to use, defaulting to the custom format if + the extension is not recognized. Alternatively, the content_type attribute can be used to specify the format + explicitly. Only ``image/png``, ``image/ktx``, or ``image/vnd.mujoco.texture`` are supported. + + The custom MuJoCo format is assumed to be a binary file containing the following data: .. code:: Text @@ -1537,8 +1540,8 @@ still be specified here but this functionality is now deprecated and will be rem :at:`content_type`: :at-val:`string, optional` If the file attribute is specified, then this sets the `Media Type `_ (formerly known as MIME types) of the - file to be loaded. Any filename extensions will be ignored. Currently ``image/png`` and ``image/vnd.mujoco.texture`` - are supported. + file to be loaded. Any filename extensions will be ignored. Currently ``image/png``, ``image/ktx``, and + ``image/vnd.mujoco.texture`` are supported. .. _asset-texture-file: @@ -1786,11 +1789,11 @@ properties are grouped together. .. _asset-material-rgba: :at:`rgba`: :at-val:`real(4), "1 1 1 1"` - Color and transparency of the material. All components should be in the range [0 1]. Note that textures are applied - in GL_MODULATE mode, meaning that the texture color and the color specified here are multiplied component-wise. Thus - the default value of "1 1 1 1" has the effect of leaving the texture unchanged. When the material is applied to a - 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. + Color and transparency of the material. All components should be in the range [0 1]. Note that the texture color (if + assigned) and the color specified here are multiplied component-wise. Thus the default value of "1 1 1 1" has the + effect of leaving the texture unchanged. When the material is applied to a 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-layer: diff --git a/src/engine/engine_io.c b/src/engine/engine_io.c index 49325b30..9a9ad366 100644 --- a/src/engine/engine_io.c +++ b/src/engine/engine_io.c @@ -2406,7 +2406,7 @@ const char* mj_validateReferences(const mjModel* m) { } } for (int i=0; i < m->ntex; i++) { - int tex_adr = m->tex_adr[i] + 3*m->tex_height[i]*m->tex_width[i]; + int tex_adr = m->tex_adr[i] + m->tex_nchannel[i]*m->tex_height[i]*m->tex_width[i]; if (tex_adr > m->ntexdata || m->tex_adr[i] < 0) { return "Invalid model: tex_adr out of bounds."; } diff --git a/src/user/user_objects.cc b/src/user/user_objects.cc index 2e2c63b2..688c9729 100644 --- a/src/user/user_objects.cc +++ b/src/user/user_objects.cc @@ -4283,6 +4283,28 @@ void mjCTexture::LoadPNG(mjResource* resource, image = png_image.MoveData(); } +// load KTX file +void mjCTexture::LoadKTX(mjResource* resource, + std::vector& image, unsigned int& w, + unsigned int& h, bool& is_srgb) { + const void* buffer = 0; + int buffer_sz = mju_readResource(resource, &buffer); + + // still not found + if (buffer_sz < 0) { + throw mjCError(this, "could not read texture file '%s'", resource->name); + } else if (!buffer_sz) { + throw mjCError(this, "texture file is empty: '%s'", resource->name); + } + + w = buffer_sz; + h = 1; + is_srgb = false; + + image.resize(buffer_sz); + memcpy(image.data(), buffer, buffer_sz); +} + // load custom file void mjCTexture::LoadCustom(mjResource* resource, std::vector& image, @@ -4336,7 +4358,7 @@ void mjCTexture::LoadFlip(std::string filename, const mjVFS* vfs, asset_type = "image/vnd.mujoco.texture"; } - if (asset_type != "image/png" && asset_type != "image/vnd.mujoco.texture") { + if (asset_type != "image/png" && asset_type != "image/ktx" && asset_type != "image/vnd.mujoco.texture") { throw mjCError(this, "unsupported content type: '%s'", asset_type.c_str()); } @@ -4345,6 +4367,11 @@ void mjCTexture::LoadFlip(std::string filename, const mjVFS* vfs, try { if (asset_type == "image/png") { LoadPNG(resource, image, w, h, is_srgb); + } else if (asset_type == "image/ktx") { + if (hflip || vflip) { + throw mjCError(this, "cannot flip KTX textures"); + } + LoadKTX(resource, image, w, h, is_srgb); } else { LoadCustom(resource, image, w, h, is_srgb); } diff --git a/src/user/user_objects.h b/src/user/user_objects.h index 994cb542..db01349a 100644 --- a/src/user/user_objects.h +++ b/src/user/user_objects.h @@ -1333,6 +1333,9 @@ class mjCTexture : public mjCTexture_, private mjsTexture { void LoadPNG(mjResource* resource, std::vector& image, unsigned int& w, unsigned int& h, bool& is_srgb); + void LoadKTX(mjResource* resource, + std::vector& image, + unsigned int& w, unsigned int& h, bool& is_srgb); void LoadCustom(mjResource* resource, std::vector& image, unsigned int& w, unsigned int& h, bool& is_srgb);