Add orthographic cameras.
Orthographic cameras are specified by setting the `orthographic` attribute of the `<camera>` element. The `fovy` attribute is still used to specify the field-of-view, but its semantic is different for orthographic cameras. For orthographic cameras, the field-of-view is expressed in units of length, rather than degrees. Other related changes: * Fix bug in the ordering of `cam_xxx` elements in `mjModel`. * Make camera visualization translucent only when the frustum is visualized. * Added a button to `simulate` to toggle between perspective and orthographic free cameras. https://youtu.be/ZXBTEIDWHhs PiperOrigin-RevId: 642293435 Change-Id: Id090a421ad88ab404b5b27ddbbd6bfc81ad49bc5
This commit is contained in:
committed by
Copybara-Service
parent
171b0d6e06
commit
07fc95ca9a
+24
-6
@@ -2615,11 +2615,20 @@ 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:
|
||||
|
||||
: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<body-camera-fovy>` attribute, see below.
|
||||
|
||||
.. _body-camera-fovy:
|
||||
|
||||
:at:`fovy`: :at-val:`real, "45"`
|
||||
Vertical field of view of the camera, expressed in degrees regardless of the global angle setting. The horizontal
|
||||
field of view is computed automatically given the window size and the vertical field of view.
|
||||
Vertical field-of-view of the camera. If the camera uses a perspective projection, the field-of-view is expressed in
|
||||
degrees, regardless of the global :ref:`compiler/angle <compiler-angle>` setting. If the camera uses an orthographic
|
||||
projection, the field-of-view is expressed in units of length; note that in this case the default of 45 is too large
|
||||
for most scenes and should likely be reduced. In either case, the horizontal field of view is computed automatically
|
||||
given the window size and the vertical field of view.
|
||||
|
||||
.. _body-camera-resolution:
|
||||
|
||||
@@ -7157,14 +7166,22 @@ coordinated visual settings corresponding to a "theme", and then include this fi
|
||||
While all settings in mjVisual are global, the settings here could not be fit into any of the other subsections. So this
|
||||
is effectively a miscellaneous subsection.
|
||||
|
||||
.. _visual-global-orthographic:
|
||||
|
||||
:at:`orthographic`: :at-val:`[false, true], "false"`
|
||||
Whether the free camera uses a perspective projection (the default) or an orthographic projection. Setting this
|
||||
attribute changes the semantic of the :ref:`global/fovy<visual-global-fovy>` attribute, see below.
|
||||
|
||||
.. _visual-global-fovy:
|
||||
|
||||
:at:`fovy`: :at-val:`real, "45"`
|
||||
This attribute specifies the vertical field of view of the free camera, i.e., the camera that is always available in
|
||||
the visualizer even if no cameras are explicitly defined in the model. It is always expressed in degrees, regardless
|
||||
of the setting of the angle attribute of :ref:`compiler <compiler>`, and is also represented in the low level model
|
||||
in degrees. This is because we pass it to OpenGL which uses degrees. The same convention applies to the fovy
|
||||
attribute of the :ref:`camera <body-camera>` element below.
|
||||
the visualizer even if no cameras are explicitly defined in the model. If the camera uses a perspective projection,
|
||||
the field-of-view is expressed in degrees, regardless of the global :ref:`compiler/angle <compiler-angle>` setting.
|
||||
If the camera uses an orthographic projection, the field-of-view is expressed in units of length; note that in this
|
||||
case the default of 45 is too large for most scenes and should likely be reduced. In either case, the horizontal
|
||||
field of view is computed automatically given the window size and the vertical field of view. The same convention
|
||||
applies to the :ref:`camera/fovy <body-camera-fovy>` attribute.
|
||||
|
||||
.. _visual-global-ipd:
|
||||
|
||||
@@ -7869,6 +7886,7 @@ if omitted.
|
||||
| This element sets the attributes of the dummy :ref:`site <body-site>` element of the defaults class.
|
||||
| All site attributes are available here except: name, class.
|
||||
|
||||
.. _default-camera-orthographic:
|
||||
|
||||
.. _default-camera-fovy:
|
||||
|
||||
|
||||
+14
-12
@@ -277,15 +277,15 @@
|
||||
| :ref:`camera | \* | :class: mjcf-attributes |
|
||||
| <body-camera>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`name<body-camera-name>` | :ref:`class<body-camera-class>` | :ref:`fovy<body-camera-fovy>` | :ref:`ipd<body-camera-ipd>` | |
|
||||
| | | | :ref:`name<body-camera-name>` | :ref:`class<body-camera-class>` | :ref:`orthographic<body-camera-orthographic>` | :ref:`fovy<body-camera-fovy>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`resolution<body-camera-resolution>` | :ref:`pos<body-camera-pos>` | :ref:`quat<body-camera-quat>` | :ref:`axisangle<body-camera-axisangle>` | |
|
||||
| | | | :ref:`ipd<body-camera-ipd>` | :ref:`resolution<body-camera-resolution>` | :ref:`pos<body-camera-pos>` | :ref:`quat<body-camera-quat>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`xyaxes<body-camera-xyaxes>` | :ref:`zaxis<body-camera-zaxis>` | :ref:`euler<body-camera-euler>` | :ref:`mode<body-camera-mode>` | |
|
||||
| | | | :ref:`axisangle<body-camera-axisangle>` | :ref:`xyaxes<body-camera-xyaxes>` | :ref:`zaxis<body-camera-zaxis>` | :ref:`euler<body-camera-euler>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`target<body-camera-target>` | :ref:`focal<body-camera-focal>` | :ref:`focalpixel<body-camera-focalpixel>` | :ref:`principal<body-camera-principal>` | |
|
||||
| | | | :ref:`mode<body-camera-mode>` | :ref:`target<body-camera-target>` | :ref:`focal<body-camera-focal>` | :ref:`focalpixel<body-camera-focalpixel>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`principalpixel<body-camera-principalpixel>` | :ref:`sensorsize<body-camera-sensorsize>` | :ref:`user<body-camera-user>` | | |
|
||||
| | | | :ref:`principal<body-camera-principal>` | :ref:`principalpixel<body-camera-principalpixel>` | :ref:`sensorsize<body-camera-sensorsize>` | :ref:`user<body-camera-user>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_| body |br| |_| |L| | | .. table:: |
|
||||
@@ -1246,11 +1246,11 @@
|
||||
| :ref:`global | ? | :class: mjcf-attributes |
|
||||
| <visual-global>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`fovy<visual-global-fovy>` | :ref:`ipd<visual-global-ipd>` | :ref:`azimuth<visual-global-azimuth>` | :ref:`elevation<visual-global-elevation>` | |
|
||||
| | | | :ref:`orthographic<visual-global-orthographic>` | :ref:`fovy<visual-global-fovy>` | :ref:`ipd<visual-global-ipd>` | :ref:`azimuth<visual-global-azimuth>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`linewidth<visual-global-linewidth>` | :ref:`glow<visual-global-glow>` | :ref:`offwidth<visual-global-offwidth>` | :ref:`offheight<visual-global-offheight>` | |
|
||||
| | | | :ref:`elevation<visual-global-elevation>` | :ref:`linewidth<visual-global-linewidth>` | :ref:`glow<visual-global-glow>` | :ref:`offwidth<visual-global-offwidth>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`realtime<visual-global-realtime>` | :ref:`ellipsoidinertia<visual-global-ellipsoidinertia>` | :ref:`bvactive<visual-global-bvactive>` | | |
|
||||
| | | | :ref:`offheight<visual-global-offheight>` | :ref:`realtime<visual-global-realtime>` | :ref:`ellipsoidinertia<visual-global-ellipsoidinertia>` | :ref:`bvactive<visual-global-bvactive>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_| visual |br| |_| |L| | | .. table:: |
|
||||
@@ -1396,13 +1396,15 @@
|
||||
| :ref:`camera | ? | :class: mjcf-attributes |
|
||||
| <default-camera>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`fovy<default-camera-fovy>` | :ref:`ipd<default-camera-ipd>` | :ref:`resolution<default-camera-resolution>` | :ref:`pos<default-camera-pos>` | |
|
||||
| | | | :ref:`orthographic<default-camera-orthographic>` | :ref:`fovy<default-camera-fovy>` | :ref:`ipd<default-camera-ipd>` | :ref:`resolution<default-camera-resolution>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`quat<default-camera-quat>` | :ref:`axisangle<default-camera-axisangle>` | :ref:`xyaxes<default-camera-xyaxes>` | :ref:`zaxis<default-camera-zaxis>` | |
|
||||
| | | | :ref:`pos<default-camera-pos>` | :ref:`quat<default-camera-quat>` | :ref:`axisangle<default-camera-axisangle>` | :ref:`xyaxes<default-camera-xyaxes>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`euler<default-camera-euler>` | :ref:`mode<default-camera-mode>` | :ref:`focal<default-camera-focal>` | :ref:`focalpixel<default-camera-focalpixel>` | |
|
||||
| | | | :ref:`zaxis<default-camera-zaxis>` | :ref:`euler<default-camera-euler>` | :ref:`mode<default-camera-mode>` | :ref:`focal<default-camera-focal>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`principal<default-camera-principal>` | :ref:`principalpixel<default-camera-principalpixel>` | :ref:`sensorsize<default-camera-sensorsize>` | :ref:`user<default-camera-user>` | |
|
||||
| | | | :ref:`focalpixel<default-camera-focalpixel>` | :ref:`principal<default-camera-principal>` | :ref:`principalpixel<default-camera-principalpixel>` | :ref:`sensorsize<default-camera-sensorsize>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`user<default-camera-user>` | | | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_| default |br| |_| |L| | | .. table:: |
|
||||
|
||||
+11
-5
@@ -13,15 +13,21 @@ General
|
||||
- Detailed documentation.
|
||||
- Python bindings.
|
||||
|
||||
2. Added :ref:`maxhullvert<asset-mesh-maxhullvert>`, the maximum number of vertices in a mesh's convex hull.
|
||||
.. youtube:: ZXBTEIDWHhs
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
2. Added support for orthographic cameras. This is available for both fixed cameras and the free camera, using the
|
||||
:ref:`camera/orthographic<body-camera-orthographic>` and :ref:`global/orthographic<visual-global-orthographic>`
|
||||
attributes, respectively.
|
||||
3. Added :ref:`maxhullvert<asset-mesh-maxhullvert>`, the maximum number of vertices in a mesh's convex hull.
|
||||
|
||||
|
||||
MJX
|
||||
~~~
|
||||
|
||||
3. Added support for :ref:`elliptic friction cones<option-cone>`.
|
||||
4. Fixed a bug that resulted in less-optimal linesearch solutions for some difficult constraint settings.
|
||||
5. Fixed a bug in the Newton solver that sometimes resulted in less-optimal gradients.
|
||||
4. Added support for :ref:`elliptic friction cones<option-cone>`.
|
||||
5. Fixed a bug that resulted in less-optimal linesearch solutions for some difficult constraint settings.
|
||||
6. Fixed a bug in the Newton solver that sometimes resulted in less-optimal gradients.
|
||||
|
||||
Version 3.1.6 (Jun 3, 2024)
|
||||
---------------------------
|
||||
|
||||
@@ -751,7 +751,8 @@ struct mjOption_ { // physics options
|
||||
typedef struct mjOption_ mjOption;
|
||||
struct mjVisual_ { // visualization options
|
||||
struct { // global parameters
|
||||
float fovy; // y-field of view for free camera (degrees)
|
||||
int orthographic; // is the free camera orthographic (0: no, 1: yes)
|
||||
float fovy; // y field-of-view of free camera (orthographic ? length : degree)
|
||||
float ipd; // inter-pupilary distance for free camera
|
||||
float azimuth; // initial azimuth of free camera (degrees)
|
||||
float elevation; // initial elevation of free camera (degrees)
|
||||
@@ -1072,11 +1073,12 @@ 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_resolution; // [width, height] in pixels (ncam x 2)
|
||||
mjtNum* cam_fovy; // y-field of view (deg) (ncam x 1)
|
||||
float* cam_intrinsic; // [focal length; principal point] (ncam x 4)
|
||||
float* cam_sensorsize; // sensor size (ncam x 2)
|
||||
int* cam_orthographic; // orthographic camera; 0: no, 1: yes (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)
|
||||
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)
|
||||
|
||||
// lights
|
||||
@@ -1858,6 +1860,7 @@ typedef struct mjsCamera_ { // camera specification
|
||||
mjString* targetbody; // target body for tracking/targeting
|
||||
|
||||
// intrinsics
|
||||
int orthographic; // is camera orthographic
|
||||
double fovy; // y-field of view
|
||||
double ipd; // inter-pupilary distance
|
||||
float intrinsic[4]; // camera intrinsics (length)
|
||||
@@ -2602,6 +2605,9 @@ struct mjvCamera_ { // abstract camera
|
||||
mjtNum distance; // distance to lookat point or tracked body
|
||||
mjtNum azimuth; // camera azimuth (deg)
|
||||
mjtNum elevation; // camera elevation (deg)
|
||||
|
||||
// orthographic / perspective
|
||||
int orthographic; // 0: perspective; 1: orthographic
|
||||
};
|
||||
typedef struct mjvCamera_ mjvCamera;
|
||||
struct mjvGLCamera_ { // OpenGL camera
|
||||
@@ -2617,6 +2623,9 @@ struct mjvGLCamera_ { // OpenGL camera
|
||||
float frustum_top; // top
|
||||
float frustum_near; // near
|
||||
float frustum_far; // far
|
||||
|
||||
// orthographic / perspective
|
||||
int orthographic; // 0: perspective; 1: orthographic
|
||||
};
|
||||
typedef struct mjvGLCamera_ mjvGLCamera;
|
||||
struct mjvGeom_ { // abstract geom
|
||||
@@ -2869,10 +2878,12 @@ struct mjvSceneState_ {
|
||||
mjtNum* site_size;
|
||||
float* site_rgba;
|
||||
|
||||
int* cam_orthographic;
|
||||
mjtNum* cam_fovy;
|
||||
mjtNum* cam_ipd;
|
||||
float* cam_intrinsic;
|
||||
int* cam_resolution;
|
||||
float* cam_sensorsize;
|
||||
float* cam_intrinsic;
|
||||
|
||||
mjtByte* light_directional;
|
||||
mjtByte* light_castshadow;
|
||||
|
||||
@@ -458,7 +458,8 @@ typedef struct mjOption_ mjOption;
|
||||
|
||||
struct mjVisual_ { // visualization options
|
||||
struct { // global parameters
|
||||
float fovy; // y-field of view for free camera (degrees)
|
||||
int orthographic; // is the free camera orthographic (0: no, 1: yes)
|
||||
float fovy; // y field-of-view of free camera (orthographic ? length : degree)
|
||||
float ipd; // inter-pupilary distance for free camera
|
||||
float azimuth; // initial azimuth of free camera (degrees)
|
||||
float elevation; // initial elevation of free camera (degrees)
|
||||
@@ -787,11 +788,12 @@ 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_resolution; // [width, height] in pixels (ncam x 2)
|
||||
mjtNum* cam_fovy; // y-field of view (deg) (ncam x 1)
|
||||
float* cam_intrinsic; // [focal length; principal point] (ncam x 4)
|
||||
float* cam_sensorsize; // sensor size (ncam x 2)
|
||||
int* cam_orthographic; // orthographic camera; 0: no, 1: yes (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)
|
||||
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)
|
||||
|
||||
// lights
|
||||
|
||||
@@ -345,6 +345,7 @@ typedef struct mjsCamera_ { // camera specification
|
||||
mjString* targetbody; // target body for tracking/targeting
|
||||
|
||||
// intrinsics
|
||||
int orthographic; // is camera orthographic
|
||||
double fovy; // y-field of view
|
||||
double ipd; // inter-pupilary distance
|
||||
float intrinsic[4]; // camera intrinsics (length)
|
||||
|
||||
@@ -192,6 +192,9 @@ struct mjvCamera_ { // abstract camera
|
||||
mjtNum distance; // distance to lookat point or tracked body
|
||||
mjtNum azimuth; // camera azimuth (deg)
|
||||
mjtNum elevation; // camera elevation (deg)
|
||||
|
||||
// orthographic / perspective
|
||||
int orthographic; // 0: perspective; 1: orthographic
|
||||
};
|
||||
typedef struct mjvCamera_ mjvCamera;
|
||||
|
||||
@@ -211,6 +214,9 @@ struct mjvGLCamera_ { // OpenGL camera
|
||||
float frustum_top; // top
|
||||
float frustum_near; // near
|
||||
float frustum_far; // far
|
||||
|
||||
// orthographic / perspective
|
||||
int orthographic; // 0: perspective; 1: orthographic
|
||||
};
|
||||
typedef struct mjvGLCamera_ mjvGLCamera;
|
||||
|
||||
@@ -487,10 +493,12 @@ struct mjvSceneState_ {
|
||||
mjtNum* site_size;
|
||||
float* site_rgba;
|
||||
|
||||
int* cam_orthographic;
|
||||
mjtNum* cam_fovy;
|
||||
mjtNum* cam_ipd;
|
||||
float* cam_intrinsic;
|
||||
int* cam_resolution;
|
||||
float* cam_sensorsize;
|
||||
float* cam_intrinsic;
|
||||
|
||||
mjtByte* light_directional;
|
||||
mjtByte* light_castshadow;
|
||||
|
||||
@@ -277,16 +277,17 @@
|
||||
X ( int, cam_mode, ncam, 1 ) \
|
||||
X ( int, cam_bodyid, ncam, 1 ) \
|
||||
X ( int, cam_targetbodyid, ncam, 1 ) \
|
||||
X ( int, cam_resolution, ncam, 2 ) \
|
||||
XMJV( float, cam_sensorsize, ncam, 2 ) \
|
||||
XMJV( float, cam_intrinsic, ncam, 4 ) \
|
||||
X ( mjtNum, cam_pos, ncam, 3 ) \
|
||||
X ( mjtNum, cam_quat, ncam, 4 ) \
|
||||
X ( mjtNum, cam_poscom0, ncam, 3 ) \
|
||||
X ( mjtNum, cam_pos0, ncam, 3 ) \
|
||||
X ( mjtNum, cam_mat0, ncam, 9 ) \
|
||||
XMJV( int, cam_orthographic, ncam, 1 ) \
|
||||
XMJV( mjtNum, cam_fovy, ncam, 1 ) \
|
||||
XMJV( mjtNum, cam_ipd, ncam, 1 ) \
|
||||
XMJV( int, cam_resolution, ncam, 2 ) \
|
||||
XMJV( float, cam_sensorsize, ncam, 2 ) \
|
||||
XMJV( float, cam_intrinsic, ncam, 4 ) \
|
||||
X ( mjtNum, cam_user, ncam, MJ_M(nuser_cam) ) \
|
||||
X ( int, light_mode, nlight, 1 ) \
|
||||
X ( int, light_bodyid, nlight, 1 ) \
|
||||
|
||||
+61
-20
@@ -310,10 +310,15 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
name='global',
|
||||
type=AnonymousStructDecl(
|
||||
fields=(
|
||||
StructFieldDecl(
|
||||
name='orthographic',
|
||||
type=ValueType(name='int'),
|
||||
doc='is the free camera orthographic (0: no, 1: yes)', # pylint: disable=line-too-long
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='fovy',
|
||||
type=ValueType(name='float'),
|
||||
doc='y-field of view for free camera (degrees)',
|
||||
doc='y field-of-view of free camera (orthographic ? length : degree)', # pylint: disable=line-too-long
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='ipd',
|
||||
@@ -2024,32 +2029,18 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
doc='global orientation in qpos0 (ncam x 9)',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='cam_resolution',
|
||||
name='cam_orthographic',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='int'),
|
||||
),
|
||||
doc='[width, height] in pixels (ncam x 2)',
|
||||
doc='orthographic camera; 0: no, 1: yes (ncam x 1)',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='cam_fovy',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjtNum'),
|
||||
),
|
||||
doc='y-field of view (deg) (ncam x 1)',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='cam_intrinsic',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='float'),
|
||||
),
|
||||
doc='[focal length; principal point] (ncam x 4)',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='cam_sensorsize',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='float'),
|
||||
),
|
||||
doc='sensor size (ncam x 2)',
|
||||
doc='y field-of-view (ortho ? len : deg) (ncam x 1)',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='cam_ipd',
|
||||
@@ -2058,6 +2049,27 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
),
|
||||
doc='inter-pupilary distance (ncam x 1)',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='cam_resolution',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='int'),
|
||||
),
|
||||
doc='resolution: pixels [width, height] (ncam x 2)',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='cam_sensorsize',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='float'),
|
||||
),
|
||||
doc='sensor size: length [width, height] (ncam x 2)',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='cam_intrinsic',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='float'),
|
||||
),
|
||||
doc='[focal length; principal point] (ncam x 4)',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='cam_user',
|
||||
type=PointerType(
|
||||
@@ -5367,6 +5379,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
type=ValueType(name='mjtNum'),
|
||||
doc='camera elevation (deg)',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='orthographic',
|
||||
type=ValueType(name='int'),
|
||||
doc='0: perspective; 1: orthographic',
|
||||
),
|
||||
),
|
||||
)),
|
||||
('mjvGLCamera',
|
||||
@@ -5428,6 +5445,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
type=ValueType(name='float'),
|
||||
doc='far',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='orthographic',
|
||||
type=ValueType(name='int'),
|
||||
doc='0: perspective; 1: orthographic',
|
||||
),
|
||||
),
|
||||
)),
|
||||
('mjvGeom',
|
||||
@@ -6701,6 +6723,13 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
),
|
||||
doc='',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='cam_orthographic',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='int'),
|
||||
),
|
||||
doc='',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='cam_fovy',
|
||||
type=PointerType(
|
||||
@@ -6716,14 +6745,21 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
doc='',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='cam_intrinsic',
|
||||
name='cam_resolution',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='int'),
|
||||
),
|
||||
doc='',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='cam_sensorsize',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='float'),
|
||||
),
|
||||
doc='',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='cam_sensorsize',
|
||||
name='cam_intrinsic',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='float'),
|
||||
),
|
||||
@@ -9216,6 +9252,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
),
|
||||
doc='target body for tracking/targeting',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='orthographic',
|
||||
type=ValueType(name='int'),
|
||||
doc='is camera orthographic',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='fovy',
|
||||
type=ValueType(name='double'),
|
||||
|
||||
@@ -1390,6 +1390,7 @@ PYBIND11_MODULE(_structs, m) {
|
||||
});
|
||||
DefineStructFunctions(mjVisualGlobal);
|
||||
#define X(var) mjVisualGlobal.def_readwrite(#var, &raw::MjVisualGlobal::var)
|
||||
X(orthographic);
|
||||
X(fovy);
|
||||
X(ipd);
|
||||
X(azimuth);
|
||||
@@ -2123,6 +2124,7 @@ This is useful for example when the MJB is not available as a file on disk.)"));
|
||||
X(distance);
|
||||
X(azimuth);
|
||||
X(elevation);
|
||||
X(orthographic);
|
||||
#undef X
|
||||
|
||||
#define X(var) DefinePyArray(mjvCamera, #var, &MjvCameraWrapper::var)
|
||||
@@ -2153,6 +2155,7 @@ This is useful for example when the MJB is not available as a file on disk.)"));
|
||||
X(frustum_top);
|
||||
X(frustum_near);
|
||||
X(frustum_far);
|
||||
X(orthographic);
|
||||
#undef X
|
||||
|
||||
#define X(var) DefinePyArray(mjvGLCamera, #var, &MjvGLCameraWrapper::var)
|
||||
|
||||
@@ -887,14 +887,15 @@ void MakeVisualizationSection(mj::Simulate* sim, const mjModel* m, int oldstate)
|
||||
{mjITEM_EDITFLOAT, "Ambient", 2, &(vis->headlight.ambient), "3"},
|
||||
{mjITEM_EDITFLOAT, "Diffuse", 2, &(vis->headlight.diffuse), "3"},
|
||||
{mjITEM_EDITFLOAT, "Specular", 2, &(vis->headlight.specular), "3"},
|
||||
{mjITEM_SEPARATOR, "Initial Free Camera", 1},
|
||||
{mjITEM_SEPARATOR, "Free Camera", 1},
|
||||
{mjITEM_RADIO, "Orthographic", 2, &(vis->global.orthographic), "No\nYes"},
|
||||
{mjITEM_EDITFLOAT, "Field of view", 2, &(vis->global.fovy), "1"},
|
||||
{mjITEM_EDITNUM, "Center", 2, &(stat->center), "3"},
|
||||
{mjITEM_EDITFLOAT, "Azimuth", 2, &(vis->global.azimuth), "1"},
|
||||
{mjITEM_EDITFLOAT, "Elevation", 2, &(vis->global.elevation), "1"},
|
||||
{mjITEM_BUTTON, "Align", 2, nullptr, "CA"},
|
||||
{mjITEM_SEPARATOR, "Global", 1},
|
||||
{mjITEM_EDITNUM, "Extent", 2, &(stat->extent), "1"},
|
||||
{mjITEM_EDITFLOAT, "Field of view", 2, &(vis->global.fovy), "1"},
|
||||
{mjITEM_RADIO, "Inertia", 5, &(vis->global.ellipsoidinertia), "Box\nEllipsoid"},
|
||||
{mjITEM_RADIO, "BVH active", 5, &(vis->global.bvactive), "False\nTrue"},
|
||||
{mjITEM_SEPARATOR, "Map", 1},
|
||||
|
||||
@@ -182,6 +182,7 @@ static void setf4(float* rgba, float r, float g, float b, float a) {
|
||||
// set visual options to default values
|
||||
void mj_defaultVisual(mjVisual* vis) {
|
||||
// global
|
||||
vis->global.orthographic = 0;
|
||||
vis->global.fovy = 45;
|
||||
vis->global.ipd = 0.068;
|
||||
vis->global.azimuth = 90;
|
||||
@@ -257,7 +258,7 @@ void mj_defaultVisual(mjVisual* vis) {
|
||||
setf4(vis->rgba.actuatornegative, .2, .6, .9, 1.);
|
||||
setf4(vis->rgba.actuatorpositive, .9, .4, .2, 1.);
|
||||
setf4(vis->rgba.com, .9, .9, .9, 1.);
|
||||
setf4(vis->rgba.camera, .6, .9, .6, .3);
|
||||
setf4(vis->rgba.camera, .6, .9, .6, 1);
|
||||
setf4(vis->rgba.light, .6, .6, .9, 1.);
|
||||
setf4(vis->rgba.selectpoint, .9, .9, .1, 1.);
|
||||
setf4(vis->rgba.connect, .2, .2, .8, 1.);
|
||||
|
||||
@@ -359,15 +359,16 @@ void mjv_defaultCamera(mjvCamera* cam) {
|
||||
void mjv_defaultFreeCamera(const mjModel* m, mjvCamera* cam) {
|
||||
memset(cam, 0, sizeof(mjvCamera));
|
||||
|
||||
cam->type = mjCAMERA_FREE;
|
||||
cam->fixedcamid = -1;
|
||||
cam->trackbodyid = -1;
|
||||
cam->lookat[0] = m->stat.center[0];
|
||||
cam->lookat[1] = m->stat.center[1];
|
||||
cam->lookat[2] = m->stat.center[2];
|
||||
cam->distance = 1.5 * m->stat.extent;
|
||||
cam->azimuth = m->vis.global.azimuth;
|
||||
cam->elevation = m->vis.global.elevation;
|
||||
cam->type = mjCAMERA_FREE;
|
||||
cam->fixedcamid = -1;
|
||||
cam->trackbodyid = -1;
|
||||
cam->lookat[0] = m->stat.center[0];
|
||||
cam->lookat[1] = m->stat.center[1];
|
||||
cam->lookat[2] = m->stat.center[2];
|
||||
cam->distance = 1.5 * m->stat.extent;
|
||||
cam->azimuth = m->vis.global.azimuth;
|
||||
cam->elevation = m->vis.global.elevation;
|
||||
cam->orthographic = m->vis.global.orthographic;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -223,16 +223,29 @@ void mjv_cameraInRoom(mjtNum* headpos, mjtNum* forward, mjtNum* up, const mjvSce
|
||||
|
||||
// get frustum height at unit distance from camera; average left and right OpenGL cameras
|
||||
mjtNum mjv_frustumHeight(const mjvScene* scn) {
|
||||
mjtNum height;
|
||||
const mjvGLCamera* cam1 = scn->camera;
|
||||
const mjvGLCamera* cam2 = scn->camera + 1;
|
||||
|
||||
// check znear
|
||||
if (scn->camera[0].frustum_near < mjMINVAL || scn->camera[1].frustum_near < mjMINVAL) {
|
||||
mjERROR("mjvScene frustum_near too small");
|
||||
if (cam1->orthographic != cam2->orthographic) {
|
||||
mjERROR("cannot average frustums of perspective and orthographic cameras");
|
||||
}
|
||||
|
||||
// add normalized height for left and right cameras
|
||||
height = (scn->camera[0].frustum_top-scn->camera[0].frustum_bottom)/scn->camera[0].frustum_near +
|
||||
(scn->camera[1].frustum_top-scn->camera[1].frustum_bottom)/scn->camera[1].frustum_near;
|
||||
// get height
|
||||
mjtNum height;
|
||||
if (!cam1->orthographic) {
|
||||
// check znear
|
||||
if (cam1->frustum_near < mjMINVAL || cam2->frustum_near < mjMINVAL) {
|
||||
mjERROR("mjvScene frustum_near too small");
|
||||
}
|
||||
|
||||
// add normalized height for left and right cameras
|
||||
height = (cam1->frustum_top - cam1->frustum_bottom) / cam1->frustum_near +
|
||||
(cam2->frustum_top - cam2->frustum_bottom) / cam2->frustum_near;
|
||||
} else {
|
||||
// add height for left and right cameras
|
||||
height = (cam1->frustum_top - cam1->frustum_bottom) +
|
||||
(cam2->frustum_top - cam2->frustum_bottom);
|
||||
}
|
||||
|
||||
// average
|
||||
return 0.5*height;
|
||||
@@ -337,6 +350,9 @@ void mjv_moveCamera(const mjModel* m, int action, mjtNum reldx, mjtNum reldy,
|
||||
mju_sub3(dif, cam->lookat, headpos);
|
||||
scl = mjv_frustumHeight(scn) * mju_dot3(dif, forward);
|
||||
|
||||
// multiply by mystery coefficient TODO: b/346130949
|
||||
if (cam->orthographic) scl *= 0.15;
|
||||
|
||||
// move lookat point in opposite direction
|
||||
mju_addToScl3(cam->lookat, vec, -scl);
|
||||
break;
|
||||
@@ -563,6 +579,9 @@ void mjv_initPerturb(const mjModel* m, mjData* d, const mjvScene* scn, mjvPertur
|
||||
mju_sub3(dif, pert->refselpos, headpos);
|
||||
pert->scale = mjv_frustumHeight(scn) * mju_dot3(dif, forward);
|
||||
|
||||
// multiply by mystery coefficient TODO: b/346130949
|
||||
if (scn->camera[0].orthographic) pert->scale *= 0.15;
|
||||
|
||||
mj_freeStack(d);
|
||||
}
|
||||
|
||||
@@ -733,6 +752,12 @@ mjvGLCamera mjv_averageCamera(const mjvGLCamera* cam1, const mjvGLCamera* cam2)
|
||||
cam.frustum_near = 0.5f * (cam1->frustum_near + cam2->frustum_near);
|
||||
cam.frustum_far = 0.5f * (cam1->frustum_far + cam2->frustum_far);
|
||||
|
||||
if (cam1->orthographic != cam2->orthographic) {
|
||||
mjERROR("cannot average perspective and orthographic cameras");
|
||||
} else {
|
||||
cam.orthographic = cam1->orthographic;
|
||||
}
|
||||
|
||||
return cam;
|
||||
}
|
||||
|
||||
@@ -755,17 +780,31 @@ int mjv_select(const mjModel* m, const mjData* d, const mjvOption* vopt,
|
||||
// compute frustum halfwidth so as to match viewport aspect ratio
|
||||
mjtNum halfwidth = 0.5*aspectratio*(cam.frustum_top - cam.frustum_bottom);
|
||||
|
||||
// construct ray
|
||||
// compute up and left offsets from normalized cursor
|
||||
mjtNum d_up = cam.frustum_bottom + rely*(cam.frustum_top-cam.frustum_bottom);
|
||||
mjtNum d_left = -(cam.frustum_center + (2*relx-1)*halfwidth);
|
||||
|
||||
// define ray
|
||||
mjtNum ray[3];
|
||||
mju_scl3(ray, forward, cam.frustum_near);
|
||||
mju_addToScl3(ray, up, cam.frustum_bottom + rely*(cam.frustum_top-cam.frustum_bottom));
|
||||
mju_addToScl3(ray, left, -(cam.frustum_center + (2*relx-1)*halfwidth));
|
||||
mju_normalize3(ray);
|
||||
|
||||
// construct ray for orthographic camera: fixed direction, modify pos
|
||||
if (cam.orthographic) {
|
||||
mju_copy3(ray, forward);
|
||||
mju_addToScl3(pos, up, d_up);
|
||||
mju_addToScl3(pos, left, d_left);
|
||||
}
|
||||
|
||||
// construct ray for perspective camera: fixed pos, modify direction
|
||||
else {
|
||||
mju_scl3(ray, forward, cam.frustum_near);
|
||||
mju_addToScl3(ray, up, d_up);
|
||||
mju_addToScl3(ray, left, d_left);
|
||||
mju_normalize3(ray);
|
||||
}
|
||||
|
||||
// find intersection with geoms
|
||||
*geomid = -1;
|
||||
mjtNum geomdist = mj_ray(m, d, pos, ray, vopt->geomgroup,
|
||||
vopt->flags[mjVIS_STATIC], -1, geomid);
|
||||
mjtNum geomdist = mj_ray(m, d, pos, ray, vopt->geomgroup, vopt->flags[mjVIS_STATIC], -1, geomid);
|
||||
|
||||
// find intersection with flexes
|
||||
int flexbodyid = -1;
|
||||
@@ -851,7 +890,6 @@ int mjv_select(const mjModel* m, const mjData* d, const mjvOption* vopt,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// geom
|
||||
if (best == 0) {
|
||||
*flexid = -1;
|
||||
|
||||
+140
-115
@@ -511,11 +511,11 @@ static int bodycategory(const mjModel* m, int bodyid) {
|
||||
|
||||
// computes the camera frustum
|
||||
static void getFrustum(float zver[2], float zhor[2], float znear,
|
||||
const float K[4], const float sensorsize[2]) {
|
||||
zhor[0] = znear / K[0] * (sensorsize[0]/2.f - K[2]);
|
||||
zhor[1] = znear / K[0] * (sensorsize[0]/2.f + K[2]);
|
||||
zver[0] = znear / K[1] * (sensorsize[1]/2.f - K[3]);
|
||||
zver[1] = znear / K[1] * (sensorsize[1]/2.f + K[3]);
|
||||
const float intrinsic[4], const float sensorsize[2]) {
|
||||
zhor[0] = znear / intrinsic[0] * (sensorsize[0]/2.f - intrinsic[2]);
|
||||
zhor[1] = znear / intrinsic[0] * (sensorsize[0]/2.f + intrinsic[2]);
|
||||
zver[0] = znear / intrinsic[1] * (sensorsize[1]/2.f - intrinsic[3]);
|
||||
zver[1] = znear / intrinsic[1] * (sensorsize[1]/2.f + intrinsic[3]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1502,11 +1502,97 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt,
|
||||
}
|
||||
}
|
||||
|
||||
// cameras
|
||||
// cameras and frustums
|
||||
objtype = mjOBJ_CAMERA;
|
||||
category = mjCAT_DECOR;
|
||||
if (vopt->flags[mjVIS_CAMERA] && (category & catmask)) {
|
||||
for (int i=0; i < m->ncam; i++) {
|
||||
// copy camera rgba
|
||||
float cam_rgba[4];
|
||||
f2f(cam_rgba, m->vis.rgba.camera, 4);
|
||||
|
||||
// draw frustum if sensorsize is defined
|
||||
if (m->cam_sensorsize[2*i+1] > 0) {
|
||||
// when drawing frustum, make camera translucent
|
||||
cam_rgba[3] = 0.3;
|
||||
|
||||
// locals
|
||||
const float* rgba = m->vis.rgba.frustum;
|
||||
mjtNum vnear[4][3], vfar[4][3];
|
||||
mjtNum center[3];
|
||||
mjtNum znear = m->vis.map.znear * m->stat.extent;
|
||||
mjtNum zfar = m->vis.scale.frustum * scl;
|
||||
float zver[2], zhor[2];
|
||||
|
||||
// get frustum
|
||||
getFrustum(zver, zhor, znear, m->cam_intrinsic + 4*i, m->cam_sensorsize + 2*i);
|
||||
|
||||
// frustum frame to convert from planes to vertex representation
|
||||
mjtNum *cam_xpos = d->cam_xpos+3*i;
|
||||
mjtNum *cam_xmat = d->cam_xmat+9*i;
|
||||
mjtNum x[] = {cam_xmat[0], cam_xmat[3], cam_xmat[6]};
|
||||
mjtNum y[] = {cam_xmat[1], cam_xmat[4], cam_xmat[7]};
|
||||
mjtNum z[] = {cam_xmat[2], cam_xmat[5], cam_xmat[8]};
|
||||
|
||||
// vertices of the near plane
|
||||
mju_addScl3(center, cam_xpos, z, -znear);
|
||||
mju_addScl3(vnear[0], center, x, -zhor[0]);
|
||||
mju_addScl3(vnear[1], center, x, zhor[1]);
|
||||
mju_addScl3(vnear[2], center, x, zhor[1]);
|
||||
mju_addScl3(vnear[3], center, x, -zhor[0]);
|
||||
mju_addToScl3(vnear[0], y, -zver[0]);
|
||||
mju_addToScl3(vnear[1], y, -zver[0]);
|
||||
mju_addToScl3(vnear[2], y, zver[1]);
|
||||
mju_addToScl3(vnear[3], y, zver[1]);
|
||||
|
||||
// vertices of the far plane
|
||||
zhor[0] *= zfar / znear;
|
||||
zhor[1] *= zfar / znear;
|
||||
zver[0] *= zfar / znear;
|
||||
zver[1] *= zfar / znear;
|
||||
mju_addScl3(center, cam_xpos, z, -zfar);
|
||||
mju_addScl3(vfar[0], center, x, -zhor[0]);
|
||||
mju_addScl3(vfar[1], center, x, zhor[1]);
|
||||
mju_addScl3(vfar[2], center, x, zhor[1]);
|
||||
mju_addScl3(vfar[3], center, x, -zhor[0]);
|
||||
mju_addToScl3(vfar[0], y, -zver[0]);
|
||||
mju_addToScl3(vfar[1], y, -zver[0]);
|
||||
mju_addToScl3(vfar[2], y, zver[1]);
|
||||
mju_addToScl3(vfar[3], y, zver[1]);
|
||||
|
||||
// triangulation and wireframe of the frustum
|
||||
for (int e=0; e < 4; e++) {
|
||||
START
|
||||
mju_sub3(x, vfar[e], vnear[e]);
|
||||
mju_sub3(y, vnear[(e+1)%4], vnear[e]);
|
||||
mju_cross(z, x, y);
|
||||
mjtNum tri1[3] = {mju_normalize3(x), mju_normalize3(y), mju_normalize3(z)};
|
||||
mjtNum xmat1[9] = {x[0], y[0], z[0], x[1], y[1], z[1], x[2], y[2], z[2]};
|
||||
mjv_initGeom(thisgeom, mjGEOM_TRIANGLE, tri1, vnear[e], xmat1, rgba);
|
||||
FINISH
|
||||
START
|
||||
mju_sub3(y, vnear[(e+1)%4], vfar[e]);
|
||||
mju_sub3(x, vfar[(e+1)%4], vfar[e]);
|
||||
mju_cross(z, x, y);
|
||||
mjtNum tri2[3] = {mju_normalize3(x), mju_normalize3(y), mju_normalize3(z)};
|
||||
mjtNum xmat2[9] = {x[0], y[0], z[0], x[1], y[1], z[1], x[2], y[2], z[2]};
|
||||
mjv_initGeom(thisgeom, mjGEOM_TRIANGLE, tri2, vfar[e], xmat2, rgba);
|
||||
FINISH
|
||||
START
|
||||
mjv_connector(thisgeom, mjGEOM_LINE, 3, vnear[e], vnear[(e+1)%4]);
|
||||
f2f(thisgeom->rgba, rgba, 4);
|
||||
FINISH
|
||||
START
|
||||
mjv_connector(thisgeom, mjGEOM_LINE, 3, vfar[e], vfar[(e+1)%4]);
|
||||
f2f(thisgeom->rgba, rgba, 4);
|
||||
FINISH
|
||||
START
|
||||
mjv_connector(thisgeom, mjGEOM_LINE, 3, vnear[e], vfar[e]);
|
||||
f2f(thisgeom->rgba, rgba, 4);
|
||||
FINISH
|
||||
}
|
||||
}
|
||||
|
||||
START
|
||||
|
||||
// construct geom: camera body
|
||||
@@ -1516,7 +1602,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt,
|
||||
thisgeom->size[2] = scl * m->vis.scale.camera * 0.4;
|
||||
mju_n2f(thisgeom->pos, d->cam_xpos+3*i, 3);
|
||||
mju_n2f(thisgeom->mat, d->cam_xmat+9*i, 9);
|
||||
f2f(thisgeom->rgba, m->vis.rgba.camera, 4);
|
||||
f2f(thisgeom->rgba, cam_rgba, 4);
|
||||
|
||||
// vopt->label
|
||||
if (vopt->label == mjLABEL_CAMERA) {
|
||||
@@ -1539,7 +1625,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt,
|
||||
thisgeom->size[1] = scl * m->vis.scale.camera * 0.4;
|
||||
thisgeom->size[2] = scl * m->vis.scale.camera * 0.3;
|
||||
mju_n2f(thisgeom->mat, d->cam_xmat+9*i, 9);
|
||||
f2f(thisgeom->rgba, m->vis.rgba.camera, 4);
|
||||
f2f(thisgeom->rgba, cam_rgba, 4);
|
||||
for (int k=0; k < 3; k++) {
|
||||
thisgeom->rgba[k] *= 0.5; // make lens body darker
|
||||
}
|
||||
@@ -1582,88 +1668,6 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt,
|
||||
}
|
||||
}
|
||||
|
||||
// camera frustum
|
||||
if (vopt->flags[mjVIS_CAMERA]) {
|
||||
objtype = mjOBJ_CAMERA;
|
||||
category = mjCAT_DECOR;
|
||||
const float* rgba = m->vis.rgba.frustum;
|
||||
mjtNum vnear[4][3], vfar[4][3];
|
||||
mjtNum center[3];
|
||||
mjtNum znear = m->vis.map.znear * m->stat.extent;
|
||||
mjtNum zfar = m->vis.scale.frustum * scl;
|
||||
float zver[2], zhor[2];
|
||||
for (int i=0; i < m->ncam; i++) {
|
||||
if (m->cam_sensorsize[2*i+1] == 0) {
|
||||
continue;
|
||||
}
|
||||
getFrustum(zver, zhor, znear, m->cam_intrinsic + 4*i, m->cam_sensorsize + 2*i);
|
||||
|
||||
// frustum frame to convert from planes to vertex representation
|
||||
mjtNum *cam_xpos = d->cam_xpos+3*i;
|
||||
mjtNum *cam_xmat = d->cam_xmat+9*i;
|
||||
mjtNum x[] = {cam_xmat[0], cam_xmat[3], cam_xmat[6]};
|
||||
mjtNum y[] = {cam_xmat[1], cam_xmat[4], cam_xmat[7]};
|
||||
mjtNum z[] = {cam_xmat[2], cam_xmat[5], cam_xmat[8]};
|
||||
|
||||
// vertices of the near plane
|
||||
mju_addScl3(center, cam_xpos, z, -znear);
|
||||
mju_addScl3(vnear[0], center, x, -zhor[0]);
|
||||
mju_addScl3(vnear[1], center, x, zhor[1]);
|
||||
mju_addScl3(vnear[2], center, x, zhor[1]);
|
||||
mju_addScl3(vnear[3], center, x, -zhor[0]);
|
||||
mju_addToScl3(vnear[0], y, -zver[0]);
|
||||
mju_addToScl3(vnear[1], y, -zver[0]);
|
||||
mju_addToScl3(vnear[2], y, zver[1]);
|
||||
mju_addToScl3(vnear[3], y, zver[1]);
|
||||
|
||||
// vertices of the far plane
|
||||
zhor[0] *= zfar / znear;
|
||||
zhor[1] *= zfar / znear;
|
||||
zver[0] *= zfar / znear;
|
||||
zver[1] *= zfar / znear;
|
||||
mju_addScl3(center, cam_xpos, z, -zfar);
|
||||
mju_addScl3(vfar[0], center, x, -zhor[0]);
|
||||
mju_addScl3(vfar[1], center, x, zhor[1]);
|
||||
mju_addScl3(vfar[2], center, x, zhor[1]);
|
||||
mju_addScl3(vfar[3], center, x, -zhor[0]);
|
||||
mju_addToScl3(vfar[0], y, -zver[0]);
|
||||
mju_addToScl3(vfar[1], y, -zver[0]);
|
||||
mju_addToScl3(vfar[2], y, zver[1]);
|
||||
mju_addToScl3(vfar[3], y, zver[1]);
|
||||
|
||||
// triangulation and wireframe of the frustum
|
||||
for (int e=0; e < 4; e++) {
|
||||
START
|
||||
mju_sub3(x, vfar[e], vnear[e]);
|
||||
mju_sub3(y, vnear[(e+1)%4], vnear[e]);
|
||||
mju_cross(z, x, y);
|
||||
mjtNum tri1[3] = {mju_normalize3(x), mju_normalize3(y), mju_normalize3(z)};
|
||||
mjtNum xmat1[9] = {x[0], y[0], z[0], x[1], y[1], z[1], x[2], y[2], z[2]};
|
||||
mjv_initGeom(thisgeom, mjGEOM_TRIANGLE, tri1, vnear[e], xmat1, rgba);
|
||||
FINISH
|
||||
START
|
||||
mju_sub3(y, vnear[(e+1)%4], vfar[e]);
|
||||
mju_sub3(x, vfar[(e+1)%4], vfar[e]);
|
||||
mju_cross(z, x, y);
|
||||
mjtNum tri2[3] = {mju_normalize3(x), mju_normalize3(y), mju_normalize3(z)};
|
||||
mjtNum xmat2[9] = {x[0], y[0], z[0], x[1], y[1], z[1], x[2], y[2], z[2]};
|
||||
mjv_initGeom(thisgeom, mjGEOM_TRIANGLE, tri2, vfar[e], xmat2, rgba);
|
||||
FINISH
|
||||
START
|
||||
mjv_connector(thisgeom, mjGEOM_LINE, 3, vnear[e], vnear[(e+1)%4]);
|
||||
f2f(thisgeom->rgba, rgba, 4);
|
||||
FINISH
|
||||
START
|
||||
mjv_connector(thisgeom, mjGEOM_LINE, 3, vfar[e], vfar[(e+1)%4]);
|
||||
f2f(thisgeom->rgba, rgba, 4);
|
||||
FINISH
|
||||
START
|
||||
mjv_connector(thisgeom, mjGEOM_LINE, 3, vnear[e], vfar[e]);
|
||||
f2f(thisgeom->rgba, rgba, 4);
|
||||
FINISH
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lights
|
||||
objtype = mjOBJ_LIGHT;
|
||||
@@ -2131,28 +2135,31 @@ void mjv_makeLights(const mjModel* m, const mjData* d, mjvScene* scn) {
|
||||
|
||||
// update camera only
|
||||
void mjv_updateCamera(const mjModel* m, const mjData* d, mjvCamera* cam, mjvScene* scn) {
|
||||
mjtNum ca, sa, ce, se, move[3], *mat;
|
||||
mjtNum headpos[3], forward[3], up[3], right[3], ipd;
|
||||
|
||||
// return if nothing to do
|
||||
if (!m || !cam || cam->type == mjCAMERA_USER) {
|
||||
return;
|
||||
}
|
||||
|
||||
// initialize frustum
|
||||
float zver[2], zhor[2] = {0, 0};
|
||||
float znear = m->vis.map.znear * m->stat.extent;
|
||||
float zfar = m->vis.map.zfar * m->stat.extent;
|
||||
// define extrinsics
|
||||
mjtNum move[3];
|
||||
mjtNum headpos[3], forward[3], up[3], right[3];
|
||||
|
||||
// get headpos, forward[3], up, right, ipd, fovy
|
||||
// define intrinsics
|
||||
int cid, orthographic = 0;
|
||||
mjtNum fovy, ipd;
|
||||
float* intrinsic = NULL;
|
||||
float* sensorsize = NULL;
|
||||
|
||||
// get headpos, forward, up, right, ipd, fovy, orthographic, intrinsic
|
||||
switch (cam->type) {
|
||||
case mjCAMERA_FREE:
|
||||
case mjCAMERA_TRACKING:
|
||||
// get global ipd
|
||||
ipd = m->vis.global.ipd;
|
||||
|
||||
// compute image size from global fovy
|
||||
zver[0] = zver[1] = (float)znear * mju_tan(m->vis.global.fovy * (float)(mjPI/360.0));
|
||||
// get orthographic, fovy
|
||||
orthographic = m->vis.global.orthographic;
|
||||
fovy = m->vis.global.fovy;
|
||||
|
||||
// move lookat for tracking
|
||||
if (cam->type == mjCAMERA_TRACKING) {
|
||||
@@ -2168,10 +2175,10 @@ void mjv_updateCamera(const mjModel* m, const mjData* d, mjvCamera* cam, mjvScen
|
||||
}
|
||||
|
||||
// compute frame
|
||||
ca = mju_cos(cam->azimuth/180.0*mjPI);
|
||||
sa = mju_sin(cam->azimuth/180.0*mjPI);
|
||||
ce = mju_cos(cam->elevation/180.0*mjPI);
|
||||
se = mju_sin(cam->elevation/180.0*mjPI);
|
||||
mjtNum ca = mju_cos(cam->azimuth/180.0*mjPI);
|
||||
mjtNum sa = mju_sin(cam->azimuth/180.0*mjPI);
|
||||
mjtNum ce = mju_cos(cam->elevation/180.0*mjPI);
|
||||
mjtNum se = mju_sin(cam->elevation/180.0*mjPI);
|
||||
forward[0] = ce*ca;
|
||||
forward[1] = ce*sa;
|
||||
forward[2] = se;
|
||||
@@ -2184,25 +2191,27 @@ void mjv_updateCamera(const mjModel* m, const mjData* d, mjvCamera* cam, mjvScen
|
||||
mju_addScl3(headpos, cam->lookat, forward, -cam->distance);
|
||||
break;
|
||||
|
||||
case mjCAMERA_FIXED: {
|
||||
// get id and check
|
||||
int cid = cam->fixedcamid;
|
||||
case mjCAMERA_FIXED:
|
||||
// get id, check range
|
||||
cid = cam->fixedcamid;
|
||||
if (cid < 0 || cid >= m->ncam) {
|
||||
mjERROR("fixed camera id is outside valid range");
|
||||
}
|
||||
|
||||
// get camera-specific ipd and fovy
|
||||
// get camera-specific ipd, orthographic, fovy
|
||||
ipd = m->cam_ipd[cid];
|
||||
|
||||
// get frustum from intrinsics or from fovy
|
||||
orthographic = m->cam_orthographic[cid];
|
||||
fovy = m->cam_fovy[cid];
|
||||
|
||||
// if positive sensorsize, get sensorsize and intrinsic
|
||||
if (m->cam_sensorsize[2*cid+1]) {
|
||||
getFrustum(zver, zhor, znear, m->cam_intrinsic + 4*cid, m->cam_sensorsize + 2*cid);
|
||||
} else {
|
||||
zver[0] = zver[1] = (float)znear * mju_tan(m->cam_fovy[cid] * (float)(mjPI/360.0));
|
||||
sensorsize = m->cam_sensorsize + 2*cid;
|
||||
intrinsic = m->cam_intrinsic + 4*cid;
|
||||
}
|
||||
|
||||
// get pointer to camera orientation matrix
|
||||
mat = d->cam_xmat + 9*cid;
|
||||
mjtNum* mat = d->cam_xmat + 9*cid;
|
||||
|
||||
// get frame
|
||||
forward[0] = -mat[2];
|
||||
@@ -2215,13 +2224,26 @@ void mjv_updateCamera(const mjModel* m, const mjData* d, mjvCamera* cam, mjvScen
|
||||
right[1] = mat[3];
|
||||
right[2] = mat[6];
|
||||
mju_copy3(headpos, d->cam_xpos + 3*cid);
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
default:
|
||||
mjERROR("unknown camera type");
|
||||
}
|
||||
|
||||
// convert intrinsics to frustum parameters
|
||||
float znear = m->vis.map.znear * m->stat.extent;
|
||||
float zfar = m->vis.map.zfar * m->stat.extent;
|
||||
float zver[2], zhor[2] = {0, 0};
|
||||
if (orthographic){
|
||||
zver[0] = zver[1] = fovy / 2;
|
||||
} else {
|
||||
if (!intrinsic) {
|
||||
zver[0] = zver[1] = znear * mju_tan(fovy * mjPI/360.0);
|
||||
} else {
|
||||
getFrustum(zver, zhor, znear, intrinsic, sensorsize);
|
||||
}
|
||||
}
|
||||
|
||||
// compute GL cameras
|
||||
for (int view=0; view < 2; view++) {
|
||||
// set frame
|
||||
@@ -2231,6 +2253,9 @@ void mjv_updateCamera(const mjModel* m, const mjData* d, mjvCamera* cam, mjvScen
|
||||
scn->camera[view].up[i] = (float)up[i];
|
||||
}
|
||||
|
||||
// set orthographic
|
||||
scn->camera[view].orthographic = orthographic;
|
||||
|
||||
// set symmetric frustum using intrinsic camera matrix
|
||||
scn->camera[view].frustum_top = zver[1];
|
||||
scn->camera[view].frustum_bottom = -zver[0];
|
||||
|
||||
+15
-5
@@ -694,7 +694,7 @@ static void initLights(mjvScene* scn) {
|
||||
|
||||
// set projection and modelview
|
||||
static void setView(int view, mjrRect viewport, const mjvScene* scn, const mjrContext* con,
|
||||
float* camProject, float* camView) {
|
||||
float camProject[16], float camView[16]) {
|
||||
mjvGLCamera cam;
|
||||
|
||||
// copy specified camera for stereo, average for mono (view = -1)
|
||||
@@ -709,24 +709,34 @@ static void setView(int view, mjrRect viewport, const mjvScene* scn, const mjrCo
|
||||
: 0.5f * (float)viewport.width / (float)viewport.height *
|
||||
(cam.frustum_top - cam.frustum_bottom);
|
||||
|
||||
// set projection
|
||||
// prepare projection
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
if (mjGLAD_GL_ARB_clip_control) {
|
||||
// reverse Z rendering mapping [znear, zfar] -> [1, 0] (ndc)
|
||||
glTranslatef(0.0f, 0.0f, 0.5f);
|
||||
glScalef(1.0f, 1.0f, -0.5f);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// reverse Z rendering mapping without shift [znear, zfar] -> [1, -1] (ndc)
|
||||
glScalef(1.0f, 1.0f, -1.0f);
|
||||
}
|
||||
glFrustum(cam.frustum_center - halfwidth,
|
||||
|
||||
// set projection, orthographic or perspective
|
||||
if (cam.orthographic) {
|
||||
glOrtho(cam.frustum_center - halfwidth,
|
||||
cam.frustum_center + halfwidth,
|
||||
cam.frustum_bottom,
|
||||
cam.frustum_top,
|
||||
cam.frustum_near,
|
||||
cam.frustum_far);
|
||||
} else {
|
||||
glFrustum(cam.frustum_center - halfwidth,
|
||||
cam.frustum_center + halfwidth,
|
||||
cam.frustum_bottom,
|
||||
cam.frustum_top,
|
||||
cam.frustum_near,
|
||||
cam.frustum_far);
|
||||
}
|
||||
|
||||
// save projection matrix if requested
|
||||
if (camProject) {
|
||||
|
||||
@@ -2040,6 +2040,7 @@ void mjCModel::CopyTree(mjModel* m) {
|
||||
m->cam_targetbodyid[cid] = pc->targetbodyid;
|
||||
copyvec(m->cam_pos+3*cid, pc->pos, 3);
|
||||
copyvec(m->cam_quat+4*cid, pc->quat, 4);
|
||||
m->cam_orthographic[cid] = pc->orthographic;
|
||||
m->cam_fovy[cid] = (mjtNum)pc->fovy;
|
||||
m->cam_ipd[cid] = (mjtNum)pc->ipd;
|
||||
copyvec(m->cam_resolution+2*cid, pc->resolution, 2);
|
||||
|
||||
@@ -123,8 +123,8 @@ const char* MJCF[nMJCF][mjXATTRNUM] = {
|
||||
|
||||
{"visual", "*", "0"},
|
||||
{"<"},
|
||||
{"global", "?", "11", "fovy", "ipd", "azimuth", "elevation", "linewidth", "glow",
|
||||
"offwidth", "offheight", "realtime", "ellipsoidinertia", "bvactive"},
|
||||
{"global", "?", "12", "orthographic", "fovy", "ipd", "azimuth", "elevation", "linewidth",
|
||||
"glow", "offwidth", "offheight", "realtime", "ellipsoidinertia", "bvactive"},
|
||||
{"quality", "?", "5", "shadowsize", "offsamples", "numslices", "numstacks",
|
||||
"numquads"},
|
||||
{"headlight", "?", "4", "ambient", "diffuse", "specular", "active"},
|
||||
@@ -160,9 +160,9 @@ const char* MJCF[nMJCF][mjXATTRNUM] = {
|
||||
"hfield", "mesh", "fitscale", "rgba", "fluidshape", "fluidcoef", "user"},
|
||||
{"site", "?", "13", "type", "group", "pos", "quat", "material",
|
||||
"size", "fromto", "axisangle", "xyaxes", "zaxis", "euler", "rgba", "user"},
|
||||
{"camera", "?", "16", "fovy", "ipd", "resolution", "pos", "quat", "axisangle", "xyaxes",
|
||||
"zaxis", "euler", "mode", "focal", "focalpixel", "principal", "principalpixel",
|
||||
"sensorsize", "user"},
|
||||
{"camera", "?", "17", "orthographic", "fovy", "ipd", "resolution", "pos", "quat",
|
||||
"axisangle", "xyaxes", "zaxis", "euler", "mode", "focal", "focalpixel",
|
||||
"principal", "principalpixel", "sensorsize", "user"},
|
||||
{"light", "?", "13", "pos", "dir", "bulbradius", "directional", "castshadow", "active",
|
||||
"attenuation", "cutoff", "exponent", "ambient", "diffuse", "specular", "mode"},
|
||||
{"pair", "?", "7", "condim", "friction", "solref", "solreffriction", "solimp",
|
||||
@@ -268,9 +268,9 @@ const char* MJCF[nMJCF][mjXATTRNUM] = {
|
||||
{">"},
|
||||
{"site", "*", "15", "name", "class", "type", "group", "pos", "quat",
|
||||
"material", "size", "fromto", "axisangle", "xyaxes", "zaxis", "euler", "rgba", "user"},
|
||||
{"camera", "*", "19", "name", "class", "fovy", "ipd", "resolution", "pos", "quat",
|
||||
"axisangle", "xyaxes", "zaxis", "euler", "mode", "target", "focal", "focalpixel",
|
||||
"principal", "principalpixel", "sensorsize", "user"},
|
||||
{"camera", "*", "20", "name", "class", "orthographic", "fovy", "ipd", "resolution", "pos",
|
||||
"quat", "axisangle", "xyaxes", "zaxis", "euler", "mode", "target",
|
||||
"focal", "focalpixel", "principal", "principalpixel", "sensorsize", "user"},
|
||||
{"light", "*", "16", "name", "class", "directional", "castshadow", "active",
|
||||
"pos", "dir", "bulbradius", "attenuation", "cutoff", "exponent", "ambient", "diffuse",
|
||||
"specular", "mode", "target"},
|
||||
@@ -1742,6 +1742,10 @@ void mjXReader::OneCamera(XMLElement* elem, mjsCamera* pcam) {
|
||||
ReadAlternative(elem, pcam->alt);
|
||||
ReadAttr(elem, "ipd", 1, &pcam->ipd, text);
|
||||
|
||||
if (MapValue(elem, "orthographic", &n, bool_map, 2)) {
|
||||
pcam->orthographic = (n==1);
|
||||
}
|
||||
|
||||
bool has_principal = ReadAttr(elem, "principalpixel", 2, pcam->principal_pixel, text) ||
|
||||
ReadAttr(elem, "principal", 2, pcam->principal_length, text);
|
||||
bool has_focal = ReadAttr(elem, "focalpixel", 2, pcam->focal_pixel, text) ||
|
||||
@@ -2948,6 +2952,7 @@ void mjXReader::Visual(XMLElement* section) {
|
||||
string text, name;
|
||||
XMLElement* elem;
|
||||
mjVisual* vis = &model->visual;
|
||||
int n;
|
||||
|
||||
// iterate over child elements
|
||||
elem = FirstChildElement(section);
|
||||
@@ -2957,6 +2962,9 @@ void mjXReader::Visual(XMLElement* section) {
|
||||
|
||||
// global sub-element
|
||||
if (name=="global") {
|
||||
if (MapValue(elem, "orthographic", &n, bool_map, 2)) {
|
||||
vis->global.orthographic = (n==1);
|
||||
}
|
||||
ReadAttr(elem, "fovy", 1, &vis->global.fovy, text);
|
||||
ReadAttr(elem, "ipd", 1, &vis->global.ipd, text);
|
||||
ReadAttr(elem, "azimuth", 1, &vis->global.azimuth, text);
|
||||
@@ -2970,13 +2978,11 @@ void mjXReader::Visual(XMLElement* section) {
|
||||
throw mjXError(elem, "realtime must be greater than 0");
|
||||
}
|
||||
}
|
||||
int ellipsoidinertia;
|
||||
if (MapValue(elem, "ellipsoidinertia", &ellipsoidinertia, bool_map, 2)) {
|
||||
vis->global.ellipsoidinertia = (ellipsoidinertia==1);
|
||||
if (MapValue(elem, "ellipsoidinertia", &n, bool_map, 2)) {
|
||||
vis->global.ellipsoidinertia = (n==1);
|
||||
}
|
||||
int bvactive;
|
||||
if (MapValue(elem, "bvactive", &bvactive, bool_map, 2)) {
|
||||
vis->global.bvactive = (bvactive==1);
|
||||
if (MapValue(elem, "bvactive", &n, bool_map, 2)) {
|
||||
vis->global.bvactive = (n==1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -474,9 +474,7 @@ void mjXWriter::OneCamera(XMLElement* elem, const mjCCamera* pcam, mjCDef* def)
|
||||
WriteAttr(elem, "ipd", 1, &pcam->ipd, &def->Camera().ipd);
|
||||
WriteAttrKey(elem, "mode", camlight_map, camlight_sz, pcam->mode, def->Camera().mode);
|
||||
WriteAttr(elem, "resolution", 2, pcam->resolution, def->Camera().resolution);
|
||||
|
||||
// resolution if positive
|
||||
WriteAttr(elem, "resolution", 2, pcam->resolution, def->Camera().resolution);
|
||||
WriteAttrKey(elem, "orthographic", bool_map, 2, pcam->orthographic, def->Camera().orthographic);
|
||||
|
||||
// camera intrinsics if specified
|
||||
if (pcam->sensor_size[0]>0 && pcam->sensor_size[1]>0) {
|
||||
@@ -1012,15 +1010,16 @@ void mjXWriter::Visual(XMLElement* root) {
|
||||
|
||||
// global
|
||||
elem = InsertEnd(section, "global");
|
||||
WriteAttr(elem, "fovy", 1, &vis->global.fovy, &visdef.global.fovy);
|
||||
WriteAttr(elem, "ipd", 1, &vis->global.ipd, &visdef.global.ipd);
|
||||
WriteAttr(elem, "azimuth", 1, &vis->global.azimuth, &visdef.global.azimuth);
|
||||
WriteAttr(elem, "elevation", 1, &vis->global.elevation, &visdef.global.elevation);
|
||||
WriteAttr(elem, "linewidth", 1, &vis->global.linewidth, &visdef.global.linewidth);
|
||||
WriteAttr(elem, "glow", 1, &vis->global.glow, &visdef.global.glow);
|
||||
WriteAttr(elem, "realtime", 1, &vis->global.realtime, &visdef.global.realtime);
|
||||
WriteAttrInt(elem, "offwidth", vis->global.offwidth, visdef.global.offwidth);
|
||||
WriteAttrInt(elem, "offheight", vis->global.offheight, visdef.global.offheight);
|
||||
WriteAttrKey(elem, "orthographic", bool_map, 2, vis->global.orthographic, visdef.global.orthographic);
|
||||
WriteAttr(elem, "fovy", 1, &vis->global.fovy, &visdef.global.fovy);
|
||||
WriteAttr(elem, "ipd", 1, &vis->global.ipd, &visdef.global.ipd);
|
||||
WriteAttr(elem, "azimuth", 1, &vis->global.azimuth, &visdef.global.azimuth);
|
||||
WriteAttr(elem, "elevation", 1, &vis->global.elevation, &visdef.global.elevation);
|
||||
WriteAttr(elem, "linewidth", 1, &vis->global.linewidth, &visdef.global.linewidth);
|
||||
WriteAttr(elem, "glow", 1, &vis->global.glow, &visdef.global.glow);
|
||||
WriteAttr(elem, "realtime", 1, &vis->global.realtime, &visdef.global.realtime);
|
||||
WriteAttrInt(elem, "offwidth", vis->global.offwidth, visdef.global.offwidth);
|
||||
WriteAttrInt(elem, "offheight", vis->global.offheight, visdef.global.offheight);
|
||||
WriteAttrKey(elem, "ellipsoidinertia", bool_map, 2, vis->global.ellipsoidinertia, visdef.global.ellipsoidinertia);
|
||||
WriteAttrKey(elem, "bvactive", bool_map, 2, vis->global.bvactive, visdef.global.bvactive);
|
||||
if (!elem->FirstAttribute()) {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<!-- Test model for visual debugging of orthographic cameras. -->
|
||||
|
||||
<mujoco>
|
||||
<visual>
|
||||
<!-- free camera is orthorgaphic with a 5m vertical field-of-view -->
|
||||
<global orthographic="true" fovy="5"/>
|
||||
<scale camera="1"/>
|
||||
</visual>
|
||||
|
||||
<asset>
|
||||
<texture name="grid" type="2d" builtin="checker" width="512" height="512" rgb1=".8 .6 .4" rgb2=".2 .3 .4"/>
|
||||
<material name="grid" texture="grid" texrepeat="10 10" texuniform="false" reflectance=".2"/>
|
||||
</asset>
|
||||
|
||||
<default>
|
||||
<camera orthographic="true"/>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<light pos="0 0 3"/>
|
||||
<geom type="plane" size="10 10 .01" material="grid"/>
|
||||
|
||||
<body pos=".5 .5 .5">
|
||||
<freejoint/>
|
||||
<geom type="box" size=".5 .5 .5" pos="0 0 .5"/>
|
||||
</body>
|
||||
|
||||
<!-- box should pefectly fit within field-of-view -->
|
||||
<camera name="fovy = 1" pos=".5 .5 2" orthographic="true" fovy="1"/>
|
||||
|
||||
<!-- 2 floor squares should pefectly fit within field-of-view -->
|
||||
<camera name="fovy = 2" pos="0 0 2" fovy="2"/> <!-- inherits orthographic="true" from defaults -->
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
@@ -1249,6 +1249,36 @@ TEST_F(XMLReaderTest, InvalidSkinGroup) {
|
||||
EXPECT_THAT(
|
||||
error.data(),
|
||||
HasSubstr("skin group must be between 0 and 5\nElement 'skin', line 7"));
|
||||
}
|
||||
|
||||
TEST_F(XMLReaderTest, Orthographic) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<visual>
|
||||
<global orthographic="true" fovy="5"/>
|
||||
<map znear="0.01"/>
|
||||
</visual>
|
||||
|
||||
<default>
|
||||
<camera orthographic="true"/>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<camera name="fovy=1" pos=".5 .5 2" orthographic="true" fovy="1"/>
|
||||
<camera name="fovy=2" pos="0 0 2" fovy="2"/>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
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_fovy[0], 1);
|
||||
EXPECT_EQ(model->cam_fovy[1], 2);
|
||||
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
|
||||
@@ -1118,6 +1118,16 @@ TEST_F(XMLWriterTest, TrimsDefaults) {
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, DoesntSaveGlobal) {
|
||||
static constexpr char xml[] = "<mujoco/>";
|
||||
std::array<char, 1000> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, NotNull()) << error.data();
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("global")));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, InheritrangeSavedAsRange) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
|
||||
@@ -5013,6 +5013,7 @@ public unsafe struct mjOption_ {
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct global {
|
||||
public int orthographic;
|
||||
public float fovy;
|
||||
public float ipd;
|
||||
public float azimuth;
|
||||
@@ -5321,11 +5322,12 @@ public unsafe struct mjModel_ {
|
||||
public double* cam_poscom0;
|
||||
public double* cam_pos0;
|
||||
public double* cam_mat0;
|
||||
public int* cam_resolution;
|
||||
public int* cam_orthographic;
|
||||
public double* cam_fovy;
|
||||
public float* cam_intrinsic;
|
||||
public float* cam_sensorsize;
|
||||
public double* cam_ipd;
|
||||
public int* cam_resolution;
|
||||
public float* cam_sensorsize;
|
||||
public float* cam_intrinsic;
|
||||
public double* cam_user;
|
||||
public int* light_mode;
|
||||
public int* light_bodyid;
|
||||
@@ -5858,6 +5860,7 @@ public unsafe struct mjvCamera_ {
|
||||
public double distance;
|
||||
public double azimuth;
|
||||
public double elevation;
|
||||
public int orthographic;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
@@ -5871,6 +5874,7 @@ public unsafe struct mjvGLCamera_ {
|
||||
public float frustum_top;
|
||||
public float frustum_near;
|
||||
public float frustum_far;
|
||||
public int orthographic;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
@@ -6184,10 +6188,12 @@ public unsafe struct model {
|
||||
public int* site_group;
|
||||
public double* site_size;
|
||||
public float* site_rgba;
|
||||
public int* cam_orthographic;
|
||||
public double* cam_fovy;
|
||||
public double* cam_ipd;
|
||||
public float* cam_intrinsic;
|
||||
public int* cam_resolution;
|
||||
public float* cam_sensorsize;
|
||||
public float* cam_intrinsic;
|
||||
public byte* light_directional;
|
||||
public byte* light_castshadow;
|
||||
public float* light_bulbradius;
|
||||
|
||||
Reference in New Issue
Block a user