Move texture block copy out of render context so it can be modified online via mjmodel

This commit is contained in:
Pedro Morais
2026-07-09 11:08:43 -04:00
parent bdeb7e7c4d
commit d43c3ed426
12 changed files with 54 additions and 95 deletions
-3
View File
@@ -583,9 +583,6 @@ Defined in `mjrender.h <https://github.com/google-deepmind/mujoco/blob/main/incl
* - ``mjMAXTEXTURE`` * - ``mjMAXTEXTURE``
- 1000 - 1000
- Maximum number of textures allowed. - Maximum number of textures allowed.
* - ``mjMAXMATERIAL``
- 1000
- Maximum number of materials with textures.
.. _glNumericUI: .. _glNumericUI:
+9
View File
@@ -32,6 +32,15 @@ General
exclusively in the compressed sparse row (CSR) format ``mjData.M``. exclusively in the compressed sparse row (CSR) format ``mjData.M``.
- :ref:`mju_round` now breaks ties away from zero rather than towards :math:`+\infty`. This only affects - :ref:`mju_round` now breaks ties away from zero rather than towards :math:`+\infty`. This only affects
negative half-integers, e.g. ``mju_round(-2.5)`` now returns -3 rather than -2. negative half-integers, e.g. ``mju_round(-2.5)`` now returns -3 rather than -2.
- Removed the ``mjMAXMATERIAL`` constant. The classic renderer no longer caps the number of textured materials.
.. admonition:: Breaking ABI changes
:class: attention
- Removed ``mat_texid``, ``mat_texuniform`` and ``mat_texrepeat`` from ``mjrContext``; the equivalent per-geom fields
``texid``, ``texuniform`` and ``texrepeat`` were added to ``mjvGeom``, populated from the model each frame by
:ref:`mjv_updateScene`. The classic renderer now resolves them live, so runtime material changes (e.g. domain
randomization) no longer require rebuilding the render context.
Version 3.10.0 (June 22, 2026) Version 3.10.0 (June 22, 2026)
------------------------------ ------------------------------
-6
View File
@@ -24,7 +24,6 @@ extern "C" {
#define mjNAUX 10 // number of auxiliary buffers #define mjNAUX 10 // number of auxiliary buffers
#define mjMAXTEXTURE 1000 // maximum number of textures #define mjMAXTEXTURE 1000 // maximum number of textures
#define mjMAXMATERIAL 1000 // maximum number of materials with textures
//---------------------------------- primitive types (mjt) ----------------------------------------- //---------------------------------- primitive types (mjt) -----------------------------------------
@@ -170,11 +169,6 @@ typedef struct mjrContext_ { // custom OpenGL context
unsigned int auxColor[mjNAUX]; // auxiliary color buffer unsigned int auxColor[mjNAUX]; // auxiliary color buffer
unsigned int auxColor_r[mjNAUX]; // auxiliary color buffer for resolving unsigned int auxColor_r[mjNAUX]; // auxiliary color buffer for resolving
// materials with textures
int mat_texid[mjMAXMATERIAL*mjNTEXROLE]; // material texture ids (-1: no texture)
int mat_texuniform[mjMAXMATERIAL]; // uniform cube mapping
float mat_texrepeat[mjMAXMATERIAL*2]; // texture repetition for 2d mapping
// texture objects and info // texture objects and info
int ntexture; // number of allocated textures int ntexture; // number of allocated textures
int textureType[mjMAXTEXTURE]; // type of texture (mjtTexture) (ntexture) int textureType[mjMAXTEXTURE]; // type of texture (mjtTexture) (ntexture)
+3
View File
@@ -229,6 +229,8 @@ typedef struct mjvGeom_ { // abstract geom
int objid; // mujoco object id; -1 for decor int objid; // mujoco object id; -1 for decor
int category; // visual category int category; // visual category
int matid; // material id; -1: no textured material int matid; // material id; -1: no textured material
int texid; // texture id; -1: none
int texuniform; // uniform cube mapping
int texcoord; // mesh or flex geom has texture coordinates int texcoord; // mesh or flex geom has texture coordinates
int segid; // segmentation id; -1: not shown int segid; // segmentation id; -1: not shown
@@ -243,6 +245,7 @@ typedef struct mjvGeom_ { // abstract geom
float specular; // specular coef float specular; // specular coef
float shininess; // shininess coef float shininess; // shininess coef
float reflectance; // reflectance coef float reflectance; // reflectance coef
float texrepeat[2]; // texture repetition for 2d mapping
char label[100]; // text label char label[100]; // text label
+18 -24
View File
@@ -10140,6 +10140,16 @@ STRUCTS: Mapping[str, StructDecl] = dict([
type=ValueType(name='int'), type=ValueType(name='int'),
doc='material id; -1: no textured material', doc='material id; -1: no textured material',
), ),
StructFieldDecl(
name='texid',
type=ValueType(name='int'),
doc='texture id; -1: none',
),
StructFieldDecl(
name='texuniform',
type=ValueType(name='int'),
doc='uniform cube mapping',
),
StructFieldDecl( StructFieldDecl(
name='texcoord', name='texcoord',
type=ValueType(name='int'), type=ValueType(name='int'),
@@ -10202,6 +10212,14 @@ STRUCTS: Mapping[str, StructDecl] = dict([
type=ValueType(name='float'), type=ValueType(name='float'),
doc='reflectance coef', doc='reflectance coef',
), ),
StructFieldDecl(
name='texrepeat',
type=ArrayType(
inner_type=ValueType(name='float'),
extents=(2,),
),
doc='texture repetition for 2d mapping',
),
StructFieldDecl( StructFieldDecl(
name='label', name='label',
type=ArrayType( type=ArrayType(
@@ -11178,30 +11196,6 @@ STRUCTS: Mapping[str, StructDecl] = dict([
), ),
doc='auxiliary color buffer for resolving', doc='auxiliary color buffer for resolving',
), ),
StructFieldDecl(
name='mat_texid',
type=ArrayType(
inner_type=ValueType(name='int'),
extents=(10000,),
),
doc='material texture ids (-1: no texture)',
),
StructFieldDecl(
name='mat_texuniform',
type=ArrayType(
inner_type=ValueType(name='int'),
extents=(1000,),
),
doc='uniform cube mapping',
),
StructFieldDecl(
name='mat_texrepeat',
type=ArrayType(
inner_type=ValueType(name='float'),
extents=(2000,),
),
doc='texture repetition for 2d mapping',
),
StructFieldDecl( StructFieldDecl(
name='ntexture', name='ntexture',
type=ValueType(name='int'), type=ValueType(name='int'),
-12
View File
@@ -54,9 +54,6 @@ class MjWrapper<raw::MjrContext> : public WrapperBase<raw::MjrContext> {
X(auxFBO_r); X(auxFBO_r);
X(auxColor); X(auxColor);
X(auxColor_r); X(auxColor_r);
X(mat_texid);
X(mat_texuniform);
X(mat_texrepeat);
X(textureType); X(textureType);
X(texture); X(texture);
X(skinvertVBO); X(skinvertVBO);
@@ -98,9 +95,6 @@ MjrContextWrapper::MjWrapper()
X(auxFBO_r), X(auxFBO_r),
X(auxColor), X(auxColor),
X(auxColor_r), X(auxColor_r),
X(mat_texid),
X(mat_texuniform),
X(mat_texrepeat),
X(textureType), X(textureType),
X(texture), X(texture),
X_SKIN(skinvertVBO), X_SKIN(skinvertVBO),
@@ -125,9 +119,6 @@ MjrContextWrapper::MjWrapper(const MjModelWrapper& model, int fontscale)
X(auxFBO_r), X(auxFBO_r),
X(auxColor), X(auxColor),
X(auxColor_r), X(auxColor_r),
X(mat_texid),
X(mat_texuniform),
X(mat_texrepeat),
X(textureType), X(textureType),
X(texture), X(texture),
X_SKIN(skinvertVBO), X_SKIN(skinvertVBO),
@@ -228,9 +219,6 @@ PYBIND11_MODULE(_render, pymodule, pybind11::mod_gil_not_used()) {
X(auxFBO_r); X(auxFBO_r);
X(auxColor); X(auxColor);
X(auxColor_r); X(auxColor_r);
X(mat_texid);
X(mat_texuniform);
X(mat_texrepeat);
X(textureType); X(textureType);
X(texture); X(texture);
X(skinvertVBO); X(skinvertVBO);
+3
View File
@@ -1185,6 +1185,8 @@ This is useful for example when the MJB is not available as a file on disk.)"));
X(objid); X(objid);
X(category); X(category);
X(matid); X(matid);
X(texid);
X(texuniform);
X(texcoord); X(texcoord);
X(segid); X(segid);
X(emission); X(emission);
@@ -1201,6 +1203,7 @@ This is useful for example when the MJB is not available as a file on disk.)"));
X(pos); X(pos);
X(mat); X(mat);
X(rgba); X(rgba);
X(texrepeat);
#undef X #undef X
DefinePyStr(mjvGeom, "label", &raw::MjvGeom::label); DefinePyStr(mjvGeom, "label", &raw::MjvGeom::label);
+1
View File
@@ -832,6 +832,7 @@ class MjWrapper<raw::MjvGeom> : public WrapperBase<raw::MjvGeom> {
X(pos); X(pos);
X(mat); X(mat);
X(rgba); X(rgba);
X(texrepeat);
#undef X #undef X
}; };
+4 -2
View File
@@ -1193,7 +1193,8 @@ MjvGeomWrapper::MjWrapper()
static_assert(sizeof(ptr_->mat) == sizeof(ptr_->mat[0]) * 9); static_assert(sizeof(ptr_->mat) == sizeof(ptr_->mat[0]) * 9);
return InitPyArray(std::array{3, 3}, ptr_->mat, owner_); return InitPyArray(std::array{3, 3}, ptr_->mat, owner_);
}()), }()),
X(rgba) { X(rgba),
X(texrepeat) {
mjv_initGeom(ptr_, mjGEOM_NONE, nullptr, nullptr, nullptr, nullptr); mjv_initGeom(ptr_, mjGEOM_NONE, nullptr, nullptr, nullptr, nullptr);
} }
@@ -1205,7 +1206,8 @@ MjvGeomWrapper::MjWrapper(raw::MjvGeom* ptr, py::handle owner)
static_assert(sizeof(ptr_->mat) == sizeof(ptr_->mat[0]) * 9); static_assert(sizeof(ptr_->mat) == sizeof(ptr_->mat[0]) * 9);
return InitPyArray(std::array{3, 3}, ptr_->mat, owner_); return InitPyArray(std::array{3, 3}, ptr_->mat, owner_);
}()), }()),
X(rgba) {} X(rgba),
X(texrepeat) {}
#undef X #undef X
MjvGeomWrapper::MjWrapper(const MjvGeomWrapper& other) : MjvGeomWrapper() { MjvGeomWrapper::MjWrapper(const MjvGeomWrapper& other) : MjvGeomWrapper() {
+8
View File
@@ -241,6 +241,10 @@ static void setMaterial(const mjModel* m, mjvGeom* geom, int matid, const float*
// set texture // set texture
if (flags[mjVIS_TEXTURE] && matid >= 0) { if (flags[mjVIS_TEXTURE] && matid >= 0) {
geom->matid = matid; geom->matid = matid;
geom->texid = m->mat_texid[matid*mjNTEXROLE + mjTEXROLE_RGB];
geom->texuniform = m->mat_texuniform[matid];
geom->texrepeat[0] = m->mat_texrepeat[2*matid];
geom->texrepeat[1] = m->mat_texrepeat[2*matid+1];
} }
// scale alpha for dynamic geoms only // scale alpha for dynamic geoms only
@@ -379,6 +383,10 @@ void mjv_initGeom(mjvGeom* geom, int type, const mjtNum* size,
// set defaults that cannot be assigned via this function // set defaults that cannot be assigned via this function
geom->dataid = -1; geom->dataid = -1;
geom->matid = -1; geom->matid = -1;
geom->texid = -1;
geom->texuniform = 0;
geom->texrepeat[0] = 0;
geom->texrepeat[1] = 0;
geom->texcoord = 0; geom->texcoord = 0;
geom->emission = 0; geom->emission = 0;
geom->specular = 0.5; geom->specular = 0.5;
-40
View File
@@ -14,7 +14,6 @@
#include "render/classic/render_context.h" #include "render/classic/render_context.h"
#include <inttypes.h> // IWYU pragma: keep
#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -1299,44 +1298,6 @@ static void makeFont(mjrContext* con, int fontscale) {
} }
} }
// make materials, just for those that have textures
static void makeMaterial(const mjModel* m, mjrContext* con) {
memset(con->mat_texid, -1, sizeof(con->mat_texid));
memset(con->mat_texuniform, 0, sizeof(con->mat_texuniform));
memset(con->mat_texrepeat, 0, sizeof(con->mat_texrepeat));
// find skybox texture
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, got %" PRId64,
mjMAXMATERIAL-1, m->nmat);
}
for (int j=0; j < mjNTEXROLE; j++) {
con->mat_texid[mjNTEXROLE * (mjMAXMATERIAL-1) + j] = -1;
}
con->mat_texid[mjNTEXROLE * (mjMAXMATERIAL-1) + mjTEXROLE_RGB] = i;
break;
}
}
if (m->nmat >= mjMAXMATERIAL-1) {
mju_error("Maximum number of materials is %d, got %" PRId64, mjMAXMATERIAL, m->nmat);
}
for (int i=0; i < m->nmat; i++) {
if (m->mat_texid[i*mjNTEXROLE + mjTEXROLE_RGB] >= 0) {
for (int j=0; j < mjNTEXROLE; j++) {
con->mat_texid[i*mjNTEXROLE + j] = m->mat_texid[i*mjNTEXROLE + j];
}
con->mat_texuniform[i] = m->mat_texuniform[i];
con->mat_texrepeat[2*i] = m->mat_texrepeat[2*i];
con->mat_texrepeat[2*i+1] = m->mat_texrepeat[2*i+1];
}
}
}
// make textures // make textures
static void makeTexture(const mjModel* m, mjrContext* con) { static void makeTexture(const mjModel* m, mjrContext* con) {
// checks size // checks size
@@ -1639,7 +1600,6 @@ void mjr_makeContext_offSize(const mjModel* m, mjrContext* con, int fontscale,
// make everything // make everything
makeOff(con); makeOff(con);
makeShadow(m, con); makeShadow(m, con);
makeMaterial(m, con);
makeTexture(m, con); makeTexture(m, con);
makePlane(m, con); makePlane(m, con);
makeMesh(m, con); makeMesh(m, con);
+8 -8
View File
@@ -64,7 +64,7 @@ static void settexture(int type, int state, const mjrContext* con, const mjvGeom
int texid = -1; int texid = -1;
if (geom) { if (geom) {
if (geom->matid >= 0) { if (geom->matid >= 0) {
texid = con->mat_texid[mjNTEXROLE * geom->matid + mjTEXROLE_RGB]; texid = geom->texid;
} }
} }
@@ -119,8 +119,8 @@ static void settexture(int type, int state, const mjrContext* con, const mjvGeom
glBindTexture(GL_TEXTURE_2D, con->texture[texid]); glBindTexture(GL_TEXTURE_2D, con->texture[texid]);
// determine scaling, adjust for pre-scaled geoms // determine scaling, adjust for pre-scaled geoms
scl[0] = con->mat_texrepeat[geom->matid*2]; scl[0] = geom->texrepeat[0];
scl[1] = con->mat_texrepeat[geom->matid*2+1]; scl[1] = geom->texrepeat[1];
if (geom->dataid >= 0) { if (geom->dataid >= 0) {
if (geom->size[0] > 0) { if (geom->size[0] > 0) {
scl[0] = scl[0] / mju_max(mjMINVAL, geom->size[0]); scl[0] = scl[0] / mju_max(mjMINVAL, geom->size[0]);
@@ -132,7 +132,7 @@ static void settexture(int type, int state, const mjrContext* con, const mjvGeom
} }
// uniform: repeat relative to spatial units rather than object // uniform: repeat relative to spatial units rather than object
if (con->mat_texuniform[geom->matid]) { if (geom->texuniform) {
if (geom->size[0] > 0) { if (geom->size[0] > 0) {
scl[0] = scl[0] * geom->size[0]; scl[0] = scl[0] * geom->size[0];
} }
@@ -171,11 +171,11 @@ static void settexture(int type, int state, const mjrContext* con, const mjvGeom
// set mapping : cube // set mapping : cube
if (type == mjtexREGULAR) { if (type == mjtexREGULAR) {
mjr_setf4(plane, con->mat_texuniform[geom->matid] ? geom->size[0] : 1, 0, 0, 0); mjr_setf4(plane, geom->texuniform ? geom->size[0] : 1, 0, 0, 0);
glTexGenfv(GL_S, GL_OBJECT_PLANE, plane); glTexGenfv(GL_S, GL_OBJECT_PLANE, plane);
mjr_setf4(plane, 0, con->mat_texuniform[geom->matid] ? geom->size[1] : 1, 0, 0); mjr_setf4(plane, 0, geom->texuniform ? geom->size[1] : 1, 0, 0);
glTexGenfv(GL_T, GL_OBJECT_PLANE, plane); glTexGenfv(GL_T, GL_OBJECT_PLANE, plane);
mjr_setf4(plane, 0, 0, con->mat_texuniform[geom->matid] ? geom->size[2] : 1, 0); mjr_setf4(plane, 0, 0, geom->texuniform ? geom->size[2] : 1, 0);
glTexGenfv(GL_R, GL_OBJECT_PLANE, plane); glTexGenfv(GL_R, GL_OBJECT_PLANE, plane);
} }
@@ -1317,7 +1317,7 @@ void mjr_render(mjrRect viewport, mjvScene* scn, const mjrContext* con) {
if (con->textureType[i] == mjTEXTURE_SKYBOX) { if (con->textureType[i] == mjTEXTURE_SKYBOX) {
// save first skybox texture id in tempgeom // save first skybox texture id in tempgeom
memset(&tempgeom, 0, sizeof(mjvGeom)); memset(&tempgeom, 0, sizeof(mjvGeom));
tempgeom.matid = mjMAXMATERIAL - 1; tempgeom.texid = i;
// modify settings // modify settings
glDisable(GL_LIGHTING); glDisable(GL_LIGHTING);