summaryrefslogtreecommitdiffstats
path: root/dev/MinGfx/src/unicam.cc
diff options
context:
space:
mode:
authorMatt Strapp <matt@mattstrapp.net>2021-09-20 18:15:14 -0500
committerMatt Strapp <matt@mattstrapp.net>2021-09-20 18:15:14 -0500
commit342403a02f8063903d0f38327430721d4d0ae331 (patch)
tree29d020a27bc16939c568dd4b29166566d1c0e658 /dev/MinGfx/src/unicam.cc
parentFix parenthesis (diff)
downloadcsci4611-5a8b6d8538618b144e39618350ad06a4d9e232fa.tar
csci4611-5a8b6d8538618b144e39618350ad06a4d9e232fa.tar.gz
csci4611-5a8b6d8538618b144e39618350ad06a4d9e232fa.tar.bz2
csci4611-5a8b6d8538618b144e39618350ad06a4d9e232fa.tar.lz
csci4611-5a8b6d8538618b144e39618350ad06a4d9e232fa.tar.xz
csci4611-5a8b6d8538618b144e39618350ad06a4d9e232fa.tar.zst
csci4611-5a8b6d8538618b144e39618350ad06a4d9e232fa.zip
Diffstat (limited to 'dev/MinGfx/src/unicam.cc')
-rw-r--r--dev/MinGfx/src/unicam.cc634
1 files changed, 317 insertions, 317 deletions
diff --git a/dev/MinGfx/src/unicam.cc b/dev/MinGfx/src/unicam.cc
index 0acb9c9..7b2fad4 100644
--- a/dev/MinGfx/src/unicam.cc
+++ b/dev/MinGfx/src/unicam.cc
@@ -1,317 +1,317 @@
-/*
- Copyright (c) 2017,2018 Regents of the University of Minnesota.
- All Rights Reserved.
- See corresponding header file for details.
- */
-
-#include "unicam.h"
-
-#include "gfxmath.h"
-
-namespace mingfx {
-
-
-UniCam::UniCam() : state_(UniCamState::START), defaultDepth_(4.0), boundingSphereRad_(1.0),
- dollyFactor_(1.0), dollyInitialized_(false), elapsedTime_(0.0), hitGeometry_(false),
- rotAngularVel_(0.0), rotInitialized_(false), rotLastTime_(0.0), showIcon_(false)
-{
-}
-
-UniCam::UniCam(const Matrix4 &initialViewMatrix) :
- state_(UniCamState::START), defaultDepth_(4.0), V_(initialViewMatrix), boundingSphereRad_(1.0),
- dollyFactor_(1.0), dollyInitialized_(false), elapsedTime_(0.0), hitGeometry_(false),
- rotAngularVel_(0.0), rotInitialized_(false), rotLastTime_(0.0), showIcon_(false)
-{
-}
-
-UniCam::~UniCam()
-{
-}
-
-
-void UniCam::recalc_angular_vel() {
- // update angular velocity
- float cutoff = (float)elapsedTime_ - 0.2f; // look just at the last 0.2 secs
- while ((rotAngularVelBuffer_.size()) && (rotAngularVelBuffer_[0].first < cutoff)) {
- rotAngularVelBuffer_.erase(rotAngularVelBuffer_.begin());
- }
- rotAngularVel_ = 0.0;
- if (rotAngularVelBuffer_.size()) {
- for (int i=0; i<rotAngularVelBuffer_.size(); i++) {
- rotAngularVel_ += rotAngularVelBuffer_[i].second;
- }
- rotAngularVel_ /= rotAngularVelBuffer_.size();
- }
- //std::cout << rotAngularVelBuffer_.size() << " " << rotAngularVel_ << std::endl;
-}
-
-
-void UniCam::OnButtonDown(const Point2 &mousePos, float mouseZ) {
- if (state_ == UniCamState::START) {
- initialClickPos_ = mousePos;
- mouseLast_ = mousePos;
- elapsedTime_ = 0.0;
- rotInitialized_ = false;
- dollyInitialized_ = false;
-
- hitGeometry_ = (mouseZ < 1.0);
- if (hitGeometry_) {
- hitPoint_ = GfxMath::ScreenToWorld(V_, Pdraw_, mousePos, mouseZ);
- }
- else {
- hitPoint_ = GfxMath::ScreenToDepthPlane(V_, Pdraw_, Point2(0,0), defaultDepth_);
- }
- showIcon_ = true;
- state_ = UniCamState::PAN_DOLLY_ROT_DECISION;
- }
- else if (state_ == UniCamState::ROT_WAIT_FOR_SECOND_CLICK) {
- // we have the second click now, and we will start the trackball rotate interaction
- state_ = UniCamState::ROT;
- }
- else if (state_ == UniCamState::SPINNING) {
- // this click is to "catch" the model, stopping it from spinning.
- state_ = UniCamState::START;
- }
- else {
- std::cerr << "UniCam::OnButtonDown() unexpected state." << std::endl;
- }
-}
-
-void UniCam::OnDrag(const Point2 &mousePos) {
- if (state_ == UniCamState::PAN_DOLLY_ROT_DECISION) {
- const double panMovementThreshold = 0.01;
- const double dollyMovementThreshold = 0.01;
- if (fabs(mousePos[0] - initialClickPos_[0]) > panMovementThreshold) {
- // already lots of horizontal movement, we can go right to pan
- state_ = UniCamState::PAN;
- showIcon_ = false;
- }
- else if (fabs(mousePos[1] - initialClickPos_[1]) > dollyMovementThreshold) {
- // already lots of vertical movement, we can go right to dolly
- state_ = UniCamState::DOLLY;
- showIcon_ = false;
- }
- else if (elapsedTime_ > 1.0) {
- // timeout, this was not a quick click to set a center of rotation,
- // so there is no intent to rotate. instead we will be doing either
- // pan or dolly.
- state_ = UniCamState::PAN_DOLLY_DECISION;
- showIcon_ = false;
- }
- }
- else if (state_ == UniCamState::PAN_DOLLY_DECISION) {
- const double panMovementThreshold = 0.01;
- const double dollyMovementThreshold = 0.01;
- if (fabs(mousePos[0] - initialClickPos_[0]) > panMovementThreshold) {
- // lots of horizontal movement, go to pan
- state_ = UniCamState::PAN;
- }
- else if (fabs(mousePos[1] - initialClickPos_[1]) > dollyMovementThreshold) {
- // lots of vertical movement, go to dolly
- state_ = UniCamState::DOLLY;
- }
- }
- else if (state_ == UniCamState::PAN) {
- Matrix4 camMat = V_.Inverse();
- Point3 eye = camMat.ColumnToPoint3(3);
- Vector3 look = -camMat.ColumnToVector3(2);
- float depth = (hitPoint_ - eye).Dot(look);
- Point3 pWorld1 = GfxMath::ScreenToDepthPlane(V_, Pdraw_, mouseLast_, depth);
- Point3 pWorld2 = GfxMath::ScreenToDepthPlane(V_, Pdraw_, mousePos, depth);
- V_ = V_ * Matrix4::Translation(pWorld2 - pWorld1);
- }
- else if (state_ == UniCamState::DOLLY) {
- if (!dollyInitialized_) {
- // Setup dollyFactor so that if you move the mouse to the bottom of the screen, the point
- // you clicked on will be right on top of the camera.
- Matrix4 camMat = V_.Inverse();
- Point3 eye = camMat.ColumnToPoint3(3);
- Vector3 look = -camMat.ColumnToVector3(2);
- float depth = (hitPoint_ - eye).Dot(look);
- float deltaYToBottom = initialClickPos_[1] + 1;
- dollyFactor_ = depth / deltaYToBottom;
- dollyInitialized_ = true;
- }
- Vector3 d(0, 0, -dollyFactor_ * (mousePos[1] - mouseLast_[1]));
- V_ = Matrix4::Translation(d) * V_ ;
- }
- else if (state_ == UniCamState::ROT) {
- if (!rotInitialized_) {
- float depth = 0.0;
- if (hitGeometry_) {
- // if we hit some geometry, then make that the center of rotation
- boundingSphereCtr_ = hitPoint_;
- Matrix4 camMat = V_.Inverse();
- Point3 eye = camMat.ColumnToPoint3(3);
- Vector3 look = -camMat.ColumnToVector3(2);
- depth = (hitPoint_ - eye).Dot(look);
- }
- else {
- // if we did not hit any geometry, then center the bounding sphere in front of
- // the camera at a distance that can be configured by the user.
- boundingSphereCtr_ = GfxMath::ScreenToDepthPlane(V_, Pdraw_, Point2(0,0), defaultDepth_);
- depth = defaultDepth_;
- }
-
- // determine the size of the bounding sphere by projecting a screen-space
- // distance of 0.75 units to the depth of the sphere center
- Point3 pWorld1 = GfxMath::ScreenToDepthPlane(V_, Pdraw_, Point2(0,0), depth);
- Point3 pWorld2 = GfxMath::ScreenToDepthPlane(V_, Pdraw_, Point2(0.75,0), depth);
- boundingSphereRad_ = (pWorld2-pWorld1).Length();
-
- rotLastTime_ = elapsedTime_;
- rotAngularVelBuffer_.clear();
- rotInitialized_ = true;
- }
- else {
- // Do a trackball rotation based on the mouse movement and the bounding sphere
- // setup earlier.
-
- Matrix4 camMat = V_.Inverse();
- Point3 eye = camMat.ColumnToPoint3(3);
-
- // last mouse pos
- bool hit1 = false;
- Point3 mouse3D1 = GfxMath::ScreenToNearPlane(V_, Pdraw_, mouseLast_);
- Ray ray1(eye, mouse3D1 - eye);
- float t1;
- Point3 iPoint1;
- if (ray1.IntersectSphere(boundingSphereCtr_, boundingSphereRad_, &t1, &iPoint1)) {
- hit1 = true;
- }
-
- // current mouse pos
- bool hit2 = false;
- Point3 mouse3D2 = GfxMath::ScreenToNearPlane(V_, Pdraw_, mousePos);
- Ray ray2(eye, mouse3D2 - eye);
- float t2;
- Point3 iPoint2;
- if (ray2.IntersectSphere(boundingSphereCtr_, boundingSphereRad_, &t2, &iPoint2)) {
- hit2 = true;
- }
- rotLastIPoint_ = iPoint2;
-
- if (hit1 && hit2) {
- Vector3 v1 = (iPoint1 - boundingSphereCtr_).ToUnit();
- Vector3 v2 = (iPoint2 - boundingSphereCtr_).ToUnit();
-
- rotAxis_ = v1.Cross(v2).ToUnit();
- float angle = std::acos(v1.Dot(v2));
-
- if (std::isfinite(angle)) {
- Matrix4 R = Matrix4::Rotation(boundingSphereCtr_, rotAxis_, angle);
- R = R.Orthonormal();
- V_ = V_ * R;
- //V_ = V_.orthonormal();
-
- // add a sample to the angular vel vector
- double dt = elapsedTime_ - rotLastTime_;
- double avel = angle / dt;
- if (std::isfinite(avel)) {
- rotAngularVelBuffer_.push_back(std::make_pair(elapsedTime_, avel));
- }
- rotLastTime_ = elapsedTime_;
- }
- }
-
- recalc_angular_vel();
- }
- }
- else if (state_ == UniCamState::START) {
- // picked up a little mouse movement after "catching" a spinning model
- // nothing to do, just wait for the button up.
- }
- else {
- std::cerr << "UniCam::OnDrag() unexpected state." << std::endl;
- }
- mouseLast_ = mousePos;
-}
-
-void UniCam::OnButtonUp(const Point2 &mousePos) {
- if (state_ == UniCamState::PAN_DOLLY_ROT_DECISION) {
- // here, we got a quick click of the mouse to indicate a center of rotation
- // so we now go into a mode of waiting for a second click to start rotating
- // around that point.
- state_ = UniCamState::ROT_WAIT_FOR_SECOND_CLICK;
- }
- else if (state_ == UniCamState::ROT) {
- showIcon_ = false;
- // if we are leaving the rotation state and the angular velocity is
- // greater than some thresold, then the user has "thrown" the model
- // keep rotating the same way by entering the spinning state.
-
- recalc_angular_vel();
- //std::cout << "check for spin: " << n-start << " " << rotAngularVel_ << " " << avel2 << std::endl;
-
- const float threshold = 0.2f;
- if (std::fabs(rotAngularVel_) > threshold) {
- state_ = UniCamState::SPINNING;
- }
- else {
- state_ = UniCamState::START;
- }
- }
- else {
- showIcon_ = false;
- // all other cases go back to the start state
- state_ = UniCamState::START;
- }
-}
-
-void UniCam::AdvanceAnimation(double dt) {
- elapsedTime_ += dt;
-
- if (state_ == UniCamState::SPINNING) {
- double deltaT = elapsedTime_ - rotLastTime_;
- rotLastTime_ = elapsedTime_;
- double angle = (double)rotAngularVel_ * deltaT;
- Matrix4 R = Matrix4::Rotation(boundingSphereCtr_, rotAxis_, (float)angle);
- //R = R.orthonormal();
- V_ = V_ * R;
- }
-}
-
-
-void UniCam::Draw(const Matrix4 &projectionMatrix) {
- Pdraw_ = projectionMatrix;
-
- if (showIcon_) {
- Matrix4 camMat = V_.Inverse();
- Point3 eye = camMat.ColumnToPoint3(3);
- Vector3 look = -camMat.ColumnToVector3(2);
- float depth = (hitPoint_ - eye).Dot(look);
- Point3 pWorld1 = GfxMath::ScreenToDepthPlane(V_, Pdraw_, Point2(0.f,0.f), depth);
- Point3 pWorld2 = GfxMath::ScreenToDepthPlane(V_, Pdraw_, Point2(0.015f,0.f), depth);
- float rad = (pWorld2 - pWorld1).Length();
- Matrix4 M = Matrix4::Translation(hitPoint_ - Point3::Origin()) * Matrix4::Scale(Vector3(rad, rad, rad));
- quickShapes_.DrawSphere(M, V_, Pdraw_, Color(0,0,0));
- }
-}
-
-
-Matrix4 UniCam::view_matrix() {
- return V_;
-}
-
-void UniCam::set_view_matrix(Matrix4 viewMatrix) {
- V_ = viewMatrix;
-}
-
-void UniCam::set_default_depth(float d) {
- defaultDepth_ = d;
-}
-
-Point3 UniCam::eye() {
- Matrix4 camMat = V_.Inverse();
- return camMat.ColumnToPoint3(3);
-}
-
-Vector3 UniCam::look() {
- Matrix4 camMat = V_.Inverse();
- return -camMat.ColumnToVector3(2);
-}
-
-
-
-
-} // end namespace
+/*
+ Copyright (c) 2017,2018 Regents of the University of Minnesota.
+ All Rights Reserved.
+ See corresponding header file for details.
+ */
+
+#include "unicam.h"
+
+#include "gfxmath.h"
+
+namespace mingfx {
+
+
+UniCam::UniCam() : state_(UniCamState::START), defaultDepth_(4.0), boundingSphereRad_(1.0),
+ dollyFactor_(1.0), dollyInitialized_(false), elapsedTime_(0.0), hitGeometry_(false),
+ rotAngularVel_(0.0), rotInitialized_(false), rotLastTime_(0.0), showIcon_(false)
+{
+}
+
+UniCam::UniCam(const Matrix4 &initialViewMatrix) :
+ state_(UniCamState::START), defaultDepth_(4.0), V_(initialViewMatrix), boundingSphereRad_(1.0),
+ dollyFactor_(1.0), dollyInitialized_(false), elapsedTime_(0.0), hitGeometry_(false),
+ rotAngularVel_(0.0), rotInitialized_(false), rotLastTime_(0.0), showIcon_(false)
+{
+}
+
+UniCam::~UniCam()
+{
+}
+
+
+void UniCam::recalc_angular_vel() {
+ // update angular velocity
+ float cutoff = (float)elapsedTime_ - 0.2f; // look just at the last 0.2 secs
+ while ((rotAngularVelBuffer_.size()) && (rotAngularVelBuffer_[0].first < cutoff)) {
+ rotAngularVelBuffer_.erase(rotAngularVelBuffer_.begin());
+ }
+ rotAngularVel_ = 0.0;
+ if (rotAngularVelBuffer_.size()) {
+ for (int i=0; i<rotAngularVelBuffer_.size(); i++) {
+ rotAngularVel_ += rotAngularVelBuffer_[i].second;
+ }
+ rotAngularVel_ /= rotAngularVelBuffer_.size();
+ }
+ //std::cout << rotAngularVelBuffer_.size() << " " << rotAngularVel_ << std::endl;
+}
+
+
+void UniCam::OnButtonDown(const Point2 &mousePos, float mouseZ) {
+ if (state_ == UniCamState::START) {
+ initialClickPos_ = mousePos;
+ mouseLast_ = mousePos;
+ elapsedTime_ = 0.0;
+ rotInitialized_ = false;
+ dollyInitialized_ = false;
+
+ hitGeometry_ = (mouseZ < 1.0);
+ if (hitGeometry_) {
+ hitPoint_ = GfxMath::ScreenToWorld(V_, Pdraw_, mousePos, mouseZ);
+ }
+ else {
+ hitPoint_ = GfxMath::ScreenToDepthPlane(V_, Pdraw_, Point2(0,0), defaultDepth_);
+ }
+ showIcon_ = true;
+ state_ = UniCamState::PAN_DOLLY_ROT_DECISION;
+ }
+ else if (state_ == UniCamState::ROT_WAIT_FOR_SECOND_CLICK) {
+ // we have the second click now, and we will start the trackball rotate interaction
+ state_ = UniCamState::ROT;
+ }
+ else if (state_ == UniCamState::SPINNING) {
+ // this click is to "catch" the model, stopping it from spinning.
+ state_ = UniCamState::START;
+ }
+ else {
+ std::cerr << "UniCam::OnButtonDown() unexpected state." << std::endl;
+ }
+}
+
+void UniCam::OnDrag(const Point2 &mousePos) {
+ if (state_ == UniCamState::PAN_DOLLY_ROT_DECISION) {
+ const double panMovementThreshold = 0.01;
+ const double dollyMovementThreshold = 0.01;
+ if (fabs(mousePos[0] - initialClickPos_[0]) > panMovementThreshold) {
+ // already lots of horizontal movement, we can go right to pan
+ state_ = UniCamState::PAN;
+ showIcon_ = false;
+ }
+ else if (fabs(mousePos[1] - initialClickPos_[1]) > dollyMovementThreshold) {
+ // already lots of vertical movement, we can go right to dolly
+ state_ = UniCamState::DOLLY;
+ showIcon_ = false;
+ }
+ else if (elapsedTime_ > 1.0) {
+ // timeout, this was not a quick click to set a center of rotation,
+ // so there is no intent to rotate. instead we will be doing either
+ // pan or dolly.
+ state_ = UniCamState::PAN_DOLLY_DECISION;
+ showIcon_ = false;
+ }
+ }
+ else if (state_ == UniCamState::PAN_DOLLY_DECISION) {
+ const double panMovementThreshold = 0.01;
+ const double dollyMovementThreshold = 0.01;
+ if (fabs(mousePos[0] - initialClickPos_[0]) > panMovementThreshold) {
+ // lots of horizontal movement, go to pan
+ state_ = UniCamState::PAN;
+ }
+ else if (fabs(mousePos[1] - initialClickPos_[1]) > dollyMovementThreshold) {
+ // lots of vertical movement, go to dolly
+ state_ = UniCamState::DOLLY;
+ }
+ }
+ else if (state_ == UniCamState::PAN) {
+ Matrix4 camMat = V_.Inverse();
+ Point3 eye = camMat.ColumnToPoint3(3);
+ Vector3 look = -camMat.ColumnToVector3(2);
+ float depth = (hitPoint_ - eye).Dot(look);
+ Point3 pWorld1 = GfxMath::ScreenToDepthPlane(V_, Pdraw_, mouseLast_, depth);
+ Point3 pWorld2 = GfxMath::ScreenToDepthPlane(V_, Pdraw_, mousePos, depth);
+ V_ = V_ * Matrix4::Translation(pWorld2 - pWorld1);
+ }
+ else if (state_ == UniCamState::DOLLY) {
+ if (!dollyInitialized_) {
+ // Setup dollyFactor so that if you move the mouse to the bottom of the screen, the point
+ // you clicked on will be right on top of the camera.
+ Matrix4 camMat = V_.Inverse();
+ Point3 eye = camMat.ColumnToPoint3(3);
+ Vector3 look = -camMat.ColumnToVector3(2);
+ float depth = (hitPoint_ - eye).Dot(look);
+ float deltaYToBottom = initialClickPos_[1] + 1;
+ dollyFactor_ = depth / deltaYToBottom;
+ dollyInitialized_ = true;
+ }
+ Vector3 d(0, 0, -dollyFactor_ * (mousePos[1] - mouseLast_[1]));
+ V_ = Matrix4::Translation(d) * V_ ;
+ }
+ else if (state_ == UniCamState::ROT) {
+ if (!rotInitialized_) {
+ float depth = 0.0;
+ if (hitGeometry_) {
+ // if we hit some geometry, then make that the center of rotation
+ boundingSphereCtr_ = hitPoint_;
+ Matrix4 camMat = V_.Inverse();
+ Point3 eye = camMat.ColumnToPoint3(3);
+ Vector3 look = -camMat.ColumnToVector3(2);
+ depth = (hitPoint_ - eye).Dot(look);
+ }
+ else {
+ // if we did not hit any geometry, then center the bounding sphere in front of
+ // the camera at a distance that can be configured by the user.
+ boundingSphereCtr_ = GfxMath::ScreenToDepthPlane(V_, Pdraw_, Point2(0,0), defaultDepth_);
+ depth = defaultDepth_;
+ }
+
+ // determine the size of the bounding sphere by projecting a screen-space
+ // distance of 0.75 units to the depth of the sphere center
+ Point3 pWorld1 = GfxMath::ScreenToDepthPlane(V_, Pdraw_, Point2(0,0), depth);
+ Point3 pWorld2 = GfxMath::ScreenToDepthPlane(V_, Pdraw_, Point2(0.75,0), depth);
+ boundingSphereRad_ = (pWorld2-pWorld1).Length();
+
+ rotLastTime_ = elapsedTime_;
+ rotAngularVelBuffer_.clear();
+ rotInitialized_ = true;
+ }
+ else {
+ // Do a trackball rotation based on the mouse movement and the bounding sphere
+ // setup earlier.
+
+ Matrix4 camMat = V_.Inverse();
+ Point3 eye = camMat.ColumnToPoint3(3);
+
+ // last mouse pos
+ bool hit1 = false;
+ Point3 mouse3D1 = GfxMath::ScreenToNearPlane(V_, Pdraw_, mouseLast_);
+ Ray ray1(eye, mouse3D1 - eye);
+ float t1;
+ Point3 iPoint1;
+ if (ray1.IntersectSphere(boundingSphereCtr_, boundingSphereRad_, &t1, &iPoint1)) {
+ hit1 = true;
+ }
+
+ // current mouse pos
+ bool hit2 = false;
+ Point3 mouse3D2 = GfxMath::ScreenToNearPlane(V_, Pdraw_, mousePos);
+ Ray ray2(eye, mouse3D2 - eye);
+ float t2;
+ Point3 iPoint2;
+ if (ray2.IntersectSphere(boundingSphereCtr_, boundingSphereRad_, &t2, &iPoint2)) {
+ hit2 = true;
+ }
+ rotLastIPoint_ = iPoint2;
+
+ if (hit1 && hit2) {
+ Vector3 v1 = (iPoint1 - boundingSphereCtr_).ToUnit();
+ Vector3 v2 = (iPoint2 - boundingSphereCtr_).ToUnit();
+
+ rotAxis_ = v1.Cross(v2).ToUnit();
+ float angle = std::acos(v1.Dot(v2));
+
+ if (std::isfinite(angle)) {
+ Matrix4 R = Matrix4::Rotation(boundingSphereCtr_, rotAxis_, angle);
+ R = R.Orthonormal();
+ V_ = V_ * R;
+ //V_ = V_.orthonormal();
+
+ // add a sample to the angular vel vector
+ double dt = elapsedTime_ - rotLastTime_;
+ double avel = angle / dt;
+ if (std::isfinite(avel)) {
+ rotAngularVelBuffer_.push_back(std::make_pair(elapsedTime_, avel));
+ }
+ rotLastTime_ = elapsedTime_;
+ }
+ }
+
+ recalc_angular_vel();
+ }
+ }
+ else if (state_ == UniCamState::START) {
+ // picked up a little mouse movement after "catching" a spinning model
+ // nothing to do, just wait for the button up.
+ }
+ else {
+ std::cerr << "UniCam::OnDrag() unexpected state." << std::endl;
+ }
+ mouseLast_ = mousePos;
+}
+
+void UniCam::OnButtonUp(const Point2 &mousePos) {
+ if (state_ == UniCamState::PAN_DOLLY_ROT_DECISION) {
+ // here, we got a quick click of the mouse to indicate a center of rotation
+ // so we now go into a mode of waiting for a second click to start rotating
+ // around that point.
+ state_ = UniCamState::ROT_WAIT_FOR_SECOND_CLICK;
+ }
+ else if (state_ == UniCamState::ROT) {
+ showIcon_ = false;
+ // if we are leaving the rotation state and the angular velocity is
+ // greater than some thresold, then the user has "thrown" the model
+ // keep rotating the same way by entering the spinning state.
+
+ recalc_angular_vel();
+ //std::cout << "check for spin: " << n-start << " " << rotAngularVel_ << " " << avel2 << std::endl;
+
+ const float threshold = 0.2f;
+ if (std::fabs(rotAngularVel_) > threshold) {
+ state_ = UniCamState::SPINNING;
+ }
+ else {
+ state_ = UniCamState::START;
+ }
+ }
+ else {
+ showIcon_ = false;
+ // all other cases go back to the start state
+ state_ = UniCamState::START;
+ }
+}
+
+void UniCam::AdvanceAnimation(double dt) {
+ elapsedTime_ += dt;
+
+ if (state_ == UniCamState::SPINNING) {
+ double deltaT = elapsedTime_ - rotLastTime_;
+ rotLastTime_ = elapsedTime_;
+ double angle = (double)rotAngularVel_ * deltaT;
+ Matrix4 R = Matrix4::Rotation(boundingSphereCtr_, rotAxis_, (float)angle);
+ //R = R.orthonormal();
+ V_ = V_ * R;
+ }
+}
+
+
+void UniCam::Draw(const Matrix4 &projectionMatrix) {
+ Pdraw_ = projectionMatrix;
+
+ if (showIcon_) {
+ Matrix4 camMat = V_.Inverse();
+ Point3 eye = camMat.ColumnToPoint3(3);
+ Vector3 look = -camMat.ColumnToVector3(2);
+ float depth = (hitPoint_ - eye).Dot(look);
+ Point3 pWorld1 = GfxMath::ScreenToDepthPlane(V_, Pdraw_, Point2(0.f,0.f), depth);
+ Point3 pWorld2 = GfxMath::ScreenToDepthPlane(V_, Pdraw_, Point2(0.015f,0.f), depth);
+ float rad = (pWorld2 - pWorld1).Length();
+ Matrix4 M = Matrix4::Translation(hitPoint_ - Point3::Origin()) * Matrix4::Scale(Vector3(rad, rad, rad));
+ quickShapes_.DrawSphere(M, V_, Pdraw_, Color(0,0,0));
+ }
+}
+
+
+Matrix4 UniCam::view_matrix() {
+ return V_;
+}
+
+void UniCam::set_view_matrix(Matrix4 viewMatrix) {
+ V_ = viewMatrix;
+}
+
+void UniCam::set_default_depth(float d) {
+ defaultDepth_ = d;
+}
+
+Point3 UniCam::eye() {
+ Matrix4 camMat = V_.Inverse();
+ return camMat.ColumnToPoint3(3);
+}
+
+Vector3 UniCam::look() {
+ Matrix4 camMat = V_.Inverse();
+ return -camMat.ColumnToVector3(2);
+}
+
+
+
+
+} // end namespace