diff options
Diffstat (limited to '')
-rw-r--r-- | dev/MinGfx/src/unicam.h | 512 |
1 files changed, 256 insertions, 256 deletions
diff --git a/dev/MinGfx/src/unicam.h b/dev/MinGfx/src/unicam.h index 999232b..d01634a 100644 --- a/dev/MinGfx/src/unicam.h +++ b/dev/MinGfx/src/unicam.h @@ -1,256 +1,256 @@ -/* - This file is part of the MinGfx Project. - - Copyright (c) 2017,2018 Regents of the University of Minnesota. - All Rights Reserved. - - Original Author(s) of this File: - Dan Keefe, 2018, University of Minnesota - - Author(s) of Significant Updates/Modifications to the File: - ... - */ - -#ifndef SRC_UNICAM_H_ -#define SRC_UNICAM_H_ - -#include "quick_shapes.h" -#include "point2.h" -#include "point3.h" -#include "vector2.h" -#include "vector3.h" - - -namespace mingfx { - - -/** This implements a user interface for controlling the camera with the mouse. - It is a special interface inspired by the "Unicam" technique developed by - Zeleznik et al. - - The key feature is that this interface makes it possible to control camera pan, - dolly, and rotation with only a single mouse button. That is quite useful - because it leaves the other mouse buttons free for pointing, sketching, or - other interaction techniques. - - The only downside of this technique is that it can take some time to learn. In - order to enjoy it, you will need to read these brief instructions on how to Pan, - Dolly, Rotate, and Spin: - - - Pan: Click and drag horizontally with the mouse. Once you make an initial - horizontal movement you can than pan up and down as well, but the key to entering - pan mode is to start with a horizontal movement. - - - Dolly: Click and drag vertically with the mouse. The initial movement must - be vertical. If you click on some object in the scene, then the speed of dollying - is set so that the object will come all the up to the camera lens if you drag - the mouse to the bottom of the screen. - - - Rotate: Start with a quick click and release to set the center of rotation. - This is most useful if you click on some object in the scene. You will see a - black dot appear to mark the center of rotation. If you click on the background - then a center of rotation will be selected for you. It will be a point straight - ahead and at a depth 4.0 units away. The depth can be adjusted for your application - with set_default_depth(). Once your center of rotation is established, move - your mouse away a bit and then click and drag to do a trackball rotatation of - the scene around this point. Come to a stop before letting go of the mouse - button in order to avoid entering the spin state! - - - Spin: For some fun, try "throwing" the scene so that it continues to rotate - even after you let go. To do this, start a rotation and then let go of the - mouse button while your mouse is still moving. To stop spinning just click and - release the mouse once to "catch" the scene. - - - Example usage: -~~~ -// Create a global or member variable in your MyGraphicsApp class: -UniCam unicam_; - - -void MyGraphicsApp::OnLeftMouseDown(const Point2 &pos) { - Point2 mouse_xy = PixelsToNormalizedDeviceCoords(pos); - float mouse_z = ReadZValueAtPixel(pos); - unicam_.OnButtonDown(mouse_xy, mouse_z); -} - -void MyGraphicsApp::OnLeftMouseDrag(const Point2 &pos, const Vector2 &delta) { - Point2 mouse_xy = PixelsToNormalizedDeviceCoords(pos); - unicam_.OnDrag(mouse_xy); -} - -void MyGraphicsApp::OnLeftMouseUp(const Point2 &pos) { - Point2 mouse_xy = PixelsToNormalizedDeviceCoords(pos); - unicam_.OnButtonUp(mouse_xy); -} - -void MyGraphicsApp::InitOpenGL() { - projMatrix_ = Matrix4::perspective(30, aspect_ratio(), 1, 20); - unicam_.set_view_matrix(Matrix4::lookAt(Point3(2.5,1,2.5), Point3(0,0,0), Vector3(0,1,0));); -} - -void MyGraphicsApp::DrawOpenGL() { - // draw your scene using the view matrix from UniCam - Matrix4 proj_matrix = Matrix4::Perspective(60, aspect_ratio(), 0.001, 10);; - Matrix4 view_matrix = uniCam.view_matrix(); - Matrix4 model_matrix = Matrix4::RotateY(to_radians(45.0)); - quickShapes.DrawCube(model_matrix, view_matirx, proj_matrix, Color(1,1,1)); - - // tell unicam to draw itself (i.e., the small sphere that marks the center of - // rotation when in rotation mode) - unicam_.Draw(proj_matrix); -} -~~~ -*/ -class UniCam { -public: - - /// Creates a UniCam object with an initial view matrix = identity. - UniCam(); - - /// Creates a UniCam object with the supplied initial view matrix. - UniCam(const Matrix4 &initialViewMatrix); - - virtual ~UniCam(); - - - // To make the interaction work, the following set of functions need to be - // called from your GraphicsApp or whatever main application class you use - // to receive user input events and a draw callback. - - /// Attach this to whatever mouse button you wish, for example, call this - /// from within GraphicsApp::OnRightMouseDown(). If your mousePos is reported - /// in pixels, you will need to convert it to normalized device coordinates - /// before passing it on to this routine. The depth buffer value for the - /// pixel under the mouse is also needed. If you are using GraphicsApp, you - /// can access both of these as follows: - /// ~~~ - /// Point2 mouse_xy = PixelsToNormalizedDeviceCoords(mouse_in_pixels); - /// float mouse_z = ReadZValueAtPixel(mouse_in_pixels); - /// uniCam.OnButtonDown(mouse_xy, mouse_z); - /// ~~~ - void OnButtonDown(const Point2 &normalizedMousePos, float mouseZ); - - /// Attach this to the corresponding mouse move event, for example, call this - /// from within GraphicsApp::OnRightMouseDrag(). If your mousePos is reported - /// in pixels, you will need to convert it to normalized device coordinates - /// before passing it on to this routine. Within GraphicsApp, use: - /// ~~~ - /// Point2 mouse_xy = PixelsToNormalizedDeviceCoords(mouse_in_pixels); - /// uniCam.OnDrag(mouse_xy); - /// ~~~ - void OnDrag(const Point2 &normalizedMousePos); - - /// Attach this to the corresponding button up event, for example, call this - /// from within GraphicsApp::OnRightMouseUp(). If your mousePos is reported - /// in pixels, you will need to convert it to normalized device coordinates - /// before passing it on to this routine. Within GraphicsApp, use: - /// ~~~ - /// Point2 mouse_xy = PixelsToNormalizedDeviceCoords(mouse_in_pixels); - /// uniCam.OnButtonUp(mouse_xy); - /// ~~~ - void OnButtonUp(const Point2 &normalizedMousePos); - - /// Attach this to a callback that can be used to control animation. Within - /// GraphicsApp::UpdateSimulation(), use: - /// ~~~ - /// uniCam.AdvanceAnimation(dt); - /// ~~~ - void AdvanceAnimation(double dt); - - /// Finally, attach this to your draw callback routine. Within - /// GraphicsApp::DrawUsingOpenGL(), use: - /// ~~~ - /// uniCam.Draw(projMatrix); - /// ~~~ - void Draw(const Matrix4 &projectionMatrix); - - - /// Access the camera view matrix created by the UniCam interactions via - /// this method and use it to draw the geometry in your scence. - /// For example, within GraphicsApp::DrawUsingOpenGL(), you might have: - /// ~~~ - /// Matrix4 P = Matrix4::Perspective(30, aspect_ratio(), 1, 20); - /// Matrix4 V = unicam.view_matrix(); - /// Matrix4 M = Matrix4::RotateY(GfxMath::ToRadians(45.0)); - /// quick_shapes.DrawCube(M, V, P, Color(1,1,1)); - /// ~~~ - Matrix4 view_matrix(); - - - /// Returns the "eye" point (i.e., focal point) of the camera in world - /// space coordinates. - Point3 eye(); - - /// Returns the look direction (i.e., -Z axis of the camera matrix) in world - /// space coordinates. - Vector3 look(); - - - // ------------- - - /// This is not required, but you may use this if you wish to set an initial - /// view matrix or reset the view matrix - void set_view_matrix(Matrix4 viewMatrix); - - /// This sets the depth of the center of rotation for the case when the user's - /// click does not intersect any geometry. It defaults to 4 units, but the - /// right value to use depends very much on the current scene. For example, - /// you could set a very good value by calculating the current centroid of - /// your scene and the finding the depth of this point (the distance along - /// the look vector) relative to the camera. - void set_default_depth(float d); - - -private: - - void recalc_angular_vel(); - - enum class UniCamState { - START, - PAN_DOLLY_ROT_DECISION, - PAN_DOLLY_DECISION, - ROT_WAIT_FOR_SECOND_CLICK, - PAN, - DOLLY, - ROT, - SPINNING - }; - UniCamState state_; - - Point2 mouseLast_; - double elapsedTime_; - - Point2 initialClickPos_; - bool hitGeometry_; - Point3 hitPoint_; - - bool rotInitialized_; - Point3 rotLastIPoint_; - float boundingSphereRad_; - Point3 boundingSphereCtr_; - double rotLastTime_; - std::vector<std::pair<double, double>> rotAngularVelBuffer_; - double rotAngularVel_; - Vector3 rotAxis_; - - bool dollyInitialized_; - float dollyFactor_; - float defaultDepth_; - - bool showIcon_; - QuickShapes quickShapes_; - - Matrix4 V_; - Matrix4 Vstart_; - - // saved from the last draw call in order to unproject the mouse pos - Matrix4 Pdraw_; -}; - - -} // end namespace - -#endif - - +/*
+ This file is part of the MinGfx Project.
+
+ Copyright (c) 2017,2018 Regents of the University of Minnesota.
+ All Rights Reserved.
+
+ Original Author(s) of this File:
+ Dan Keefe, 2018, University of Minnesota
+
+ Author(s) of Significant Updates/Modifications to the File:
+ ...
+ */
+
+#ifndef SRC_UNICAM_H_
+#define SRC_UNICAM_H_
+
+#include "quick_shapes.h"
+#include "point2.h"
+#include "point3.h"
+#include "vector2.h"
+#include "vector3.h"
+
+
+namespace mingfx {
+
+
+/** This implements a user interface for controlling the camera with the mouse.
+ It is a special interface inspired by the "Unicam" technique developed by
+ Zeleznik et al.
+
+ The key feature is that this interface makes it possible to control camera pan,
+ dolly, and rotation with only a single mouse button. That is quite useful
+ because it leaves the other mouse buttons free for pointing, sketching, or
+ other interaction techniques.
+
+ The only downside of this technique is that it can take some time to learn. In
+ order to enjoy it, you will need to read these brief instructions on how to Pan,
+ Dolly, Rotate, and Spin:
+
+ - Pan: Click and drag horizontally with the mouse. Once you make an initial
+ horizontal movement you can than pan up and down as well, but the key to entering
+ pan mode is to start with a horizontal movement.
+
+ - Dolly: Click and drag vertically with the mouse. The initial movement must
+ be vertical. If you click on some object in the scene, then the speed of dollying
+ is set so that the object will come all the up to the camera lens if you drag
+ the mouse to the bottom of the screen.
+
+ - Rotate: Start with a quick click and release to set the center of rotation.
+ This is most useful if you click on some object in the scene. You will see a
+ black dot appear to mark the center of rotation. If you click on the background
+ then a center of rotation will be selected for you. It will be a point straight
+ ahead and at a depth 4.0 units away. The depth can be adjusted for your application
+ with set_default_depth(). Once your center of rotation is established, move
+ your mouse away a bit and then click and drag to do a trackball rotatation of
+ the scene around this point. Come to a stop before letting go of the mouse
+ button in order to avoid entering the spin state!
+
+ - Spin: For some fun, try "throwing" the scene so that it continues to rotate
+ even after you let go. To do this, start a rotation and then let go of the
+ mouse button while your mouse is still moving. To stop spinning just click and
+ release the mouse once to "catch" the scene.
+
+
+ Example usage:
+~~~
+// Create a global or member variable in your MyGraphicsApp class:
+UniCam unicam_;
+
+
+void MyGraphicsApp::OnLeftMouseDown(const Point2 &pos) {
+ Point2 mouse_xy = PixelsToNormalizedDeviceCoords(pos);
+ float mouse_z = ReadZValueAtPixel(pos);
+ unicam_.OnButtonDown(mouse_xy, mouse_z);
+}
+
+void MyGraphicsApp::OnLeftMouseDrag(const Point2 &pos, const Vector2 &delta) {
+ Point2 mouse_xy = PixelsToNormalizedDeviceCoords(pos);
+ unicam_.OnDrag(mouse_xy);
+}
+
+void MyGraphicsApp::OnLeftMouseUp(const Point2 &pos) {
+ Point2 mouse_xy = PixelsToNormalizedDeviceCoords(pos);
+ unicam_.OnButtonUp(mouse_xy);
+}
+
+void MyGraphicsApp::InitOpenGL() {
+ projMatrix_ = Matrix4::perspective(30, aspect_ratio(), 1, 20);
+ unicam_.set_view_matrix(Matrix4::lookAt(Point3(2.5,1,2.5), Point3(0,0,0), Vector3(0,1,0)););
+}
+
+void MyGraphicsApp::DrawOpenGL() {
+ // draw your scene using the view matrix from UniCam
+ Matrix4 proj_matrix = Matrix4::Perspective(60, aspect_ratio(), 0.001, 10);;
+ Matrix4 view_matrix = uniCam.view_matrix();
+ Matrix4 model_matrix = Matrix4::RotateY(to_radians(45.0));
+ quickShapes.DrawCube(model_matrix, view_matirx, proj_matrix, Color(1,1,1));
+
+ // tell unicam to draw itself (i.e., the small sphere that marks the center of
+ // rotation when in rotation mode)
+ unicam_.Draw(proj_matrix);
+}
+~~~
+*/
+class UniCam {
+public:
+
+ /// Creates a UniCam object with an initial view matrix = identity.
+ UniCam();
+
+ /// Creates a UniCam object with the supplied initial view matrix.
+ UniCam(const Matrix4 &initialViewMatrix);
+
+ virtual ~UniCam();
+
+
+ // To make the interaction work, the following set of functions need to be
+ // called from your GraphicsApp or whatever main application class you use
+ // to receive user input events and a draw callback.
+
+ /// Attach this to whatever mouse button you wish, for example, call this
+ /// from within GraphicsApp::OnRightMouseDown(). If your mousePos is reported
+ /// in pixels, you will need to convert it to normalized device coordinates
+ /// before passing it on to this routine. The depth buffer value for the
+ /// pixel under the mouse is also needed. If you are using GraphicsApp, you
+ /// can access both of these as follows:
+ /// ~~~
+ /// Point2 mouse_xy = PixelsToNormalizedDeviceCoords(mouse_in_pixels);
+ /// float mouse_z = ReadZValueAtPixel(mouse_in_pixels);
+ /// uniCam.OnButtonDown(mouse_xy, mouse_z);
+ /// ~~~
+ void OnButtonDown(const Point2 &normalizedMousePos, float mouseZ);
+
+ /// Attach this to the corresponding mouse move event, for example, call this
+ /// from within GraphicsApp::OnRightMouseDrag(). If your mousePos is reported
+ /// in pixels, you will need to convert it to normalized device coordinates
+ /// before passing it on to this routine. Within GraphicsApp, use:
+ /// ~~~
+ /// Point2 mouse_xy = PixelsToNormalizedDeviceCoords(mouse_in_pixels);
+ /// uniCam.OnDrag(mouse_xy);
+ /// ~~~
+ void OnDrag(const Point2 &normalizedMousePos);
+
+ /// Attach this to the corresponding button up event, for example, call this
+ /// from within GraphicsApp::OnRightMouseUp(). If your mousePos is reported
+ /// in pixels, you will need to convert it to normalized device coordinates
+ /// before passing it on to this routine. Within GraphicsApp, use:
+ /// ~~~
+ /// Point2 mouse_xy = PixelsToNormalizedDeviceCoords(mouse_in_pixels);
+ /// uniCam.OnButtonUp(mouse_xy);
+ /// ~~~
+ void OnButtonUp(const Point2 &normalizedMousePos);
+
+ /// Attach this to a callback that can be used to control animation. Within
+ /// GraphicsApp::UpdateSimulation(), use:
+ /// ~~~
+ /// uniCam.AdvanceAnimation(dt);
+ /// ~~~
+ void AdvanceAnimation(double dt);
+
+ /// Finally, attach this to your draw callback routine. Within
+ /// GraphicsApp::DrawUsingOpenGL(), use:
+ /// ~~~
+ /// uniCam.Draw(projMatrix);
+ /// ~~~
+ void Draw(const Matrix4 &projectionMatrix);
+
+
+ /// Access the camera view matrix created by the UniCam interactions via
+ /// this method and use it to draw the geometry in your scence.
+ /// For example, within GraphicsApp::DrawUsingOpenGL(), you might have:
+ /// ~~~
+ /// Matrix4 P = Matrix4::Perspective(30, aspect_ratio(), 1, 20);
+ /// Matrix4 V = unicam.view_matrix();
+ /// Matrix4 M = Matrix4::RotateY(GfxMath::ToRadians(45.0));
+ /// quick_shapes.DrawCube(M, V, P, Color(1,1,1));
+ /// ~~~
+ Matrix4 view_matrix();
+
+
+ /// Returns the "eye" point (i.e., focal point) of the camera in world
+ /// space coordinates.
+ Point3 eye();
+
+ /// Returns the look direction (i.e., -Z axis of the camera matrix) in world
+ /// space coordinates.
+ Vector3 look();
+
+
+ // -------------
+
+ /// This is not required, but you may use this if you wish to set an initial
+ /// view matrix or reset the view matrix
+ void set_view_matrix(Matrix4 viewMatrix);
+
+ /// This sets the depth of the center of rotation for the case when the user's
+ /// click does not intersect any geometry. It defaults to 4 units, but the
+ /// right value to use depends very much on the current scene. For example,
+ /// you could set a very good value by calculating the current centroid of
+ /// your scene and the finding the depth of this point (the distance along
+ /// the look vector) relative to the camera.
+ void set_default_depth(float d);
+
+
+private:
+
+ void recalc_angular_vel();
+
+ enum class UniCamState {
+ START,
+ PAN_DOLLY_ROT_DECISION,
+ PAN_DOLLY_DECISION,
+ ROT_WAIT_FOR_SECOND_CLICK,
+ PAN,
+ DOLLY,
+ ROT,
+ SPINNING
+ };
+ UniCamState state_;
+
+ Point2 mouseLast_;
+ double elapsedTime_;
+
+ Point2 initialClickPos_;
+ bool hitGeometry_;
+ Point3 hitPoint_;
+
+ bool rotInitialized_;
+ Point3 rotLastIPoint_;
+ float boundingSphereRad_;
+ Point3 boundingSphereCtr_;
+ double rotLastTime_;
+ std::vector<std::pair<double, double>> rotAngularVelBuffer_;
+ double rotAngularVel_;
+ Vector3 rotAxis_;
+
+ bool dollyInitialized_;
+ float dollyFactor_;
+ float defaultDepth_;
+
+ bool showIcon_;
+ QuickShapes quickShapes_;
+
+ Matrix4 V_;
+ Matrix4 Vstart_;
+
+ // saved from the last draw call in order to unproject the mouse pos
+ Matrix4 Pdraw_;
+};
+
+
+} // end namespace
+
+#endif
+
+
|