#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 = eAffineType::AffinePre; m_HoverType = eHoverType::HoverNone; m_DragState = eDragState::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 = static_cast(1.0 / 4.0); // michel, needs to insert on GUI to be flexible//TODO 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 = eHoverType::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. const auto windowCenter = ScrolledCenter(false); const v2T windowMousePosDistanceFromCenter(m_MousePos.x - windowCenter.x, m_MousePos.y - windowCenter.y); const v2T windowMouseDownDistanceFromCenter(m_MouseDownPos.x - windowCenter.x, m_MouseDownPos.y - windowCenter.y); const T lengthMousePosFromCenterInPixels = glm::length(windowMousePosDistanceFromCenter); const 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() { const auto scrolledWorldCenter = ScrolledCenter(true); const T rotStart = NormalizeDeg360((std::atan2(m_MouseDownWorldPos.y - scrolledWorldCenter.y, m_MouseDownWorldPos.x - scrolledWorldCenter.x) * RAD_2_DEG_T)); const T rot = NormalizeDeg360((std::atan2(m_MouseWorldPos.y - scrolledWorldCenter.y, m_MouseWorldPos.x - scrolledWorldCenter.x) * RAD_2_DEG_T)); return rotStart - rot; } /// /// Return the window coordinates of the center of the viewable area. /// This is the middle of the parent scroll area plus the scroll bar offset, all scaled by the device pixel ratio. /// /// True to return world coordinates, else return window coordinates. /// The coordinates of the center of the viewable area in either window space or world space. template v3T GLEmberController::ScrolledCenter(bool toWorld) { const auto dprf = m_GL->devicePixelRatioF(); const auto wpsa = m_Fractorium->ui.GLParentScrollArea->width(); const auto hpsa = m_Fractorium->ui.GLParentScrollArea->height(); const auto hpos = m_Fractorium->ui.GLParentScrollArea->horizontalScrollBar()->value(); const auto vpos = m_Fractorium->ui.GLParentScrollArea->verticalScrollBar()->value(); v3T v; if (!m_Fractorium->ui.GLParentScrollArea->horizontalScrollBar()->isVisible() && !m_Fractorium->ui.GLParentScrollArea->verticalScrollBar()->isVisible()) v = v3T(((m_GL->width() / 2)) * dprf, ((m_GL->height() / 2)) * dprf, 0); else v = v3T((hpos + (wpsa / 2)) * dprf, (vpos + (hpsa / 2)) * dprf, 0); if (toWorld) return WindowToWorld(v, true); return v; } /// /// 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 v2T GLEmberController::SnapToGrid(const v2T& vec) const { return v2T(glm::round(vec.x / GridStep) * GridStep, glm::round(vec.y / GridStep) * GridStep); } /// /// 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(const v3T& vec) const { return v3T(glm::round(vec.x / GridStep) * GridStep, glm::round(vec.y / GridStep) * GridStep, vec.z); } /// /// 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(const v3T& vec, uint divisions) const { T bestRsq = numeric_limits::max(); v3T c(0, 0, 0), best; best.x = 1; best.y = 0; for (uint i = 0; i < divisions; i++) { const auto theta = 2.0 * M_PI * static_cast(i) / static_cast(divisions); c.x = std::cos(theta); c.y = std::sin(theta); const auto 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(const v3T& v, bool flip) const { const 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() { #ifndef USE_GLSL 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)); #else m_Viewport = m_GL->m_Viewport; glm::tmat4x4 tempmat = glm::make_mat4(m_GL->m_ModelViewMatrix.data()); m_Modelview = tempmat; tempmat = glm::make_mat4(m_GL->m_ProjMatrix.data()); m_Projection = tempmat; #endif } #ifdef DO_DOUBLE /// /// Template specialization for querying the viewport, modelview and projection /// matrices as doubles. /// template <> void GLEmberController::QueryVMP() { #ifndef USE_GLSL 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)); #else m_Viewport = m_GL->m_Viewport; glm::tmat4x4 tempmat = glm::make_mat4(m_GL->m_ModelViewMatrix.data()); m_Modelview = tempmat; tempmat = glm::make_mat4(m_GL->m_ProjMatrix.data()); m_Projection = tempmat; #endif } #endif /// /// Template specialization for multiplying the current matrix /// by an m4. /// #ifndef USE_GLSL template <> void GLEmberController::MultMatrix(tmat4x4& mat) { m_GL->glMultMatrixf(glm::value_ptr(mat)); } #endif #ifdef DO_DOUBLE /// /// Template specialization for multiplying the current matrix /// by an m4. /// #ifndef USE_GLSL template <> void GLEmberController::MultMatrix(tmat4x4& mat) { m_GL->glMultMatrixd(glm::value_ptr(mat)); } #endif #endif /// /// Query the matrices currently being used. /// Debugging function, unused. /// /// True to print values, else false. template void GLEmberController::QueryMatrices(bool print) { if (const auto renderer = m_FractoriumEmberController->Renderer()) { QueryVMP(); if (print) { for (glm::length_t i = 0; i < 4; i++) qDebug() << "Viewport[" << i << "] = " << m_Viewport[i] << "\n"; for (glm::length_t i = 0; i < 16; i++) qDebug() << "Modelview[" << i << "] = " << glm::value_ptr(m_Modelview)[i] << "\n"; for (glm::length_t i = 0; i < 16; i++) qDebug() << "Projection[" << i << "] = " << glm::value_ptr(m_Projection)[i] << "\n"; } } } template class GLEmberController; #ifdef DO_DOUBLE template class GLEmberController; #endif