#include "FractoriumPch.h" #include "GLEmberController.h" #include "FractoriumEmberController.h" #include "Fractorium.h" #include "GLWidget.h" /// /// Constructor which assigns pointers to the main window and the GLWidget. /// /// Pointer to the main window /// Pointer to the GLWidget GLEmberControllerBase::GLEmberControllerBase(Fractorium* fractorium, GLWidget* glWidget) { m_Fractorium = fractorium; m_GL = glWidget; m_AffineType = AffinePre; m_HoverType = HoverNone; m_DragState = DragNone; m_DragModifier = 0; } /// /// Empty destructor which does nothing. /// GLEmberControllerBase::~GLEmberControllerBase() { } /// /// Constructor which passes the pointers to the main window the GLWidget to the base, /// then assigns the pointer to the parent controller. /// /// Pointer to the main window /// Pointer to the GLWidget /// Pointer to the parent controller of the same template type template GLEmberController::GLEmberController(Fractorium* fractorium, GLWidget* glWidget, FractoriumEmberController* controller) : GLEmberControllerBase(fractorium, glWidget) { GridStep = T(1.0 / 8.0); m_FractoriumEmberController = controller; m_HoverXform = nullptr; m_SelectedXform = nullptr; m_CenterDownX = 0; m_CenterDownY = 0; } /// /// Empty destructor which does nothing. /// template GLEmberController::~GLEmberController() { } /// /// Check that the final output size of the current ember matches the dimensions passed in. /// /// The width to compare to /// The height to compare to /// True if any don't match, else false if they are both equal. template bool GLEmberController::CheckForSizeMismatch(int w, int h) { return (m_FractoriumEmberController->FinalRasW() != w || m_FractoriumEmberController->FinalRasH() != h); } /// /// Reset the drag and hover state. Called in response setting a new ember as the current one. /// template void GLEmberController::ResetMouseState() { m_HoverType = HoverNone; m_HoverXform = nullptr; m_SelectedXform = nullptr; } /// /// Calculate the scale. /// Used when dragging the right mouse button. /// /// The distance dragged in pixels template T GLEmberController::CalcScale() { //Can't operate using world coords here because every time scale changes, the world bounds change. //So must instead calculate distance traveled based on window coords, which do not change outside of resize events. v2T windowCenter(T(m_GL->width()) / T(2), T(m_GL->height()) / T(2)); v2T windowMousePosDistanceFromCenter(m_MousePos.x - windowCenter.x, m_MousePos.y - windowCenter.y); v2T windowMouseDownDistanceFromCenter(m_MouseDownPos.x - windowCenter.x, m_MouseDownPos.y - windowCenter.y); T lengthMousePosFromCenterInPixels = glm::length(windowMousePosDistanceFromCenter); T lengthMouseDownFromCenterInPixels = glm::length(windowMouseDownDistanceFromCenter); return lengthMousePosFromCenterInPixels - lengthMouseDownFromCenterInPixels; } /// /// Calculate the rotation. /// Used when dragging the right mouse button. /// /// The angular distance rotated from -180-180 template T GLEmberController::CalcRotation() { T rotStart = NormalizeDeg180(T(90) - (atan2(-m_MouseDownWorldPos.y, m_MouseDownWorldPos.x) * RAD_2_DEG_T)); T rot = NormalizeDeg180(T(90) - (atan2(-m_MouseWorldPos.y, m_MouseWorldPos.x) * RAD_2_DEG_T)); return rotStart - rot; } /// /// Snap the passed in world cartesian coordinate to the grid for rotation, scale or translation. /// /// The world cartesian coordinate to be snapped /// The snapped world cartesian coordinate template typename v3T GLEmberController::SnapToGrid(v3T& vec) { v3T ret; ret.x = glm::round(vec.x / GridStep) * GridStep; ret.y = glm::round(vec.y / GridStep) * GridStep; return ret; } /// /// Snap the passed in world cartesian coordinate to the grid for rotation only. /// /// The world cartesian coordinate to be snapped /// The divisions of a circle to use for snapping /// The snapped world cartesian coordinate template typename v3T GLEmberController::SnapToNormalizedAngle(v3T& vec, uint divisions) { T rsq, theta; T bestRsq = numeric_limits::max(); v3T c, best; best.x = 1; best.y = 0; for (uint i = 0; i < divisions; i++) { theta = 2.0 * M_PI * T(i) / T(divisions); c.x = cos(theta); c.y = sin(theta); rsq = glm::distance(vec, c); if (rsq < bestRsq) { best = c; bestRsq = rsq; } } return best; } /// /// Convert raster window coordinates to world cartesian coordinates. /// /// The window coordinates to convert /// True to flip vertically, else don't. /// The converted world cartesian coordinates template typename v3T GLEmberController::WindowToWorld(v3T& v, bool flip) { v3T mouse(v.x, flip ? m_Viewport[3] - v.y : v.y, 0);//Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left. v3T newCoords = glm::unProject(mouse, m_Modelview, m_Projection, m_Viewport);//Perform the calculation. newCoords.z = 0;//For some reason, unProject() always comes back with the z coordinate as something other than 0. It should be 0 at all times. return newCoords; } /// /// Template specialization for querying the viewport, modelview and projection /// matrices as floats. /// template <> void GLEmberController::QueryVMP() { m_GL->glGetIntegerv(GL_VIEWPORT, glm::value_ptr(m_Viewport)); m_GL->glGetFloatv(GL_MODELVIEW_MATRIX, glm::value_ptr(m_Modelview)); m_GL->glGetFloatv(GL_PROJECTION_MATRIX, glm::value_ptr(m_Projection)); } #ifdef DO_DOUBLE /// /// Template specialization for querying the viewport, modelview and projection /// matrices as doubles. /// template <> void GLEmberController::QueryVMP() { m_GL->glGetIntegerv(GL_VIEWPORT, glm::value_ptr(m_Viewport)); m_GL->glGetDoublev(GL_MODELVIEW_MATRIX, glm::value_ptr(m_Modelview)); m_GL->glGetDoublev(GL_PROJECTION_MATRIX, glm::value_ptr(m_Projection)); } #endif /// /// Template specialization for multiplying the current matrix /// by an m4. /// template <> void GLEmberController::MultMatrix(tmat4x4& mat) { m_GL->glMultMatrixf(glm::value_ptr(mat)); } #ifdef DO_DOUBLE /// /// Template specialization for multiplying the current matrix /// by an m4. /// template <> void GLEmberController::MultMatrix(tmat4x4& mat) { m_GL->glMultMatrixd(glm::value_ptr(mat)); } #endif /// /// Query the matrices currently being used. /// Debugging function, unused. /// /// True to print values, else false. template void GLEmberController::QueryMatrices(bool print) { RendererBase* renderer = m_FractoriumEmberController->Renderer(); if (renderer) { double unitX = fabs(renderer->UpperRightX(false) - renderer->LowerLeftX(false)) / 2.0; double unitY = fabs(renderer->UpperRightY(false) - renderer->LowerLeftY(false)) / 2.0; m_GL->glMatrixMode(GL_PROJECTION); m_GL->glPushMatrix(); m_GL->glLoadIdentity(); m_GL->glOrtho(-unitX, unitX, -unitY, unitY, -1, 1); m_GL->glMatrixMode(GL_MODELVIEW); m_GL->glPushMatrix(); m_GL->glLoadIdentity(); QueryVMP(); m_GL->glMatrixMode(GL_PROJECTION); m_GL->glPopMatrix(); m_GL->glMatrixMode(GL_MODELVIEW); m_GL->glPopMatrix(); if (print) { for (int i = 0; i < 4; i++) qDebug() << "Viewport[" << i << "] = " << m_Viewport[i] << endl; for (int i = 0; i < 16; i++) qDebug() << "Modelview[" << i << "] = " << glm::value_ptr(m_Modelview)[i] << endl; for (int i = 0; i < 16; i++) qDebug() << "Projection[" << i << "] = " << glm::value_ptr(m_Projection)[i] << endl; } } } template class GLEmberController; #ifdef DO_DOUBLE template class GLEmberController; #endif