diff --git a/doc/XMLreference.rst b/doc/XMLreference.rst index 05b67169..90e8f04a 100644 --- a/doc/XMLreference.rst +++ b/doc/XMLreference.rst @@ -3181,6 +3181,12 @@ the direction specified by the dir attribute. It does not have a full spatial fr :at:`dir`: :at-val:`real(3), "0 0 -1"` Direction of the light. +.. _body-light-radius: + +:at:`radius`: :at-val:`real, "0"` + Radius of the light, affects shadow softness. This attribute has no effect in MuJoCo's native renderer, but it can be + useful when rendering scenes with an external renderer. + .. _body-light-attenuation: :at:`attenuation`: :at-val:`real(3), "1 0 0"` @@ -7586,6 +7592,8 @@ if omitted. .. _default-light-dir: +.. _default-light-radius: + .. _default-light-directional: .. _default-light-castshadow: diff --git a/doc/XMLschema.rst b/doc/XMLschema.rst index 5b516790..c374cfa0 100644 --- a/doc/XMLschema.rst +++ b/doc/XMLschema.rst @@ -369,11 +369,11 @@ | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | | :ref:`name` | :ref:`class` | :ref:`directional` | :ref:`castshadow` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`active` | :ref:`pos` | :ref:`dir` | :ref:`attenuation` | | +| | | | :ref:`active` | :ref:`pos` | :ref:`dir` | :ref:`radius` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`cutoff` | :ref:`exponent` | :ref:`ambient` | :ref:`diffuse` | | +| | | | :ref:`attenuation` | :ref:`cutoff` | :ref:`exponent` | :ref:`ambient` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`specular` | :ref:`mode` | :ref:`target` | | | +| | | | :ref:`diffuse` | :ref:`specular` | :ref:`mode` | :ref:`target` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | +------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | |_| body |br| |_| |L| | | .. table:: | @@ -1378,11 +1378,13 @@ | :ref:`light | ? | :class: mjcf-attributes | | ` | | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`pos` | :ref:`dir` | :ref:`directional` | :ref:`castshadow` | | +| | | | :ref:`pos` | :ref:`dir` | :ref:`radius` | :ref:`directional` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`active` | :ref:`attenuation` | :ref:`cutoff` | :ref:`exponent` | | +| | | | :ref:`castshadow` | :ref:`active` | :ref:`attenuation` | :ref:`cutoff` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`ambient` | :ref:`diffuse` | :ref:`specular` | :ref:`mode` | | +| | | | :ref:`exponent` | :ref:`ambient` | :ref:`diffuse` | :ref:`specular` | | +| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | +| | | | :ref:`mode` | | | | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | +------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | |_| default |br| |_| |L| | | .. table:: | diff --git a/doc/changelog.rst b/doc/changelog.rst index a964c9fb..d488b18b 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -9,12 +9,16 @@ General ^^^^^^^ 1. Added `mjModel.mesh_scale`: the scaling applied to asset vertices, as specified in the :ref:`scale` attribute. - +2. Added the :ref:`light/radius` attribute and corresponding ``mjModel.light_radius`` field. This + value has no effect in MuJoCo's native renderer, but can be usful when rendering scenes with an external renderer. MJX ^^^ +3. Added cylinder plane collisions. -1. Add cylinder plane collisions. +Bug fixes +^^^^^^^^^ +4. Defaults of lights were not being saved, now fixed. Version 3.1.4 (April 10th, 2024) diff --git a/doc/includes/references.h b/doc/includes/references.h index c932b61e..e0a77d94 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -1082,6 +1082,7 @@ struct mjModel_ { mjtByte* light_active; // is light on (nlight x 1) mjtNum* light_pos; // position rel. to body frame (nlight x 3) mjtNum* light_dir; // direction rel. to body frame (nlight x 3) + mjtNum* light_radius; // radius of the light (nlight x 1) mjtNum* light_poscom0; // global position rel. to sub-com in qpos0 (nlight x 3) mjtNum* light_pos0; // global position rel. to body in qpos0 (nlight x 3) mjtNum* light_dir0; // global direction in qpos0 (nlight x 3) @@ -1195,20 +1196,20 @@ struct mjModel_ { int* skin_pathadr; // address of asset path for skin; -1: none (nskin x 1) // height fields - mjtNum* hfield_size; // (x, y, z_top, z_bottom) (nhfield x 4) - int* hfield_nrow; // number of rows in grid (nhfield x 1) - int* hfield_ncol; // number of columns in grid (nhfield x 1) - int* hfield_adr; // address in hfield_data (nhfield x 1) - float* hfield_data; // elevation data (nhfielddata x 1) - int* hfield_pathadr; // address of asset path for hfield; -1: none (nhfield x 1) + mjtNum* hfield_size; // (x, y, z_top, z_bottom) (nhfield x 4) + int* hfield_nrow; // number of rows in grid (nhfield x 1) + int* hfield_ncol; // number of columns in grid (nhfield x 1) + int* hfield_adr; // address in hfield_data (nhfield x 1) + float* hfield_data; // elevation data (nhfielddata x 1) + int* hfield_pathadr; // address of hfield asset path; -1: none (nhfield x 1) // textures - int* tex_type; // texture type (mjtTexture) (ntex x 1) - int* tex_height; // number of rows in texture image (ntex x 1) - int* tex_width; // number of columns in texture image (ntex x 1) - int* tex_adr; // address in rgb (ntex x 1) - mjtByte* tex_rgb; // rgb (alpha = 1) (ntexdata x 1) - int* tex_pathadr; // address of asset path for texture; -1: none (ntex x 1) + int* tex_type; // texture type (mjtTexture) (ntex x 1) + int* tex_height; // number of rows in texture image (ntex x 1) + int* tex_width; // number of columns in texture image (ntex x 1) + int* tex_adr; // address in rgb (ntex x 1) + mjtByte* tex_rgb; // rgb (alpha = 1) (ntexdata x 1) + int* tex_pathadr; // address of texture asset path; -1: none (ntex x 1) // materials int* mat_texid; // texture id; -1: none (nmat x 1) diff --git a/include/mujoco/mjmodel.h b/include/mujoco/mjmodel.h index 80a0c4b4..37e2a9f4 100644 --- a/include/mujoco/mjmodel.h +++ b/include/mujoco/mjmodel.h @@ -797,6 +797,7 @@ struct mjModel_ { mjtByte* light_active; // is light on (nlight x 1) mjtNum* light_pos; // position rel. to body frame (nlight x 3) mjtNum* light_dir; // direction rel. to body frame (nlight x 3) + mjtNum* light_radius; // radius of the light (nlight x 1) mjtNum* light_poscom0; // global position rel. to sub-com in qpos0 (nlight x 3) mjtNum* light_pos0; // global position rel. to body in qpos0 (nlight x 3) mjtNum* light_dir0; // global direction in qpos0 (nlight x 3) @@ -910,20 +911,20 @@ struct mjModel_ { int* skin_pathadr; // address of asset path for skin; -1: none (nskin x 1) // height fields - mjtNum* hfield_size; // (x, y, z_top, z_bottom) (nhfield x 4) - int* hfield_nrow; // number of rows in grid (nhfield x 1) - int* hfield_ncol; // number of columns in grid (nhfield x 1) - int* hfield_adr; // address in hfield_data (nhfield x 1) - float* hfield_data; // elevation data (nhfielddata x 1) - int* hfield_pathadr; // address of asset path for hfield; -1: none (nhfield x 1) + mjtNum* hfield_size; // (x, y, z_top, z_bottom) (nhfield x 4) + int* hfield_nrow; // number of rows in grid (nhfield x 1) + int* hfield_ncol; // number of columns in grid (nhfield x 1) + int* hfield_adr; // address in hfield_data (nhfield x 1) + float* hfield_data; // elevation data (nhfielddata x 1) + int* hfield_pathadr; // address of hfield asset path; -1: none (nhfield x 1) // textures - int* tex_type; // texture type (mjtTexture) (ntex x 1) - int* tex_height; // number of rows in texture image (ntex x 1) - int* tex_width; // number of columns in texture image (ntex x 1) - int* tex_adr; // address in rgb (ntex x 1) - mjtByte* tex_rgb; // rgb (alpha = 1) (ntexdata x 1) - int* tex_pathadr; // address of asset path for texture; -1: none (ntex x 1) + int* tex_type; // texture type (mjtTexture) (ntex x 1) + int* tex_height; // number of rows in texture image (ntex x 1) + int* tex_width; // number of columns in texture image (ntex x 1) + int* tex_adr; // address in rgb (ntex x 1) + mjtByte* tex_rgb; // rgb (alpha = 1) (ntexdata x 1) + int* tex_pathadr; // address of texture asset path; -1: none (ntex x 1) // materials int* mat_texid; // texture id; -1: none (nmat x 1) diff --git a/include/mujoco/mjxmacro.h b/include/mujoco/mjxmacro.h index 0c7e03ca..6ad3093b 100644 --- a/include/mujoco/mjxmacro.h +++ b/include/mujoco/mjxmacro.h @@ -295,6 +295,7 @@ XMJV( mjtByte, light_active, nlight, 1 ) \ X ( mjtNum, light_pos, nlight, 3 ) \ X ( mjtNum, light_dir, nlight, 3 ) \ + X ( mjtNum, light_radius, nlight, 1 ) \ X ( mjtNum, light_poscom0, nlight, 3 ) \ X ( mjtNum, light_pos0, nlight, 3 ) \ X ( mjtNum, light_dir0, nlight, 3 ) \ diff --git a/introspect/structs.py b/introspect/structs.py index 7ac1a8f6..112cb730 100644 --- a/introspect/structs.py +++ b/introspect/structs.py @@ -2116,6 +2116,13 @@ STRUCTS: Mapping[str, StructDecl] = dict([ ), doc='direction rel. to body frame (nlight x 3)', ), + StructFieldDecl( + name='light_radius', + type=PointerType( + inner_type=ValueType(name='mjtNum'), + ), + doc='radius of the light (nlight x 1)', + ), StructFieldDecl( name='light_poscom0', type=PointerType( @@ -2842,84 +2849,84 @@ STRUCTS: Mapping[str, StructDecl] = dict([ type=PointerType( inner_type=ValueType(name='mjtNum'), ), - doc='(x, y, z_top, z_bottom) (nhfield x 4)', + doc='(x, y, z_top, z_bottom) (nhfield x 4)', ), StructFieldDecl( name='hfield_nrow', type=PointerType( inner_type=ValueType(name='int'), ), - doc='number of rows in grid (nhfield x 1)', + doc='number of rows in grid (nhfield x 1)', ), StructFieldDecl( name='hfield_ncol', type=PointerType( inner_type=ValueType(name='int'), ), - doc='number of columns in grid (nhfield x 1)', + doc='number of columns in grid (nhfield x 1)', ), StructFieldDecl( name='hfield_adr', type=PointerType( inner_type=ValueType(name='int'), ), - doc='address in hfield_data (nhfield x 1)', + doc='address in hfield_data (nhfield x 1)', ), StructFieldDecl( name='hfield_data', type=PointerType( inner_type=ValueType(name='float'), ), - doc='elevation data (nhfielddata x 1)', # pylint: disable=line-too-long + doc='elevation data (nhfielddata x 1)', # pylint: disable=line-too-long ), StructFieldDecl( name='hfield_pathadr', type=PointerType( inner_type=ValueType(name='int'), ), - doc='address of asset path for hfield; -1: none (nhfield x 1)', + doc='address of hfield asset path; -1: none (nhfield x 1)', ), StructFieldDecl( name='tex_type', type=PointerType( inner_type=ValueType(name='int'), ), - doc='texture type (mjtTexture) (ntex x 1)', + doc='texture type (mjtTexture) (ntex x 1)', ), StructFieldDecl( name='tex_height', type=PointerType( inner_type=ValueType(name='int'), ), - doc='number of rows in texture image (ntex x 1)', + doc='number of rows in texture image (ntex x 1)', ), StructFieldDecl( name='tex_width', type=PointerType( inner_type=ValueType(name='int'), ), - doc='number of columns in texture image (ntex x 1)', + doc='number of columns in texture image (ntex x 1)', ), StructFieldDecl( name='tex_adr', type=PointerType( inner_type=ValueType(name='int'), ), - doc='address in rgb (ntex x 1)', + doc='address in rgb (ntex x 1)', ), StructFieldDecl( name='tex_rgb', type=PointerType( inner_type=ValueType(name='mjtByte'), ), - doc='rgb (alpha = 1) (ntexdata x 1)', # pylint: disable=line-too-long + doc='rgb (alpha = 1) (ntexdata x 1)', ), StructFieldDecl( name='tex_pathadr', type=PointerType( inner_type=ValueType(name='int'), ), - doc='address of asset path for texture; -1: none (ntex x 1)', + doc='address of texture asset path; -1: none (ntex x 1)', ), StructFieldDecl( name='mat_texid', diff --git a/src/user/user_api.h b/src/user/user_api.h index c12a4e4c..dd03c959 100644 --- a/src/user/user_api.h +++ b/src/user/user_api.h @@ -347,6 +347,7 @@ typedef struct _mjsLight { // light specification // frame double pos[3]; // position double dir[3]; // direction + double radius; // radius mjtCamLight mode; // tracking mode mjString targetbody; // target body for targeting diff --git a/src/user/user_model.cc b/src/user/user_model.cc index 9026c045..0a539376 100644 --- a/src/user/user_model.cc +++ b/src/user/user_model.cc @@ -1985,6 +1985,7 @@ void mjCModel::CopyTree(mjModel* m) { m->light_active[lid] = (mjtByte)pl->active; copyvec(m->light_pos+3*lid, pl->pos, 3); copyvec(m->light_dir+3*lid, pl->dir, 3); + m->light_radius[lid] = pl->radius; copyvec(m->light_attenuation+3*lid, pl->attenuation, 3); m->light_cutoff[lid] = pl->cutoff; m->light_exponent[lid] = pl->exponent; diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc index 55e02d03..7427f019 100644 --- a/src/xml/xml_native_reader.cc +++ b/src/xml/xml_native_reader.cc @@ -153,7 +153,7 @@ const char* MJCF[nMJCF][mjXATTRNUM] = { {"camera", "?", "16", "fovy", "ipd", "resolution", "pos", "quat", "axisangle", "xyaxes", "zaxis", "euler", "mode", "focal", "focalpixel", "principal", "principalpixel", "sensorsize", "user"}, - {"light", "?", "12", "pos", "dir", "directional", "castshadow", "active", + {"light", "?", "13", "pos", "dir", "radius", "directional", "castshadow", "active", "attenuation", "cutoff", "exponent", "ambient", "diffuse", "specular", "mode"}, {"pair", "?", "7", "condim", "friction", "solref", "solreffriction", "solimp", "gap", "margin"}, @@ -263,9 +263,9 @@ const char* MJCF[nMJCF][mjXATTRNUM] = { {"camera", "*", "19", "name", "class", "fovy", "ipd", "resolution", "pos", "quat", "axisangle", "xyaxes", "zaxis", "euler", "mode", "target", "focal", "focalpixel", "principal", "principalpixel", "sensorsize", "user"}, - {"light", "*", "15", "name", "class", "directional", "castshadow", "active", - "pos", "dir", "attenuation", "cutoff", "exponent", "ambient", "diffuse", "specular", - "mode", "target"}, + {"light", "*", "16", "name", "class", "directional", "castshadow", "active", + "pos", "dir", "radius", "attenuation", "cutoff", "exponent", "ambient", "diffuse", + "specular", "mode", "target"}, {"plugin", "*", "2", "plugin", "instance"}, {"<"}, {"config", "*", "2", "key", "value"}, @@ -1783,6 +1783,7 @@ void mjXReader::OneLight(XMLElement* elem, mjsLight* plight) { } ReadAttr(elem, "pos", 3, plight->pos, text); ReadAttr(elem, "dir", 3, plight->dir, text); + ReadAttr(elem, "radius", 1, &plight->radius, text); ReadAttr(elem, "attenuation", 3, plight->attenuation, text); ReadAttr(elem, "cutoff", 1, &plight->cutoff, text); ReadAttr(elem, "exponent", 1, &plight->exponent, text); diff --git a/src/xml/xml_native_writer.cc b/src/xml/xml_native_writer.cc index c70ee22b..a2fa4ec6 100644 --- a/src/xml/xml_native_writer.cc +++ b/src/xml/xml_native_writer.cc @@ -508,6 +508,7 @@ void mjXWriter::OneLight(XMLElement* elem, mjCLight* plight, mjCDef* def) { } // defaults and regular + WriteAttr(elem, "radius", 1, &plight->radius, &def->light.radius); WriteAttrKey(elem, "directional", bool_map, 2, plight->directional, def->light.directional); WriteAttrKey(elem, "castshadow", bool_map, 2, plight->castshadow, def->light.castshadow); WriteAttrKey(elem, "active", bool_map, 2, plight->active, def->light.active); @@ -1172,6 +1173,11 @@ void mjXWriter::Default(XMLElement* root, mjCDef* def) { OneCamera(elem, &def->camera, par); if (!elem->FirstAttribute()) section->DeleteChild(elem); + // light + elem = InsertEnd(section, "light"); + OneLight(elem, &def->light, par); + if (!elem->FirstAttribute()) section->DeleteChild(elem); + // pair elem = InsertEnd(section, "pair"); OnePair(elem, &def->pair, par); diff --git a/test/xml/xml_native_reader_test.cc b/test/xml/xml_native_reader_test.cc index 2e854d89..61253f35 100644 --- a/test/xml/xml_native_reader_test.cc +++ b/test/xml/xml_native_reader_test.cc @@ -1868,5 +1868,29 @@ TEST_F(XMLReaderTest, ExtentNegativeNotAllowed) { EXPECT_THAT(error.data(), HasSubstr("line 3")); } +TEST_F(XMLReaderTest, LightRadius) { + static constexpr char xml[] = R"( + + + + + + + + + + + + + )"; + std::array error; + mjModel* model = LoadModelFromString(xml, error.data(), error.size()); + ASSERT_THAT(model, NotNull()) << error.data(); + EXPECT_EQ(model->light_radius[0], 0); + EXPECT_EQ(model->light_radius[1], 1); + EXPECT_EQ(model->light_radius[2], 2); + mj_deleteModel(model); +} + } // namespace } // namespace mujoco diff --git a/test/xml/xml_native_writer_test.cc b/test/xml/xml_native_writer_test.cc index cf48889b..0b5f1dec 100644 --- a/test/xml/xml_native_writer_test.cc +++ b/test/xml/xml_native_writer_test.cc @@ -935,6 +935,35 @@ TEST_F(XMLWriterTest, WritesHfield) { mj_deleteModel(model); } +TEST_F(XMLWriterTest, WritesLight) { + static constexpr char xml[] = R"( + + + + + + + + + + + + + )"; + mjModel* model = LoadModelFromString(xml); + ASSERT_THAT(model, NotNull()); + + // save and read, compare data + mjModel* mtemp = LoadModelFromString(SaveAndReadXml(model)); + EXPECT_EQ(mtemp->nlight, 3); + EXPECT_EQ(mtemp->light_radius[0], 0); + EXPECT_EQ(mtemp->light_radius[1], 1); + EXPECT_EQ(mtemp->light_radius[2], 2); + + mj_deleteModel(mtemp); + mj_deleteModel(model); +} + TEST_F(XMLWriterTest, SpringlengthOneValue) { static constexpr char xml[] = R"( diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index 704eaf09..4b8d6f84 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -5297,6 +5297,7 @@ public unsafe struct mjModel_ { public byte* light_active; public double* light_pos; public double* light_dir; + public double* light_radius; public double* light_poscom0; public double* light_pos0; public double* light_dir0;