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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user