#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