diff --git a/examples/camera/example.cpp b/examples/camera/example.cpp index e09703d8e..96dba8ac4 100644 --- a/examples/camera/example.cpp +++ b/examples/camera/example.cpp @@ -20,6 +20,7 @@ #include #include #include +#include class Camera { @@ -115,13 +116,55 @@ class Camera void push_away(const double s) { using namespace Eigen; + using namespace igl; #ifndef NDEBUG - Vector3d old_at = camera.at(); + Vector3d old_at = at(); #endif const double old_at_dist = m_at_dist; m_at_dist = old_at_dist * s; dolly(Vector3d(0,0,1)*(m_at_dist - old_at_dist)); - assert((old_at-camera.at()).squaredNorm() < DOUBLE_EPS); + assert((old_at-at()).squaredNorm() < DOUBLE_EPS); + } + + // Turn around eye so that rotation is now q + // + // Inputs: + // q new rotation as quaternion + void turn_eye(const Eigen::Quaterniond & q) + { + using namespace Eigen; + using namespace igl; + Vector3d old_eye = eye(); + // eye should be fixed + // + // eye_1 = R_1 * t_1 = eye_0 + // t_1 = R_1' * eye_0 + m_rotation = q; + m_translation = m_rotation.conjugate() * old_eye; + assert((old_eye - eye()).squaredNorm() < DOUBLE_EPS); + } + + // Orbit around at so that rotation is now q + // + // Inputs: + // q new rotation as quaternion + void orbit(const Eigen::Quaterniond & q) + { + using namespace Eigen; + using namespace igl; +#ifndef NDEBUG + Vector3d old_at = at(); +#endif + // at should be fixed + // + // at_1 = R_1 * t_1 - R_1 * z = at_0 + // t_1 = R_1' * (at_0 + R_1 * z) + m_rotation = q; + m_translation = + m_rotation.conjugate() * + (old_at + + m_rotation * Vector3d(0,0,1) * m_at_dist); + assert((old_at - at()).squaredNorm() < DOUBLE_EPS); } // Aka "Hitchcock", "Vertigo", "Spielberg" or "Trombone" zoom: @@ -133,9 +176,10 @@ class Camera void dolly_zoom(const double da) { using namespace std; + using namespace igl; using namespace Eigen; #ifndef NDEBUG - Vector3d old_at = camera.at(); + Vector3d old_at = at(); #endif const double old_angle = m_angle; m_angle += da; @@ -146,7 +190,7 @@ class Camera const double old_at_dist = m_at_dist; m_at_dist = old_at_dist * s; dolly(Vector3d(0,0,1)*(m_at_dist - old_at_dist)); - assert((old_at-camera.at()).squaredNorm() < DOUBLE_EPS); + assert((old_at-at()).squaredNorm() < DOUBLE_EPS); } // Return top right corner of unit plane in relative coordinates, that is @@ -558,7 +602,9 @@ void mouse_wheel(int wheel, int direction, int mouse_x, int mouse_y) const double delta_y = 0.125*direction; mouse_scroll_y += delta_y; TwMouseWheel(mouse_scroll_y); + return; } + auto & camera = s.cameras[s.viewing_camera]; switch(center_type) { @@ -571,7 +617,6 @@ void mouse_wheel(int wheel, int direction, int mouse_x, int mouse_y) //camera.m_angle *= s; //camera.m_angle = min(max(camera.m_angle,1),89); camera.push_away(s); - // Rotation should stay around at }else { // Dolly zoom: @@ -581,8 +626,7 @@ void mouse_wheel(int wheel, int direction, int mouse_x, int mouse_y) default: case CENTER_TYPE_FPS: // Move `eye` and `at` - Vector3d diff = (wheel==0?Vector3d(0,0,1):Vector3d(-1,0,0))*0.1; - camera.dolly(diff*direction); + camera.dolly((wheel==0?Vector3d(0,0,1):Vector3d(-1,0,0))*0.1*direction); break; } } @@ -665,56 +709,35 @@ void mouse_drag(int mouse_x, int mouse_y) { // Rotate according to trackball igl::trackball( - width, - height, + width, height, 2.0, down_camera.m_rotation.conjugate(), - down_x, - down_y, - mouse_x, - mouse_y, + down_x, down_y, mouse_x, mouse_y, q); break; } case ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP: { + // Rotate according to two axis valuator with fixed up vector two_axis_valuator_fixed_up( - width, - height, + width, height, 2.0, down_camera.m_rotation.conjugate(), - down_x, - down_y, - mouse_x, - mouse_y, + down_x, down_y, mouse_x, mouse_y, q); break; } default: break; } - camera.m_rotation = q.conjugate(); switch(center_type) { default: case CENTER_TYPE_ORBIT: - // at should be fixed - // - // at_1 = R_1 * t_1 - R_1 * z = at_0 - // t_1 = R_1' * (at_0 + R_1 * z) - camera.m_translation = - camera.m_rotation.conjugate() * - (down_camera.at() + - camera.m_rotation * Vector3d(0,0,1) * camera.m_at_dist); - assert((down_camera.at() - camera.at()).squaredNorm() < DOUBLE_EPS); + camera.orbit(q.conjugate()); break; case CENTER_TYPE_FPS: - // eye should be fixed - // - // eye_1 = R_1 * t_1 = eye_0 - // t_1 = R_1' * eye_0 - camera.m_translation = camera.m_rotation.conjugate() * down_camera.eye(); - assert((down_camera.eye() - camera.eye()).squaredNorm() < DOUBLE_EPS); + camera.turn_eye(q.conjugate()); break; } } @@ -754,6 +777,36 @@ void key(unsigned char key, int mouse_x, int mouse_y) } +void TW_CALL set_rotation(const void * value, void * clientData) +{ + using namespace std; + using namespace Eigen; + const double * rt = (const double*)(value); + Quaterniond conj; + copy(rt,rt+4,conj.coeffs().data()); + auto & camera = s.cameras[s.viewing_camera]; + switch(center_type) + { + default: + case CENTER_TYPE_ORBIT: + camera.orbit(conj.conjugate()); + break; + case CENTER_TYPE_FPS: + camera.turn_eye(conj.conjugate()); + break; + } +} + +void TW_CALL get_rotation(void * value, void *clientData) +{ + using namespace std; + using namespace Eigen; + const auto & camera = s.cameras[s.viewing_camera]; + double * rt = (double*)(value); + Quaterniond conj = camera.m_rotation.conjugate(); + copy(conj.coeffs().data(),conj.coeffs().data()+4,rt); +} + int main(int argc, char * argv[]) { using namespace std; @@ -780,6 +833,7 @@ int main(int argc, char * argv[]) rebar.TwAddVarRW("rotation_type", RotationTypeTW,&rotation_type, "keyIncr=] keyDecr=["); TwType CenterTypeTW = ReTwDefineEnumFromString("CenterType","orbit,fps"); + rebar.TwAddVarCB("rotation", TW_TYPE_QUAT4D,set_rotation,get_rotation,NULL,""); rebar.TwAddVarRW("center_type", CenterTypeTW,¢er_type, "keyIncr={ keyDecr=}"); rebar.load(REBAR_NAME); diff --git a/include/igl/snap_to_fixed_up.cpp b/include/igl/snap_to_fixed_up.cpp new file mode 100644 index 000000000..1b8a9b84e --- /dev/null +++ b/include/igl/snap_to_fixed_up.cpp @@ -0,0 +1,25 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "snap_to_fixed_up.h" + +IGL_INLINE void igl::snap_to_fixed_up( + const Eigen::Quaterniond & q, + Eigen::Quaterniond & s) +{ + using namespace Eigen; + const Vector3d up = q.matrix() * Vector3d(0,1,0); + Vector3d proj_up(0,up(1),up(2)); + if(proj_up.norm() == 0) + { + proj_up = Vector3d(0,1,0); + } + proj_up.normalize(); + Quaterniond dq; + dq = Quaterniond::FromTwoVectors(up,proj_up); + s = dq * q; +} diff --git a/include/igl/snap_to_fixed_up.h b/include/igl/snap_to_fixed_up.h new file mode 100644 index 000000000..aa0193744 --- /dev/null +++ b/include/igl/snap_to_fixed_up.h @@ -0,0 +1,38 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_SNAP_TO_FIXED_UP_H +#define IGL_SNAP_TO_FIXED_UP_H + +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Snap an arbitrary rotation to a rotation resulting from a rotation about + // the y-axis then the x-axis (maintaining fixed up like + // two_axis_valuator_fixed_up.) + // + // Inputs: + // q General rotation as quaternion + // Outputs: + // s the resulting rotation (as quaternion) + // + // See also: two_axis_valuator_fixed_up + IGL_INLINE void snap_to_fixed_up( + const Eigen::Quaterniond & q, + Eigen::Quaterniond & s); +} + +#ifdef IGL_HEADER_ONLY +# include "snap_to_fixed_up.cpp" +#endif + +#endif + + diff --git a/include/igl/two_axis_valuator_fixed_up.cpp b/include/igl/two_axis_valuator_fixed_up.cpp new file mode 100644 index 000000000..d4d12319e --- /dev/null +++ b/include/igl/two_axis_valuator_fixed_up.cpp @@ -0,0 +1,43 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#include "two_axis_valuator_fixed_up.h" +#include "PI.h" + +IGL_INLINE void igl::two_axis_valuator_fixed_up( + const int w, + const int h, + const double speed, + const Eigen::Quaterniond & down_quat, + const int down_x, + const int down_y, + const int mouse_x, + const int mouse_y, + Eigen::Quaterniond & quat) +{ + using namespace Eigen; + Vector3d axis(0,1,0); + quat = down_quat * + Quaterniond( + AngleAxisd( + PI*((double)(mouse_x-down_x))/(double)w*speed/2.0, + axis.normalized())); + quat.normalize(); + { + Vector3d axis(1,0,0); + if(axis.norm() != 0) + { + quat = + Quaterniond( + AngleAxisd( + PI*(mouse_y-down_y)/(double)h*speed/2.0, + axis.normalized())) * quat; + quat.normalize(); + } + } +} + diff --git a/include/igl/two_axis_valuator_fixed_up.h b/include/igl/two_axis_valuator_fixed_up.h new file mode 100644 index 000000000..95ea66b69 --- /dev/null +++ b/include/igl/two_axis_valuator_fixed_up.h @@ -0,0 +1,50 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at http://mozilla.org/MPL/2.0/. +#ifndef IGL_TWO_AXIS_VALUATOR_FIXED_AXIS_UP_H +#define IGL_TWO_AXIS_VALUATOR_FIXED_AXIS_UP_H + +#include "igl_inline.h" +#include +#include + +namespace igl +{ + // Applies a two-axis valuator drag rotation (as seen in Maya/Studio max) to a given rotation. + // Inputs: + // w width of the trackball context + // h height of the trackball context + // speed controls how fast the trackball feels, 1 is normal + // down_quat rotation at mouse down, i.e. the rotation we're applying the + // trackball motion to (as quaternion). **Note:** Up-vector that is fixed + // is with respect to this rotation. + // down_x position of mouse down + // down_y position of mouse down + // mouse_x current x position of mouse + // mouse_y current y position of mouse + // Outputs: + // quat the resulting rotation (as quaternion) + // + // See also: snap_to_fixed_up + IGL_INLINE void two_axis_valuator_fixed_up( + const int w, + const int h, + const double speed, + const Eigen::Quaterniond & down_quat, + const int down_x, + const int down_y, + const int mouse_x, + const int mouse_y, + Eigen::Quaterniond & quat); +} + +#ifdef IGL_HEADER_ONLY +# include "two_axis_valuator_fixed_up.cpp" +#endif + +#endif +