diff --git a/doc/includes/references.h b/doc/includes/references.h index 90914e75..5d16766c 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -2676,7 +2676,9 @@ typedef enum mjtMouse_ { // mouse interaction mode mjMOUSE_ROTATE_H, // rotate, horizontal plane mjMOUSE_MOVE_V, // move, vertical plane mjMOUSE_MOVE_H, // move, horizontal plane - mjMOUSE_ZOOM // zoom + mjMOUSE_ZOOM, // zoom + mjMOUSE_MOVE_V_REL, // move, vertical plane, relative to target + mjMOUSE_MOVE_H_REL, // move, horizontal plane, relative to target } mjtMouse; typedef enum mjtPertBit_ { // mouse perturbations mjPERT_TRANSLATE = 1, // translation diff --git a/include/mujoco/mjvisualize.h b/include/mujoco/mjvisualize.h index 2198278f..d7c04523 100644 --- a/include/mujoco/mjvisualize.h +++ b/include/mujoco/mjvisualize.h @@ -44,7 +44,9 @@ typedef enum mjtMouse_ { // mouse interaction mode mjMOUSE_ROTATE_H, // rotate, horizontal plane mjMOUSE_MOVE_V, // move, vertical plane mjMOUSE_MOVE_H, // move, horizontal plane - mjMOUSE_ZOOM // zoom + mjMOUSE_ZOOM, // zoom + mjMOUSE_MOVE_V_REL, // move, vertical plane, relative to target + mjMOUSE_MOVE_H_REL, // move, horizontal plane, relative to target } mjtMouse; diff --git a/python/mujoco/introspect/enums.py b/python/mujoco/introspect/enums.py index 5f0eba5c..db3b8d70 100644 --- a/python/mujoco/introspect/enums.py +++ b/python/mujoco/introspect/enums.py @@ -575,6 +575,8 @@ ENUMS: Mapping[str, EnumDecl] = dict([ ('mjMOUSE_MOVE_V', 3), ('mjMOUSE_MOVE_H', 4), ('mjMOUSE_ZOOM', 5), + ('mjMOUSE_MOVE_V_REL', 6), + ('mjMOUSE_MOVE_H_REL', 7), ]), )), ('mjtPertBit', diff --git a/src/engine/engine_vis_interact.c b/src/engine/engine_vis_interact.c index 96ca6dc4..3133ba81 100644 --- a/src/engine/engine_vis_interact.c +++ b/src/engine/engine_vis_interact.c @@ -294,12 +294,14 @@ static void convert2D(mjtNum* res, int action, mjtNum dx, mjtNum dy, const mjtNu break; case mjMOUSE_MOVE_V: + case mjMOUSE_MOVE_V_REL: vec[0] = dx; vec[1] = 0; vec[2] = -dy; break; case mjMOUSE_MOVE_H: + case mjMOUSE_MOVE_H_REL: vec[0] = dx; vec[1] = -dy; vec[2] = 0; @@ -321,7 +323,7 @@ static void convert2D(mjtNum* res, int action, mjtNum dx, mjtNum dy, const mjtNu // move camera with mouse; action is mjtMouse void mjv_moveCamera(const mjModel* m, int action, mjtNum reldx, mjtNum reldy, const mjvScene* scn, mjvCamera* cam) { - mjtNum headpos[3], forward[3]; + mjtNum headpos[3], forward[3], up[3], right[3]; mjtNum vec[3], dif[3], scl; // fixed camera: nothing to do @@ -363,6 +365,25 @@ void mjv_moveCamera(const mjModel* m, int action, mjtNum reldx, mjtNum reldy, cam->distance -= mju_log(1 + cam->distance/m->stat.extent/3) * reldy * 9 * m->stat.extent; break; + case mjMOUSE_MOVE_V_REL: + case mjMOUSE_MOVE_H_REL: + // do not move lookat point of tracking camera + if (cam->type == mjCAMERA_TRACKING) { + return; + } + + mjv_cameraInModel(headpos, forward, up, scn); + mju_cross(right, forward, up); + + // y-axis movement moves forward/backward (ie. camera dolly) on horizontal plane or up/down + // (ie. camera pedestal) on vertical plane + mju_addToScl3(cam->lookat, (action == mjMOUSE_MOVE_V_REL) ? up : forward, reldy); + + // x-axis movement strafes left/right (ie. camera truck) + mju_addToScl3(cam->lookat, right, reldx); + + break; + default: mjERROR("unexpected action %d", action); } @@ -393,7 +414,12 @@ void mjv_moveCamera(const mjModel* m, int action, mjtNum reldx, mjtNum reldy, // move perturb object with mouse; action is mjtMouse void mjv_movePerturb(const mjModel* m, const mjData* d, int action, mjtNum reldx, mjtNum reldy, const mjvScene* scn, mjvPerturb* pert) { + const mjtNum xaxis[3] = {1, 0, 0}; + const mjtNum yaxis[3] = {0, 1, 0}; + const mjtNum zaxis[3] = {0, 0, 1}; + int sel = pert->select; + const mjtNum* xmat = d->xmat+9*sel; mjtNum forward[3], vec[3], scl, q1[4], xiquat[4]; // get camera info and align @@ -404,10 +430,29 @@ void mjv_movePerturb(const mjModel* m, const mjData* d, int action, mjtNum reldx switch ((mjtMouse) action) { case mjMOUSE_MOVE_V: case mjMOUSE_MOVE_H: + // move along world-space horizontal/vertical planes relative to camera mju_addToScl3(pert->refpos, vec, pert->scale); mju_addToScl3(pert->refselpos, vec, pert->scale); break; + case mjMOUSE_MOVE_V_REL: + case mjMOUSE_MOVE_H_REL: + // move along object's local coordinate frame + if (action == mjMOUSE_MOVE_H_REL) { + mju_mulMatVec3(vec, xmat, xaxis); + mju_addToScl3(pert->refpos, vec, pert->scale * reldy); + mju_addToScl3(pert->refselpos, vec, pert->scale * reldy); + } else { + mju_mulMatVec3(vec, xmat, zaxis); + mju_addToScl3(pert->refpos, vec, pert->scale * reldy); + mju_addToScl3(pert->refselpos, vec, pert->scale * reldy); + } + + mju_mulMatVec3(vec, xmat, yaxis); + mju_addToScl3(pert->refpos, vec, pert->scale * reldx); + mju_addToScl3(pert->refselpos, vec, pert->scale * reldx); + break; + case mjMOUSE_ROTATE_V: case mjMOUSE_ROTATE_H: // normalize vector, get length @@ -506,12 +551,14 @@ void mjv_moveModel(const mjModel* m, int action, mjtNum reldx, mjtNum reldy, break; case mjMOUSE_MOVE_V: + case mjMOUSE_MOVE_V_REL: for (int i=0; i < 3; i++) { scn->translate[i] += (float)(roomright[i]*reldx - roomup[i]*reldy) * m->stat.extent; } break; case mjMOUSE_MOVE_H: + case mjMOUSE_MOVE_H_REL: for (int i=0; i < 3; i++) { scn->translate[i] += (float)(roomright[i]*reldx - roomforward[i]*reldy) * m->stat.extent; } diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index 15846de8..16310dbc 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -582,6 +582,8 @@ public enum mjtMouse : int{ mjMOUSE_MOVE_V = 3, mjMOUSE_MOVE_H = 4, mjMOUSE_ZOOM = 5, + mjMOUSE_MOVE_V_REL = 6, + mjMOUSE_MOVE_H_REL = 7, } public enum mjtPertBit : int{ mjPERT_TRANSLATE = 1,