Add texture attribute to lights.

PiperOrigin-RevId: 763739805
Change-Id: I333ed2ac68f30f79f688ca01d7b5fd6bf2d836c9
This commit is contained in:
Google DeepMind
2025-05-27 05:03:04 -07:00
committed by Copybara-Service
parent 441ca838a1
commit 568620dd2f
18 changed files with 106 additions and 4 deletions
+5
View File
@@ -2961,6 +2961,11 @@ Attributes may be applied or ignored depending on the lighting model being used.
The color of the light. For the Phong (default) lighting model, this defines the diffuse color of
the light.
.. _body-light-texture:
:at:`texture`: :at-val:`string, optional`
The texture to use for image-based lighting. This is unused by the default Phong lighting model.
.. _body-light-intensity:
:at:`intensity`: :at-val:`real, "1000.0"`
+1 -1
View File
@@ -321,7 +321,7 @@
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`cutoff<body-light-cutoff>` | :ref:`exponent<body-light-exponent>` | :ref:`ambient<body-light-ambient>` | :ref:`diffuse<body-light-diffuse>` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`specular<body-light-specular>` | :ref:`mode<body-light-mode>` | :ref:`target<body-light-target>` | | |
| | | | :ref:`specular<body-light-specular>` | :ref:`mode<body-light-mode>` | :ref:`target<body-light-target>` | :ref:`texture<body-light-texture>` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_| body |br| |_| |L| | | .. table:: |
+3
View File
@@ -1186,6 +1186,7 @@ struct mjModel_ {
int* light_bodyid; // id of light's body (nlight x 1)
int* light_targetbodyid; // id of targeted body; -1: none (nlight x 1)
int* light_type; // spot, directional, etc. (mjtLightType) (nlight x 1)
int* light_texid; // texture id for image lights (nlight x 1)
mjtByte* light_castshadow; // does light cast shadows (nlight x 1)
float* light_bulbradius; // light radius for soft shadows (nlight x 1)
float* light_intensity; // intensity, in candela (nlight x 1)
@@ -2043,6 +2044,7 @@ typedef struct mjsLight_ { // light specification
// intrinsics
mjtByte active; // is light active
mjtLightType type; // type of light
mjString* texture; // texture name for image lights
mjtByte castshadow; // does light cast shadows
float bulbradius; // bulb radius, for soft shadows
float intensity; // intensity, in candelas
@@ -2847,6 +2849,7 @@ struct mjvLight_ { // OpenGL light
float pos[3]; // position rel. to body frame
float dir[3]; // direction rel. to body frame
int type; // type (mjtLightType)
int texid; // texture id for image lights
float attenuation[3]; // OpenGL attenuation (quadratic model)
float cutoff; // OpenGL cutoff
float exponent; // OpenGL exponent
+1
View File
@@ -858,6 +858,7 @@ struct mjModel_ {
int* light_bodyid; // id of light's body (nlight x 1)
int* light_targetbodyid; // id of targeted body; -1: none (nlight x 1)
int* light_type; // spot, directional, etc. (mjtLightType) (nlight x 1)
int* light_texid; // texture id for image lights (nlight x 1)
mjtByte* light_castshadow; // does light cast shadows (nlight x 1)
float* light_bulbradius; // light radius for soft shadows (nlight x 1)
float* light_intensity; // intensity, in candela (nlight x 1)
+1
View File
@@ -395,6 +395,7 @@ typedef struct mjsLight_ { // light specification
// intrinsics
mjtByte active; // is light active
mjtLightType type; // type of light
mjString* texture; // texture name for image lights
mjtByte castshadow; // does light cast shadows
float bulbradius; // bulb radius, for soft shadows
float intensity; // intensity, in candelas
+1
View File
@@ -262,6 +262,7 @@ struct mjvLight_ { // OpenGL light
float pos[3]; // position rel. to body frame
float dir[3]; // direction rel. to body frame
int type; // type (mjtLightType)
int texid; // texture id for image lights
float attenuation[3]; // OpenGL attenuation (quadratic model)
float cutoff; // OpenGL cutoff
float exponent; // OpenGL exponent
+1
View File
@@ -300,6 +300,7 @@
X ( int, light_bodyid, nlight, 1 ) \
X ( int, light_targetbodyid, nlight, 1 ) \
X ( int, light_type, nlight, 1 ) \
X ( int, light_texid, nlight, 1 ) \
X ( mjtByte, light_castshadow, nlight, 1 ) \
X ( float, light_bulbradius, nlight, 1 ) \
X ( float, light_intensity, nlight, 1 ) \
+20
View File
@@ -2227,6 +2227,14 @@ STRUCTS: Mapping[str, StructDecl] = dict([
doc='spot, directional, etc. (mjtLightType)',
array_extent=('nlight',),
),
StructFieldDecl(
name='light_texid',
type=PointerType(
inner_type=ValueType(name='int'),
),
doc='texture id for image lights',
array_extent=('nlight',),
),
StructFieldDecl(
name='light_castshadow',
type=PointerType(
@@ -6702,6 +6710,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
type=ValueType(name='int'),
doc='type (mjtLightType)',
),
StructFieldDecl(
name='texid',
type=ValueType(name='int'),
doc='texture id for image lights',
),
StructFieldDecl(
name='attenuation',
type=ArrayType(
@@ -8996,6 +9009,13 @@ STRUCTS: Mapping[str, StructDecl] = dict([
type=ValueType(name='mjtLightType'),
doc='type of light',
),
StructFieldDecl(
name='texture',
type=PointerType(
inner_type=ValueType(name='mjString'),
),
doc='texture name for image lights',
),
StructFieldDecl(
name='castshadow',
type=ValueType(name='mjtByte'),
+1
View File
@@ -1074,6 +1074,7 @@ This is useful for example when the MJB is not available as a file on disk.)"));
X(exponent);
X(headlight);
X(type);
X(texid);
X(castshadow);
X(bulbradius);
X(intensity);
+5
View File
@@ -2143,8 +2143,12 @@ void mjv_makeLights(const mjModel* m, const mjData* d, mjvScene* scn) {
// set default properties
memset(thislight, 0, sizeof(mjvLight));
thislight->headlight = 1;
thislight->texid = -1;
thislight->type = mjLIGHT_DIRECTIONAL;
thislight->castshadow = 0;
thislight->bulbradius = 0.02;
thislight->intensity = 1000;
thislight->range = 10;
// compute head position and gaze direction in model space
mjtNum hpos[3], hfwd[3];
@@ -2170,6 +2174,7 @@ void mjv_makeLights(const mjModel* m, const mjData* d, mjvScene* scn) {
// copy properties
memset(thislight, 0, sizeof(mjvLight));
thislight->type = m->light_type[i];
thislight->texid = m->light_texid[i];
thislight->castshadow = m->light_castshadow[i];
thislight->bulbradius = m->light_bulbradius[i];
thislight->intensity = m->light_intensity[i];
+1
View File
@@ -2670,6 +2670,7 @@ void mjCModel::CopyTree(mjModel* m) {
m->light_mode[lid] = (int)pl->mode;
m->light_targetbodyid[lid] = pl->targetbodyid;
m->light_type[lid] = pl->type;
m->light_texid[lid] = pl->texid;
m->light_castshadow[lid] = (mjtByte)pl->castshadow;
m->light_active[lid] = (mjtByte)pl->active;
mjuu_copyvec(m->light_pos+3*lid, pl->pos, 3);
+18
View File
@@ -3553,7 +3553,10 @@ mjCLight::mjCLight(mjCModel* _model, mjCDef* _def) {
// clear private variables
body = 0;
targetbodyid = -1;
texid = -1;
spec_targetbody_.clear();
spec_texture_.clear();
// reset to default if given
if (_def) {
@@ -3593,6 +3596,7 @@ void mjCLight::PointToLocal() {
spec.element = static_cast<mjsElement*>(this);
spec.name = &name;
spec.targetbody = &spec_targetbody_;
spec.texture = &spec_texture_;
spec.info = &info;
targetbody = nullptr;
}
@@ -3604,6 +3608,9 @@ void mjCLight::NameSpace(const mjCModel* m) {
if (!spec_targetbody_.empty()) {
spec_targetbody_ = m->prefix + spec_targetbody_ + m->suffix;
}
if (!spec_texture_.empty()) {
spec_texture_ = m->prefix + spec_texture_ + m->suffix;
}
}
@@ -3611,6 +3618,7 @@ void mjCLight::NameSpace(const mjCModel* m) {
void mjCLight::CopyFromSpec() {
*static_cast<mjsLight*>(this) = spec;
targetbody_ = spec_targetbody_;
texture_ = spec_texture_;
}
@@ -3643,6 +3651,16 @@ void mjCLight::Compile(void) {
throw mjCError(this, "unknown target body in light");
}
}
// get texture
if (!texture_.empty()) {
mjCTexture* tex = (mjCTexture*)model->FindObject(mjOBJ_TEXTURE, texture_);
if (tex) {
texid = tex->id;
} else {
throw mjCError(this, "unknown target body in light");
}
}
}
+4
View File
@@ -793,6 +793,9 @@ class mjCLight_ : public mjCBase {
protected:
mjCBody* body; // light's body
int targetbodyid; // id of target body; -1: none
int texid; // id of texture; -1: none
std::string texture_;
std::string spec_texture_;
std::string targetbody_;
std::string spec_targetbody_;
};
@@ -814,6 +817,7 @@ class mjCLight : public mjCLight_, private mjsLight {
// used by mjXWriter and mjCModel
const std::string& get_targetbody() const { return targetbody_; }
const std::string& get_texture() const { return texture_; }
void SetParent(mjCBody* _body) { body = _body; }
mjCBody* GetParent() const { return body; }
+6 -3
View File
@@ -281,9 +281,9 @@ const char* MJCF[nMJCF][mjXATTRNUM] = {
{"camera", "*", "20", "name", "class", "orthographic", "fovy", "ipd", "resolution", "pos",
"quat", "axisangle", "xyaxes", "zaxis", "euler", "mode", "target",
"focal", "focalpixel", "principal", "principalpixel", "sensorsize", "user"},
{"light", "*", "19", "name", "class", "directional", "type", "castshadow", "active",
{"light", "*", "20", "name", "class", "directional", "type", "castshadow", "active",
"pos", "dir", "bulbradius", "intensity", "range", "attenuation", "cutoff",
"exponent", "ambient", "diffuse", "specular", "mode", "target"},
"exponent", "ambient", "diffuse", "specular", "mode", "target", "texture"},
{"plugin", "*", "2", "plugin", "instance"},
{"<"},
{"config", "*", "2", "key", "value"},
@@ -1864,12 +1864,15 @@ void mjXReader::OneCamera(XMLElement* elem, mjsCamera* camera) {
void mjXReader::OneLight(XMLElement* elem, mjsLight* light) {
int n;
bool has_directional = false;
string text, name, targetbody;
string text, name, texture, targetbody;
// read attributes
if (ReadAttrTxt(elem, "name", name)) {
mjs_setString(light->name, name.c_str());
}
if (ReadAttrTxt(elem, "texture", texture)) {
mjs_setString(light->texture, texture.c_str());
}
if (ReadAttrTxt(elem, "target", targetbody)) {
mjs_setString(light->targetbody, targetbody.c_str());
}
+1
View File
@@ -608,6 +608,7 @@ void mjXWriter::OneLight(XMLElement* elem, const mjCLight* light, mjCDef* def,
WriteAttr(elem, "intensity", 1, &light->intensity, &def->Light().intensity);
WriteAttr(elem, "range", 1, &light->range, &def->Light().range);
WriteAttrKey(elem, "type", lighttype_map, lighttype_sz, light->type, def->Light().type);
WriteAttrTxt(elem, "texture", light->get_texture());
WriteAttrKey(elem, "castshadow", bool_map, 2, light->castshadow, def->Light().castshadow);
WriteAttrKey(elem, "active", bool_map, 2, light->active, def->Light().active);
WriteAttr(elem, "attenuation", 3, light->attenuation, def->Light().attenuation);
+23
View File
@@ -2294,6 +2294,29 @@ TEST_F(UserObjectsTest, FrameTransformsLight) {
mj_deleteModel(m);
}
TEST_F(ContentTypeTest, ImageLightsReferenceTexture) {
static constexpr char xml[] = R"(
<mujoco>
<asset>
<texture name="texture" type="cube" builtin="flat" mark="cross" width="8"
rgb1="0.8 0.6 0.4" rgb2="0.8 0.6 0.4" markrgb="1 1 1"/>
</asset>
<worldbody>
<light type="image" texture="texture"/>
</worldbody>
</mujoco>
)";
std::array<char, 1024> error;
mjModel* m = LoadModelFromString(xml, error.data(), error.size());
EXPECT_THAT(m, NotNull());
EXPECT_EQ(m->ntex, 1);
EXPECT_EQ(m->nlight, 1);
EXPECT_THAT(m->light_texid[0], 0);
mj_deleteModel(m);
}
// ------------- test bvh ------------------------------------------------------
TEST_F(UserObjectsTest, RobustBVH) {
+12
View File
@@ -0,0 +1,12 @@
<mujoco>
<asset>
<texture name="texture1" type="2d" file="hfield.png"/>
</asset>
<worldbody>
<light name="light1" type="spot" pos="1 2 3" dir="0 0 1" diffuse="0.1 0.2 0.3" ambient="0.4 0.5 0.6" specular="0.7 0.8 0.95"/>
<light name="light2" type="spot" attenuation="0.1 0.2 0.3" cutoff="0.4" exponent="0.5"/>
<light name="light3" type="point" range="2" intensity="300" bulbradius="0.5"/>
<light name="light4" type="directional" dir="1 0 0" active="false" castshadow="false"/>
<light name="light5" type="image" texture="texture1"/>
</worldbody>
</mujoco>
+2
View File
@@ -5441,6 +5441,7 @@ public unsafe struct mjModel_ {
public int* light_bodyid;
public int* light_targetbodyid;
public int* light_type;
public int* light_texid;
public byte* light_castshadow;
public float* light_bulbradius;
public float* light_intensity;
@@ -6082,6 +6083,7 @@ public unsafe struct mjvLight_ {
public fixed float pos[3];
public fixed float dir[3];
public int type;
public int texid;
public fixed float attenuation[3];
public float cutoff;
public float exponent;