render: skip textures the classic renderer can't upload

KTX textures crash the classic OpenGL renderer and the Python viewer with
"Number of channels not supported: 1": mjCTexture::LoadKTX stores the file as
an opaque single-channel blob for the Filament renderer, and mjr_uploadTexture
aborts via mju_error because the classic path only handles 3- or 4-channel
textures.

Skip textures the classic renderer can't upload (nchannel not 3 or 4) with a
warning instead of aborting, so the model still loads and the other textures
render. LoadKTX and the Filament path are unchanged. The guard also covers the
cube/skybox path, which previously over-read a single-channel blob as RGB.

Add a render_test.py regression test that builds a model with a KTX texture and
constructs an MjrContext; it aborts before this change and passes after.

Addresses #3343.
This commit is contained in:
Anas
2026-06-20 11:05:31 -04:00
parent a8eaccd2b6
commit ce025f0ec3
2 changed files with 41 additions and 3 deletions
+11 -3
View File
@@ -1365,6 +1365,16 @@ void mjr_uploadTexture(const mjModel* m, const mjrContext* con, int texid) {
int w = m->tex_width[texid];
float plane[4];
// the classic renderer only uploads 3- (RGB) or 4-channel (RGBA) textures. KTX
// and other encoded textures are single-channel blobs for the Filament renderer
// (see LoadKTX); skip them with a warning instead of aborting (issue #3343).
if (m->tex_nchannel[texid] != 3 && m->tex_nchannel[texid] != 4) {
mju_warning("texture %d: classic renderer can't upload %d-channel data, "
"skipping (KTX/encoded textures need the Filament renderer)",
texid, m->tex_nchannel[texid]);
return;
}
// 2D texture
if (m->tex_type[texid] == mjTEXTURE_2D) {
// OpenGL settings
@@ -1390,11 +1400,9 @@ void mjr_uploadTexture(const mjModel* m, const mjrContext* con, int texid) {
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) {
} else { // 4 channels, guaranteed by the channel check at the top of the function
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, internaltype, m->tex_width[texid],