summaryrefslogtreecommitdiffstats
path: root/dev/MinGfx/src/unicam.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dev/MinGfx/src/unicam.h256
1 files changed, 256 insertions, 0 deletions
diff --git a/dev/MinGfx/src/unicam.h b/dev/MinGfx/src/unicam.h
new file mode 100644
index 0000000..999232b
--- /dev/null
+++ b/dev/MinGfx/src/unicam.h
@@ -0,0 +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
+
+