diff --git a/doc/APIreference/APItypes.rst b/doc/APIreference/APItypes.rst index b1997964..5e854f87 100644 --- a/doc/APIreference/APItypes.rst +++ b/doc/APIreference/APItypes.rst @@ -166,6 +166,16 @@ elements. These values are used in ``m->geom_type`` and ``m->site_type``. .. mujoco-include:: mjtGeom +.. _mjtProjection: + +mjtProjection +~~~~~~~~~~~~~ + +Type of camera projection. Used in ``m->cam_projection``. + +.. mujoco-include:: mjtProjection + + .. _mjtCamLight: mjtCamLight diff --git a/doc/XMLreference.rst b/doc/XMLreference.rst index c436aeb9..cbe815a5 100644 --- a/doc/XMLreference.rst +++ b/doc/XMLreference.rst @@ -2961,11 +2961,11 @@ and the +Y axis points up. Thus the frame position and orientation are the key a When the camera mode is "targetbody" or "targetbodycom", this attribute becomes required. It specifies which body should be targeted by the camera. In all other modes this attribute is ignored. -.. _body-camera-orthographic: +.. _body-camera-projection: -:at:`orthographic`: :at-val:`[false, true], "false"` - Whether the camera uses a perspective projection (the default) or an orthographic projection. Setting this attribute - changes the semantic of the :ref:`fovy` attribute, see below. +:at:`projection`: :at-val:`[perspective, orthographic], "perspective"` + Whether the camera uses a perspective (the default) or orthographic projection. Setting this + attribute to "orthographic" changes the semantic of the :ref:`fovy` attribute, see below. .. _body-camera-fovy: @@ -8666,7 +8666,7 @@ if omitted. | This element sets the attributes of the dummy :ref:`site ` element of the defaults class. | All site attributes are available here except: name, class. -.. _default-camera-orthographic: +.. _default-camera-projection: .. _default-camera-fovy: diff --git a/doc/XMLschema.rst b/doc/XMLschema.rst index a42b23fa..781293b7 100644 --- a/doc/XMLschema.rst +++ b/doc/XMLschema.rst @@ -302,7 +302,7 @@ | :ref:`camera | \* | :class: mjcf-attributes | | ` | | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`name` | :ref:`class` | :ref:`orthographic` | :ref:`fovy` | | +| | | | :ref:`name` | :ref:`class` | :ref:`projection` | :ref:`fovy` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | | :ref:`ipd` | :ref:`resolution` | :ref:`pos` | :ref:`quat` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | @@ -1490,7 +1490,7 @@ | :ref:`camera | ? | :class: mjcf-attributes | | ` | | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`orthographic` | :ref:`fovy` | :ref:`ipd` | :ref:`resolution` | | +| | | | :ref:`projection` | :ref:`fovy` | :ref:`ipd` | :ref:`resolution` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | | :ref:`pos` | :ref:`quat` | :ref:`axisangle` | :ref:`xyaxes` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | diff --git a/doc/changelog.rst b/doc/changelog.rst index fc5489ec..b162275c 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -5,6 +5,17 @@ Changelog Upcoming version (not yet released) ----------------------------------- +.. admonition:: Breaking API changes + :class: attention + + - The ``mjModel.cam_orthographic`` field has been renamed to ``cam_projection``, with the semantic of a new enum type + :ref:`mjtProjection`. This will allow for more projection types in the future like fisheye cameras. + Relatedly, the ``camera/orthographic`` MJCF attribute for cameras has been renamed to + :ref:`camera/projection` and now accepts the values ``orthographic`` and ``perspective``. + + **Migration:** Replace ``orthographic = "false/true"`` with ``projection="perspective/orthographic"``, + respectively. + General ^^^^^^^ - Non-breaking ABI changes: @@ -793,7 +804,7 @@ General :width: 240px 8. Added support for orthographic cameras. This is available for both fixed cameras and the free camera, using the - :ref:`camera/orthographic` and :ref:`global/orthographic` + ``camera/orthographic`` and :ref:`global/orthographic` attributes, respectively. 9. Added :ref:`maxhullvert`, the maximum number of vertices in a mesh's convex hull. 10. Added :ref:`mj_setKeyframe` for saving the current state into a model keyframe. diff --git a/doc/includes/references.h b/doc/includes/references.h index be6e0368..f857b796 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -538,6 +538,10 @@ typedef enum mjtGeom_ { // type of geometric shape mjGEOM_NONE = 1001 // missing geom type } mjtGeom; +typedef enum mjtProjection_ { // type of camera projection + mjPROJ_PERSPECTIVE = 0, // perspective + mjPROJ_ORTHOGRAPHIC // orthographic +} mjtProjection; typedef enum mjtCamLight_ { // tracking mode for camera and light mjCAMLIGHT_FIXED = 0, // pos and rot fixed in body mjCAMLIGHT_TRACK, // pos tracks body, rot fixed in global @@ -1223,7 +1227,7 @@ struct mjModel_ { mjtNum* cam_poscom0; // global position rel. to sub-com in qpos0 (ncam x 3) mjtNum* cam_pos0; // global position rel. to body in qpos0 (ncam x 3) mjtNum* cam_mat0; // global orientation in qpos0 (ncam x 9) - int* cam_orthographic; // orthographic camera; 0: no, 1: yes (ncam x 1) + int* cam_projection; // projection type (mjtProjection) (ncam x 1) 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) @@ -2102,7 +2106,7 @@ typedef struct mjsCamera_ { // camera specification mjString* targetbody; // target body for tracking/targeting // intrinsics - int orthographic; // is camera orthographic + mjtProjection proj; // camera projection type double fovy; // y-field of view double ipd; // inter-pupilary distance float intrinsic[4]; // camera intrinsics (length) diff --git a/include/mujoco/mjmodel.h b/include/mujoco/mjmodel.h index 3c1eba18..6910d861 100644 --- a/include/mujoco/mjmodel.h +++ b/include/mujoco/mjmodel.h @@ -124,6 +124,12 @@ typedef enum mjtGeom_ { // type of geometric shape } mjtGeom; +typedef enum mjtProjection_ { // type of camera projection + mjPROJ_PERSPECTIVE = 0, // perspective + mjPROJ_ORTHOGRAPHIC // orthographic +} mjtProjection; + + typedef enum mjtCamLight_ { // tracking mode for camera and light mjCAMLIGHT_FIXED = 0, // pos and rot fixed in body mjCAMLIGHT_TRACK, // pos tracks body, rot fixed in global @@ -882,7 +888,7 @@ struct mjModel_ { mjtNum* cam_poscom0; // global position rel. to sub-com in qpos0 (ncam x 3) mjtNum* cam_pos0; // global position rel. to body in qpos0 (ncam x 3) mjtNum* cam_mat0; // global orientation in qpos0 (ncam x 9) - int* cam_orthographic; // orthographic camera; 0: no, 1: yes (ncam x 1) + int* cam_projection; // projection type (mjtProjection) (ncam x 1) 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) diff --git a/include/mujoco/mjspec.h b/include/mujoco/mjspec.h index 6c1ced28..299f95e9 100644 --- a/include/mujoco/mjspec.h +++ b/include/mujoco/mjspec.h @@ -373,7 +373,7 @@ typedef struct mjsCamera_ { // camera specification mjString* targetbody; // target body for tracking/targeting // intrinsics - int orthographic; // is camera orthographic + mjtProjection proj; // camera projection type double fovy; // y-field of view double ipd; // inter-pupilary distance float intrinsic[4]; // camera intrinsics (length) diff --git a/include/mujoco/mjxmacro.h b/include/mujoco/mjxmacro.h index 1606aebd..077b44d3 100644 --- a/include/mujoco/mjxmacro.h +++ b/include/mujoco/mjxmacro.h @@ -302,7 +302,7 @@ X ( mjtNum, cam_poscom0, ncam, 3 ) \ X ( mjtNum, cam_pos0, ncam, 3 ) \ X ( mjtNum, cam_mat0, ncam, 9 ) \ - X ( int, cam_orthographic, ncam, 1 ) \ + X ( int, cam_projection, ncam, 1 ) \ X ( mjtNum, cam_fovy, ncam, 1 ) \ X ( mjtNum, cam_ipd, ncam, 1 ) \ X ( int, cam_resolution, ncam, 2 ) \ diff --git a/python/mujoco/introspect/enums.py b/python/mujoco/introspect/enums.py index 70e2c0d6..db5f1e08 100644 --- a/python/mujoco/introspect/enums.py +++ b/python/mujoco/introspect/enums.py @@ -101,6 +101,15 @@ ENUMS: Mapping[str, EnumDecl] = dict([ ('mjGEOM_NONE', 1001), ]), )), + ('mjtProjection', + EnumDecl( + name='mjtProjection', + declname='enum mjtProjection_', + values=dict([ + ('mjPROJ_PERSPECTIVE', 0), + ('mjPROJ_ORTHOGRAPHIC', 1), + ]), + )), ('mjtCamLight', EnumDecl( name='mjtCamLight', diff --git a/python/mujoco/introspect/structs.py b/python/mujoco/introspect/structs.py index 5393b5d3..61786b45 100644 --- a/python/mujoco/introspect/structs.py +++ b/python/mujoco/introspect/structs.py @@ -2244,11 +2244,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([ array_extent=('ncam', 9), ), StructFieldDecl( - name='cam_orthographic', + name='cam_projection', type=PointerType( inner_type=ValueType(name='int'), ), - doc='orthographic camera; 0: no, 1: yes', + doc='projection type (mjtProjection)', array_extent=('ncam',), ), StructFieldDecl( @@ -7566,9 +7566,9 @@ STRUCTS: Mapping[str, StructDecl] = dict([ doc='target body for tracking/targeting', ), StructFieldDecl( - name='orthographic', - type=ValueType(name='int'), - doc='is camera orthographic', + name='proj', + type=ValueType(name='mjtProjection'), + doc='camera projection type', ), StructFieldDecl( name='fovy', diff --git a/python/mujoco/specs_test.py b/python/mujoco/specs_test.py index 312ff668..19778d8e 100644 --- a/python/mujoco/specs_test.py +++ b/python/mujoco/specs_test.py @@ -295,8 +295,10 @@ class SpecsTest(absltest.TestCase): self.assertEqual(site.info, 'info') # Add camera. - cam = body.add_camera(orthographic=1, resolution=[10, 20]) - self.assertEqual(cam.orthographic, 1) + cam = body.add_camera( + proj=mujoco.mjtProjection.mjPROJ_ORTHOGRAPHIC, resolution=[10, 20] + ) + self.assertEqual(cam.proj, 1) np.testing.assert_array_equal(cam.resolution, [10, 20]) # Add frame. diff --git a/src/engine/engine_vis_visualize.c b/src/engine/engine_vis_visualize.c index 10711ebd..8baa4a0a 100644 --- a/src/engine/engine_vis_visualize.c +++ b/src/engine/engine_vis_visualize.c @@ -522,7 +522,7 @@ void mjv_cameraFrustum(float zver[2], float zhor[2], float zclip[2], const mjMod if (cid < 0 || cid >= m->ncam) { mjERROR("fixed camera id is outside valid range"); } - orthographic = m->cam_orthographic[cid]; + orthographic = m->cam_projection[cid] == mjPROJ_ORTHOGRAPHIC; fovy = m->cam_fovy[cid]; // if positive sensorsize, get sensorsize and intrinsic @@ -2763,7 +2763,7 @@ void mjv_updateCamera(const mjModel* m, const mjData* d, mjvCamera* cam, mjvScen mjERROR("fixed camera id is outside valid range"); } ipd = m->cam_ipd[cid]; - orthographic = m->cam_orthographic[cid]; + orthographic = m->cam_projection[cid] == mjPROJ_ORTHOGRAPHIC; break; default: diff --git a/src/user/user_model.cc b/src/user/user_model.cc index 06bcd073..63e89e94 100644 --- a/src/user/user_model.cc +++ b/src/user/user_model.cc @@ -2864,7 +2864,7 @@ void mjCModel::CopyTree(mjModel* m) { m->cam_targetbodyid[cid] = pc->targetbodyid; mjuu_copyvec(m->cam_pos+3*cid, pc->pos, 3); mjuu_copyvec(m->cam_quat+4*cid, pc->quat, 4); - m->cam_orthographic[cid] = pc->orthographic; + m->cam_projection[cid] = pc->proj; m->cam_fovy[cid] = (mjtNum)pc->fovy; m->cam_ipd[cid] = (mjtNum)pc->ipd; mjuu_copyvec(m->cam_resolution+2*cid, pc->resolution, 2); diff --git a/src/xml/xml_base.h b/src/xml/xml_base.h index 9c43c018..5cedbc95 100644 --- a/src/xml/xml_base.h +++ b/src/xml/xml_base.h @@ -27,6 +27,7 @@ // keyword maps (defined in implementation files) extern const int joint_sz; extern const int bodysleep_sz; +extern const int projection_sz; extern const int camlight_sz; extern const int lighttype_sz; extern const int integrator_sz; @@ -53,6 +54,7 @@ extern const mjMap TFAuto_map[]; extern const mjMap joint_map[]; extern const mjMap bodysleep_map[]; extern const mjMap geom_map[]; +extern const mjMap projection_map[]; extern const mjMap camlight_map[]; extern const mjMap lighttype_map[]; extern const mjMap integrator_map[]; diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc index fbee962a..ccc1a9af 100644 --- a/src/xml/xml_native_reader.cc +++ b/src/xml/xml_native_reader.cc @@ -171,7 +171,7 @@ std::vector MJCF[nMJCF] = { "hfield", "mesh", "fitscale", "rgba", "fluidshape", "fluidcoef", "user"}, {"site", "?", "type", "group", "pos", "quat", "material", "size", "fromto", "axisangle", "xyaxes", "zaxis", "euler", "rgba", "user"}, - {"camera", "?", "orthographic", "fovy", "ipd", "resolution", "pos", "quat", + {"camera", "?", "projection", "fovy", "ipd", "resolution", "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 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", "orthographic", "fovy", "ipd", "resolution", "pos", + {"camera", "*", "name", "class", "projection", "fovy", "ipd", "resolution", "pos", "quat", "axisangle", "xyaxes", "zaxis", "euler", "mode", "target", "focal", "focalpixel", "principal", "principalpixel", "sensorsize", "user"}, {"light", "*", "name", "class", "directional", "type", "castshadow", "active", @@ -590,6 +590,13 @@ const mjMap geom_map[mjNGEOMTYPES] = { }; +// projection type +const int projection_sz = 2; +const mjMap projection_map[projection_sz] = { + {"perspective", mjPROJ_PERSPECTIVE}, + {"orthographic", mjPROJ_ORTHOGRAPHIC} +}; + // camlight type const int camlight_sz = 5; const mjMap camlight_map[camlight_sz] = { @@ -1926,8 +1933,8 @@ void mjXReader::OneCamera(XMLElement* elem, mjsCamera* camera) { ReadAlternative(elem, camera->alt); ReadAttr(elem, "ipd", 1, &camera->ipd, text); - if (MapValue(elem, "orthographic", &n, bool_map, 2)) { - camera->orthographic = (n == 1); + if (MapValue(elem, "projection", &n, projection_map, 2)) { + camera->proj = (mjtProjection)n; } bool has_principal = ReadAttr(elem, "principalpixel", 2, camera->principal_pixel, text) || diff --git a/src/xml/xml_native_writer.cc b/src/xml/xml_native_writer.cc index ae9e9da4..f3c29330 100644 --- a/src/xml/xml_native_writer.cc +++ b/src/xml/xml_native_writer.cc @@ -573,7 +573,7 @@ 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); - WriteAttrKey(elem, "orthographic", bool_map, 2, camera->orthographic, def->Camera().orthographic); + WriteAttrKey(elem, "projection", projection_map, projection_sz, camera->proj, def->Camera().proj); // camera intrinsics if specified if (camera->sensor_size[0] > 0 && camera->sensor_size[1] > 0) { diff --git a/test/engine/testdata/vis_visualize/orthographic.xml b/test/engine/testdata/vis_visualize/orthographic.xml index bdff71a3..d01b9622 100644 --- a/test/engine/testdata/vis_visualize/orthographic.xml +++ b/test/engine/testdata/vis_visualize/orthographic.xml @@ -13,7 +13,7 @@ - + @@ -26,9 +26,9 @@ - + - + diff --git a/test/xml/xml_native_reader_test.cc b/test/xml/xml_native_reader_test.cc index d150099e..8efd45ab 100644 --- a/test/xml/xml_native_reader_test.cc +++ b/test/xml/xml_native_reader_test.cc @@ -2274,11 +2274,11 @@ TEST_F(XMLReaderTest, Orthographic) { - + - + @@ -2288,8 +2288,8 @@ TEST_F(XMLReaderTest, Orthographic) { EXPECT_THAT(model, NotNull()) << error.data(); EXPECT_EQ(model->vis.global.orthographic, 1); - EXPECT_EQ(model->cam_orthographic[0], 1); - EXPECT_EQ(model->cam_orthographic[1], 1); + EXPECT_EQ(model->cam_projection[0], mjPROJ_ORTHOGRAPHIC); + EXPECT_EQ(model->cam_projection[1], mjPROJ_ORTHOGRAPHIC); EXPECT_EQ(model->cam_fovy[0], 1); EXPECT_EQ(model->cam_fovy[1], 2); diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index 873de91f..e1c48435 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -221,6 +221,10 @@ public enum mjtGeom : int{ mjGEOM_TRIANGLE = 108, mjGEOM_NONE = 1001, } +public enum mjtProjection : int{ + mjPROJ_PERSPECTIVE = 0, + mjPROJ_ORTHOGRAPHIC = 1, +} public enum mjtCamLight : int{ mjCAMLIGHT_FIXED = 0, mjCAMLIGHT_TRACK = 1, @@ -5474,7 +5478,7 @@ public unsafe struct mjModel_ { public double* cam_poscom0; public double* cam_pos0; public double* cam_mat0; - public int* cam_orthographic; + public int* cam_projection; public double* cam_fovy; public double* cam_ipd; public int* cam_resolution; diff --git a/wasm/codegen/generated/bindings.cc b/wasm/codegen/generated/bindings.cc index 15795889..da048b38 100644 --- a/wasm/codegen/generated/bindings.cc +++ b/wasm/codegen/generated/bindings.cc @@ -3129,11 +3129,11 @@ struct MjsCamera { *(ptr_->targetbody) = value; } } - int orthographic() const { - return ptr_->orthographic; + mjtProjection proj() const { + return ptr_->proj; } - void set_orthographic(int value) { - ptr_->orthographic = value; + void set_proj(mjtProjection value) { + ptr_->proj = value; } double fovy() const { return ptr_->fovy; @@ -4162,8 +4162,8 @@ struct MjModel { emscripten::val cam_mat0() const { return emscripten::val(emscripten::typed_memory_view(ptr_->ncam * 9, ptr_->cam_mat0)); } - emscripten::val cam_orthographic() const { - return emscripten::val(emscripten::typed_memory_view(ptr_->ncam, ptr_->cam_orthographic)); + emscripten::val cam_projection() const { + return emscripten::val(emscripten::typed_memory_view(ptr_->ncam, ptr_->cam_projection)); } emscripten::val cam_fovy() const { return emscripten::val(emscripten::typed_memory_view(ptr_->ncam, ptr_->cam_fovy)); @@ -10503,6 +10503,9 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) { .value("mjPLUGIN_SENSOR", mjPLUGIN_SENSOR) .value("mjPLUGIN_PASSIVE", mjPLUGIN_PASSIVE) .value("mjPLUGIN_SDF", mjPLUGIN_SDF); + enum_("mjtProjection") + .value("mjPROJ_PERSPECTIVE", mjPROJ_PERSPECTIVE) + .value("mjPROJ_ORTHOGRAPHIC", mjPROJ_ORTHOGRAPHIC); enum_("mjtRndFlag") .value("mjRND_SHADOW", mjRND_SHADOW) .value("mjRND_WIREFRAME", mjRND_WIREFRAME) @@ -11032,10 +11035,10 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) { .property("cam_ipd", &MjModel::cam_ipd) .property("cam_mat0", &MjModel::cam_mat0) .property("cam_mode", &MjModel::cam_mode) - .property("cam_orthographic", &MjModel::cam_orthographic) .property("cam_pos", &MjModel::cam_pos) .property("cam_pos0", &MjModel::cam_pos0) .property("cam_poscom0", &MjModel::cam_poscom0) + .property("cam_projection", &MjModel::cam_projection) .property("cam_quat", &MjModel::cam_quat) .property("cam_resolution", &MjModel::cam_resolution) .property("cam_sensorsize", &MjModel::cam_sensorsize) @@ -11733,10 +11736,10 @@ 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("orthographic", &MjsCamera::orthographic, &MjsCamera::set_orthographic, reference()) .property("pos", &MjsCamera::pos) .property("principal_length", &MjsCamera::principal_length) .property("principal_pixel", &MjsCamera::principal_pixel) + .property("proj", &MjsCamera::proj, &MjsCamera::set_proj, reference()) .property("quat", &MjsCamera::quat) .property("resolution", &MjsCamera::resolution) .property("sensor_size", &MjsCamera::sensor_size)