Add output attribute to camera elements.

This attribute allows specifying the intended output types (e.g., RGB, depth, normal) for each camera in the XML. The output types are stored as bit flags in `mjModel`.

PiperOrigin-RevId: 853316261
Change-Id: I1ad2e94102593cf402756f8f4c80509371ca86d2
This commit is contained in:
Yuval Tassa
2026-01-07 10:19:35 -08:00
committed by Copybara-Service
parent 3c93d6b979
commit 608115ab95
18 changed files with 233 additions and 27 deletions
+16
View File
@@ -2988,6 +2988,20 @@ and the +Y axis points up. Thus the frame position and orientation are the key a
location to save the required resolution. Setting either value larger than 1
enables frustum visualization when the :ref:`mjVIS_CAMERA<mjtVisFlag>` visualization flag is active.
.. _body-camera-output:
:at:`output`: :at-val:`[rgb, depth, distance, normal, segmentation], "rgb"`
Types of output images supported by the camera.
- :at-val:`rgb`: RGB image.
- :at-val:`depth`: Depth image (distance from camera plane).
- :at-val:`distance`: Distance image (distance from camera origin).
- :at-val:`normal`: Surface normal image.
- :at-val:`segmentation`: Segmentation image.
This attribute is not used for rendering, but serves as a convenient location to save the output types supported by
the camera. The :at:`output` attribute can contain multiple types, e.g. :at-val:`"rgb normal"`.
.. _body-camera-sensorsize:
:at:`sensorsize`: :at-val:`real(2), "0 0"`
@@ -8715,6 +8729,8 @@ if omitted.
.. _default-camera-resolution:
.. _default-camera-output:
.. _default-camera-ipd:
.. _default-camera-pos:
+6
View File
@@ -1045,6 +1045,9 @@
.. grid-item::
:ref:`resolution<body-camera-resolution>`
.. grid-item::
:ref:`output<body-camera-output>`
.. grid-item::
:ref:`pos<body-camera-pos>`
@@ -4705,6 +4708,9 @@
.. grid-item::
:ref:`resolution<default-camera-resolution>`
.. grid-item::
:ref:`output<default-camera-output>`
.. grid-item::
:ref:`pos<default-camera-pos>`
+2
View File
@@ -30,6 +30,8 @@ General
attribute. In this case, the sensor will cast multiple ray, one for each pixel.
- Rangefinder sensors can now now report various kinds of information besides ray distances, including surface normals.
See :ref:`rangefinder<sensor-rangefinder>` for details.
- Cameras now have an :ref:`output<body-camera-output>` attribute, parsed into the ``mjModel.cam_output`` bitfield.
Unused by the renderer, it serves as a convenient location to store a camera's supported output types.
- Non-breaking ABI changes:
- The type of the ``sig`` (signature) argument of :ref:`mj_stateSize` and related functions has been changed from
+11
View File
@@ -786,6 +786,15 @@ typedef enum mjtRayDataField_ { // data fields returned by rangefinder sensors
mjNRAYDATA // number of rangefinder sensor data fields
} mjtRayDataField;
typedef enum mjtCamOutBit_ { // camera output type bitflags
mjCAMOUT_RGB = 1<<0, // RGB image
mjCAMOUT_DEPTH = 1<<1, // depth image (distance from camera plane)
mjCAMOUT_DIST = 1<<2, // distance image (distance from camera origin)
mjCAMOUT_NORMAL = 1<<3, // normal image
mjCAMOUT_SEG = 1<<4, // segmentation image
mjNCAMOUT = 5 // number of camera output types
} mjtCamOutBit;
typedef enum mjtSameFrame_ { // frame alignment of bodies with their children
mjSAMEFRAME_NONE = 0, // no alignment
mjSAMEFRAME_BODY, // frame is same as body frame
@@ -1240,6 +1249,7 @@ struct mjModel_ {
mjtNum* cam_fovy; // y field-of-view (ortho ? len : deg) (ncam x 1)
mjtNum* cam_ipd; // inter-pupilary distance (ncam x 1)
int* cam_resolution; // resolution: pixels [width, height] (ncam x 2)
int* cam_output; // output types (mjtCamOut bit flags) (ncam x 1)
float* cam_sensorsize; // sensor size: length [width, height] (ncam x 2)
float* cam_intrinsic; // [focal length; principal point] (ncam x 4)
mjtNum* cam_user; // user data (ncam x nuser_cam)
@@ -2117,6 +2127,7 @@ typedef struct mjsCamera_ { // camera specification
// intrinsics
mjtProjection proj; // camera projection type
int resolution[2]; // resolution (pixel)
int output; // bit flags for output type
double fovy; // y-field of view
double ipd; // inter-pupillary distance
float intrinsic[4]; // camera intrinsics (length)
+12
View File
@@ -416,6 +416,17 @@ typedef enum mjtRayDataField_ { // data fields returned by rangefinder sensors
} mjtRayDataField;
typedef enum mjtCamOutBit_ { // camera output type bitflags
mjCAMOUT_RGB = 1<<0, // RGB image
mjCAMOUT_DEPTH = 1<<1, // depth image (distance from camera plane)
mjCAMOUT_DIST = 1<<2, // distance image (distance from camera origin)
mjCAMOUT_NORMAL = 1<<3, // normal image
mjCAMOUT_SEG = 1<<4, // segmentation image
mjNCAMOUT = 5 // number of camera output types
} mjtCamOutBit;
typedef enum mjtSameFrame_ { // frame alignment of bodies with their children
mjSAMEFRAME_NONE = 0, // no alignment
mjSAMEFRAME_BODY, // frame is same as body frame
@@ -903,6 +914,7 @@ struct mjModel_ {
mjtNum* cam_fovy; // y field-of-view (ortho ? len : deg) (ncam x 1)
mjtNum* cam_ipd; // inter-pupilary distance (ncam x 1)
int* cam_resolution; // resolution: pixels [width, height] (ncam x 2)
int* cam_output; // output types (mjtCamOut bit flags) (ncam x 1)
float* cam_sensorsize; // sensor size: length [width, height] (ncam x 2)
float* cam_intrinsic; // [focal length; principal point] (ncam x 4)
mjtNum* cam_user; // user data (ncam x nuser_cam)
+1
View File
@@ -375,6 +375,7 @@ typedef struct mjsCamera_ { // camera specification
// intrinsics
mjtProjection proj; // camera projection type
int resolution[2]; // resolution (pixel)
int output; // bit flags for output type
double fovy; // y-field of view
double ipd; // inter-pupillary distance
float intrinsic[4]; // camera intrinsics (length)
+1
View File
@@ -306,6 +306,7 @@
X ( mjtNum, cam_fovy, ncam, 1 ) \
X ( mjtNum, cam_ipd, ncam, 1 ) \
X ( int, cam_resolution, ncam, 2 ) \
X ( int, cam_output, ncam, 1 ) \
X ( float, cam_sensorsize, ncam, 2 ) \
X ( float, cam_intrinsic, ncam, 4 ) \
X ( mjtNum, cam_user, ncam, MJ_M(nuser_cam) )
+13
View File
@@ -430,6 +430,19 @@ ENUMS: Mapping[str, EnumDecl] = dict([
('mjNRAYDATA', 6),
]),
)),
('mjtCamOutBit',
EnumDecl(
name='mjtCamOutBit',
declname='enum mjtCamOutBit_',
values=dict([
('mjCAMOUT_RGB', 1),
('mjCAMOUT_DEPTH', 2),
('mjCAMOUT_DIST', 4),
('mjCAMOUT_NORMAL', 8),
('mjCAMOUT_SEG', 16),
('mjNCAMOUT', 5),
]),
)),
('mjtSameFrame',
EnumDecl(
name='mjtSameFrame',
+13
View File
@@ -2275,6 +2275,14 @@ STRUCTS: Mapping[str, StructDecl] = dict([
doc='resolution: pixels [width, height]',
array_extent=('ncam', 2),
),
StructFieldDecl(
name='cam_output',
type=PointerType(
inner_type=ValueType(name='int'),
),
doc='output types (mjtCamOut bit flags)',
array_extent=('ncam',),
),
StructFieldDecl(
name='cam_sensorsize',
type=PointerType(
@@ -7578,6 +7586,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
),
doc='resolution (pixel)',
),
StructFieldDecl(
name='output',
type=ValueType(name='int'),
doc='bit flags for output type',
),
StructFieldDecl(
name='fovy',
type=ValueType(name='double'),
+1 -25
View File
@@ -21,7 +21,6 @@
#include "user/user_api.h"
// default model attributes
void mjs_defaultSpec(mjSpec* spec) {
memset(spec, 0, sizeof(mjSpec));
@@ -66,14 +65,12 @@ void mjs_defaultSpec(mjSpec* spec) {
}
// default orientation attributes
void mjs_defaultOrientation(mjsOrientation* orient) {
memset(orient, 0, sizeof(mjsOrientation));
}
// default body attributes
void mjs_defaultBody(mjsBody* body) {
memset(body, 0, sizeof(mjsBody));
@@ -88,7 +85,6 @@ void mjs_defaultBody(mjsBody* body) {
}
// default frame attributes
void mjs_defaultFrame(mjsFrame* frame) {
memset(frame, 0, sizeof(mjsFrame));
@@ -96,7 +92,6 @@ void mjs_defaultFrame(mjsFrame* frame) {
}
// default joint attributes
void mjs_defaultJoint(mjsJoint* joint) {
memset(joint, 0, sizeof(mjsJoint));
@@ -110,7 +105,6 @@ void mjs_defaultJoint(mjsJoint* joint) {
}
// default geom attributes
void mjs_defaultGeom(mjsGeom* geom) {
memset(geom, 0, sizeof(mjsGeom));
@@ -153,7 +147,6 @@ void mjs_defaultGeom(mjsGeom* geom) {
}
// default site attributes
void mjs_defaultSite(mjsSite* site) {
memset(site, 0, sizeof(mjsSite));
@@ -172,7 +165,6 @@ void mjs_defaultSite(mjsSite* site) {
}
// default cam attributes
void mjs_defaultCamera(mjsCamera* cam) {
memset(cam, 0, sizeof(mjsCamera));
@@ -187,10 +179,10 @@ void mjs_defaultCamera(mjsCamera* cam) {
cam->fovy = 45;
cam->ipd = 0.068;
cam->resolution[0] = cam->resolution[1] = 1;
cam->output = mjCAMOUT_RGB;
}
// default light attributes
void mjs_defaultLight(mjsLight* light) {
memset(light, 0, sizeof(mjsLight));
@@ -215,7 +207,6 @@ void mjs_defaultLight(mjsLight* light) {
}
// default flex attributes
void mjs_defaultFlex(mjsFlex* flex) {
memset(flex, 0, sizeof(mjsFlex));
@@ -242,7 +233,6 @@ void mjs_defaultFlex(mjsFlex* flex) {
}
// default mesh attributes
void mjs_defaultMesh(mjsMesh* mesh) {
memset(mesh, 0, sizeof(mjsMesh));
@@ -253,14 +243,12 @@ void mjs_defaultMesh(mjsMesh* mesh) {
}
// default height field attributes
void mjs_defaultHField(mjsHField* hfield) {
memset(hfield, 0, sizeof(mjsHField));
}
// default skin attributes
void mjs_defaultSkin(mjsSkin* skin) {
memset(skin, 0, sizeof(mjsSkin));
@@ -269,7 +257,6 @@ void mjs_defaultSkin(mjsSkin* skin) {
}
// default texture attributes
void mjs_defaultTexture(mjsTexture* texture) {
memset(texture, 0, sizeof(mjsTexture));
@@ -285,7 +272,6 @@ void mjs_defaultTexture(mjsTexture* texture) {
}
// default material attributes
void mjs_defaultMaterial(mjsMaterial* material) {
memset(material, 0, sizeof(mjsMaterial));
@@ -298,7 +284,6 @@ void mjs_defaultMaterial(mjsMaterial* material) {
}
// default pair attributes
void mjs_defaultPair(mjsPair* pair) {
memset(pair, 0, sizeof(mjsPair));
@@ -312,7 +297,6 @@ void mjs_defaultPair(mjsPair* pair) {
}
// default equality attributes
void mjs_defaultEquality(mjsEquality* equality) {
memset(equality, 0, sizeof(mjsEquality));
@@ -324,7 +308,6 @@ void mjs_defaultEquality(mjsEquality* equality) {
}
// default tendon attributes
void mjs_defaultTendon(mjsTendon* tendon) {
memset(tendon, 0, sizeof(mjsTendon));
@@ -338,7 +321,6 @@ void mjs_defaultTendon(mjsTendon* tendon) {
}
// default actuator attributes
void mjs_defaultActuator(mjsActuator* actuator) {
memset(actuator, 0, sizeof(mjsActuator));
@@ -364,7 +346,6 @@ void mjs_defaultActuator(mjsActuator* actuator) {
}
// default sensor attributes
void mjs_defaultSensor(mjsSensor* sensor) {
memset(sensor, 0, sizeof(mjsSensor));
@@ -375,35 +356,30 @@ void mjs_defaultSensor(mjsSensor* sensor) {
}
// Default numeric attributes.
void mjs_defaultNumeric(mjsNumeric* numeric) {
memset(numeric, 0, sizeof(mjsNumeric));
}
// Default text attributes.
void mjs_defaultText(mjsText* text) {
memset(text, 0, sizeof(mjsText));
}
// Default tuple attributes.
void mjs_defaultTuple(mjsTuple* tuple) {
memset(tuple, 0, sizeof(mjsTuple));
}
// Default keyframe attributes.
void mjs_defaultKey(mjsKey* key) {
memset(key, 0, sizeof(mjsKey));
}
// default plugin attributes
void mjs_defaultPlugin(mjsPlugin* plugin) {
memset(plugin, 0, sizeof(mjsPlugin));
+1
View File
@@ -2868,6 +2868,7 @@ void mjCModel::CopyTree(mjModel* m) {
m->cam_fovy[cid] = (mjtNum)pc->fovy;
m->cam_ipd[cid] = (mjtNum)pc->ipd;
mjuu_copyvec(m->cam_resolution+2*cid, pc->resolution, 2);
m->cam_output[cid] = pc->output;
mjuu_copyvec(m->cam_sensorsize+2*cid, pc->sensor_size, 2);
mjuu_copyvec(m->cam_intrinsic+4*cid, pc->intrinsic, 4);
mjuu_copyvec(m->cam_user+nuser_cam*cid, pc->get_userdata().data(), nuser_cam);
+2
View File
@@ -45,6 +45,7 @@ extern const int gain_sz;
extern const int bias_sz;
extern const int stage_sz;
extern const int datatype_sz;
extern const int camout_sz;
extern const int reduce_sz;
extern const mjMap angle_map[];
extern const mjMap enable_map[];
@@ -77,6 +78,7 @@ extern const mjMap stage_map[];
extern const mjMap datatype_map[];
extern const mjMap condata_map[];
extern const mjMap raydata_map[];
extern const mjMap camout_map[];
extern const mjMap reduce_map[];
extern const mjMap meshtype_map[];
extern const mjMap meshinertia_map[];
+20 -2
View File
@@ -171,7 +171,7 @@ std::vector<const char*> MJCF[nMJCF] = {
"hfield", "mesh", "fitscale", "rgba", "fluidshape", "fluidcoef", "user"},
{"site", "?", "type", "group", "pos", "quat", "material",
"size", "fromto", "axisangle", "xyaxes", "zaxis", "euler", "rgba", "user"},
{"camera", "?", "projection", "fovy", "ipd", "resolution", "pos", "quat",
{"camera", "?", "projection", "fovy", "ipd", "resolution", "output", "pos", "quat",
"axisangle", "xyaxes", "zaxis", "euler", "mode", "focal", "focalpixel",
"principal", "principalpixel", "sensorsize", "user"},
{"light", "?", "pos", "dir", "bulbradius", "intensity", "range",
@@ -285,7 +285,7 @@ std::vector<const char*> MJCF[nMJCF] = {
{"attach", "*", "model", "body", "prefix"},
{"site", "*", "name", "class", "type", "group", "pos", "quat",
"material", "size", "fromto", "axisangle", "xyaxes", "zaxis", "euler", "rgba", "user"},
{"camera", "*", "name", "class", "projection", "fovy", "ipd", "resolution", "pos",
{"camera", "*", "name", "class", "projection", "fovy", "ipd", "resolution", "output", "pos",
"quat", "axisangle", "xyaxes", "zaxis", "euler", "mode", "target",
"focal", "focalpixel", "principal", "principalpixel", "sensorsize", "user"},
{"light", "*", "name", "class", "directional", "type", "castshadow", "active",
@@ -793,6 +793,13 @@ const mjMap raydata_map[mjNRAYDATA] = {
{"depth", mjRAYDATA_DEPTH}
};
// camera output type
const int camout_sz = mjNCAMOUT;
const mjMap camout_map[mjNCAMOUT] = {{"rgb", mjCAMOUT_RGB},
{"depth", mjCAMOUT_DEPTH},
{"distance", mjCAMOUT_DIST},
{"normal", mjCAMOUT_NORMAL},
{"segmentation", mjCAMOUT_SEG}};
// contact reduction type
const int reduce_sz = 4;
@@ -1953,6 +1960,17 @@ void mjXReader::OneCamera(XMLElement* elem, mjsCamera* camera) {
ReadAttr(elem, "focalpixel", 2, camera->focal_pixel, text);
ReadAttr(elem, "focal", 2, camera->focal_length, text);
ReadAttr(elem, "resolution", 2, camera->resolution, text);
// read output attribute as space-separated bitflags
std::vector<int> outvals(mjNCAMOUT);
int nout = MapValues(elem, "output", outvals.data(), camout_map, mjNCAMOUT);
if (nout) {
camera->output = 0;
for (int i = 0; i < nout; ++i) {
camera->output |= outvals[i];
}
}
bool sensorsize = ReadAttr(elem, "sensorsize", 2, camera->sensor_size, text);
bool fovy = ReadAttr(elem, "fovy", 1, &camera->fovy, text);
if (fovy && sensorsize) {
+13
View File
@@ -573,6 +573,19 @@ void mjXWriter::OneCamera(XMLElement* elem, const mjCCamera* camera, mjCDef* def
WriteAttr(elem, "ipd", 1, &camera->ipd, &def->Camera().ipd);
WriteAttrKey(elem, "mode", camlight_map, camlight_sz, camera->mode, def->Camera().mode);
WriteAttr(elem, "resolution", 2, camera->resolution, def->Camera().resolution);
// write output attribute if different from default
if (camera->output != def->Camera().output) {
int data[mjNCAMOUT];
int ndata = 0;
for (int i = 0; i < mjNCAMOUT; i++) {
if (camera->output & camout_map[i].value) {
data[ndata++] = camout_map[i].value;
}
}
WriteAttrKeys(elem, "output", camout_map, camout_sz, data, ndata, 0);
}
WriteAttrKey(elem, "projection", projection_map, projection_sz, camera->proj, def->Camera().proj);
// camera intrinsics if specified
+46
View File
@@ -3244,5 +3244,51 @@ TEST_F(XMLReaderTest, LightRadius) {
mj_deleteModel(model);
}
TEST_F(XMLReaderTest, CameraOutput) {
static constexpr char xml[] = R"(
<mujoco>
<worldbody>
<camera name="default_cam"/>
<camera name="single_cam" output="depth"/>
<camera name="multi_cam" output="rgb normal segmentation"/>
</worldbody>
</mujoco>
)";
std::array<char, 1024> error;
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
ASSERT_THAT(model, NotNull()) << error.data();
EXPECT_EQ(model->ncam, 3);
EXPECT_EQ(model->cam_output[0], mjCAMOUT_RGB); // default
EXPECT_EQ(model->cam_output[1], mjCAMOUT_DEPTH);
EXPECT_EQ(model->cam_output[2],
mjCAMOUT_RGB | mjCAMOUT_NORMAL | mjCAMOUT_SEG);
mj_deleteModel(model);
}
TEST_F(XMLReaderTest, CameraOutputDefault) {
static constexpr char xml[] = R"(
<mujoco>
<default>
<default class="multi">
<camera output="depth distance"/>
</default>
</default>
<worldbody>
<camera name="default_cam"/>
<camera name="class_cam" class="multi"/>
<camera name="override_cam" class="multi" output="normal"/>
</worldbody>
</mujoco>
)";
std::array<char, 1024> error;
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
ASSERT_THAT(model, NotNull()) << error.data();
EXPECT_EQ(model->ncam, 3);
EXPECT_EQ(model->cam_output[0], mjCAMOUT_RGB); // main default
EXPECT_EQ(model->cam_output[1], mjCAMOUT_DEPTH | mjCAMOUT_DIST);
EXPECT_EQ(model->cam_output[2], mjCAMOUT_NORMAL);
mj_deleteModel(model);
}
} // namespace
} // namespace mujoco
+48
View File
@@ -1672,5 +1672,53 @@ TEST_F(XMLWriterTest, ExpandAttach) {
mj_deleteVFS(vfs.get());
}
TEST_F(XMLWriterTest, WritesCameraOutput) {
static constexpr char xml[] = R"(
<mujoco>
<worldbody>
<camera name="default_cam"/>
<camera name="multi_cam" output="depth normal"/>
</worldbody>
</mujoco>
)";
mjModel* model = LoadModelFromString(xml);
ASSERT_THAT(model, NotNull());
std::string saved_xml = SaveAndReadXml(model);
// default output="rgb" should not be written
EXPECT_THAT(saved_xml, Not(HasSubstr("output=\"rgb\"")));
// non-default output should be written
EXPECT_THAT(saved_xml, HasSubstr("output=\"depth normal\""));
mj_deleteModel(model);
}
TEST_F(XMLWriterTest, WritesCameraOutputDefault) {
static constexpr char xml[] = R"(
<mujoco>
<default>
<default class="multi">
<camera output="depth distance"/>
</default>
</default>
<worldbody>
<camera name="default_cam"/>
<camera name="class_cam" class="multi"/>
<camera name="override_cam" class="multi" output="segmentation"/>
</worldbody>
</mujoco>
)";
mjModel* model = LoadModelFromString(xml);
ASSERT_THAT(model, NotNull());
std::string saved_xml = SaveAndReadXml(model);
// save and reload to verify round-trip
mjModel* mtemp = LoadModelFromString(saved_xml);
ASSERT_THAT(mtemp, NotNull());
EXPECT_EQ(mtemp->ncam, 3);
EXPECT_EQ(mtemp->cam_output[0], mjCAMOUT_RGB);
EXPECT_EQ(mtemp->cam_output[1], mjCAMOUT_DEPTH | mjCAMOUT_DIST);
EXPECT_EQ(mtemp->cam_output[2], mjCAMOUT_SEG);
mj_deleteModel(mtemp);
mj_deleteModel(model);
}
} // namespace
} // namespace mujoco
+9
View File
@@ -440,6 +440,14 @@ public enum mjtRayDataField : int{
mjRAYDATA_DEPTH = 5,
mjNRAYDATA = 6,
}
public enum mjtCamOutBit : int{
mjCAMOUT_RGB = 1,
mjCAMOUT_DEPTH = 2,
mjCAMOUT_DIST = 4,
mjCAMOUT_NORMAL = 8,
mjCAMOUT_SEG = 16,
mjNCAMOUT = 5,
}
public enum mjtSameFrame : int{
mjSAMEFRAME_NONE = 0,
mjSAMEFRAME_BODY = 1,
@@ -5491,6 +5499,7 @@ public unsafe struct mjModel_ {
public double* cam_fovy;
public double* cam_ipd;
public int* cam_resolution;
public int* cam_output;
public float* cam_sensorsize;
public float* cam_intrinsic;
public double* cam_user;
+18
View File
@@ -3138,6 +3138,12 @@ struct MjsCamera {
emscripten::val resolution() const {
return emscripten::val(emscripten::typed_memory_view(2, ptr_->resolution));
}
int output() const {
return ptr_->output;
}
void set_output(int value) {
ptr_->output = value;
}
double fovy() const {
return ptr_->fovy;
}
@@ -4174,6 +4180,9 @@ struct MjModel {
emscripten::val cam_resolution() const {
return emscripten::val(emscripten::typed_memory_view(ptr_->ncam * 2, ptr_->cam_resolution));
}
emscripten::val cam_output() const {
return emscripten::val(emscripten::typed_memory_view(ptr_->ncam, ptr_->cam_output));
}
emscripten::val cam_sensorsize() const {
return emscripten::val(emscripten::typed_memory_view(ptr_->ncam * 2, ptr_->cam_sensorsize));
}
@@ -10191,6 +10200,13 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.value("mjCAMLIGHT_TRACKCOM", mjCAMLIGHT_TRACKCOM)
.value("mjCAMLIGHT_TARGETBODY", mjCAMLIGHT_TARGETBODY)
.value("mjCAMLIGHT_TARGETBODYCOM", mjCAMLIGHT_TARGETBODYCOM);
enum_<mjtCamOutBit>("mjtCamOutBit")
.value("mjCAMOUT_RGB", mjCAMOUT_RGB)
.value("mjCAMOUT_DEPTH", mjCAMOUT_DEPTH)
.value("mjCAMOUT_DIST", mjCAMOUT_DIST)
.value("mjCAMOUT_NORMAL", mjCAMOUT_NORMAL)
.value("mjCAMOUT_SEG", mjCAMOUT_SEG)
.value("mjNCAMOUT", mjNCAMOUT);
enum_<mjtCamera>("mjtCamera")
.value("mjCAMERA_FREE", mjCAMERA_FREE)
.value("mjCAMERA_TRACKING", mjCAMERA_TRACKING)
@@ -11043,6 +11059,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.property("cam_ipd", &MjModel::cam_ipd)
.property("cam_mat0", &MjModel::cam_mat0)
.property("cam_mode", &MjModel::cam_mode)
.property("cam_output", &MjModel::cam_output)
.property("cam_pos", &MjModel::cam_pos)
.property("cam_pos0", &MjModel::cam_pos0)
.property("cam_poscom0", &MjModel::cam_poscom0)
@@ -11744,6 +11761,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.property("intrinsic", &MjsCamera::intrinsic)
.property("ipd", &MjsCamera::ipd, &MjsCamera::set_ipd, reference())
.property("mode", &MjsCamera::mode, &MjsCamera::set_mode, reference())
.property("output", &MjsCamera::output, &MjsCamera::set_output, reference())
.property("pos", &MjsCamera::pos)
.property("principal_length", &MjsCamera::principal_length)
.property("principal_pixel", &MjsCamera::principal_pixel)