diff --git a/src/backend/common/defines.hpp b/src/backend/common/defines.hpp index 3628f2af..b0ca4d69 100644 --- a/src/backend/common/defines.hpp +++ b/src/backend/common/defines.hpp @@ -20,11 +20,16 @@ namespace common { using CellIndex = std::tuple; using MatrixHashMap = std::unordered_map; -constexpr float PI = 3.14159f; -constexpr float BLACK[] = {0.0f, 0.0f, 0.0f, 1.0f}; -constexpr float GRAY[] = {0.75f, 0.75f, 0.75f, 1.0f}; -constexpr float WHITE[] = {1.0f, 1.0f, 1.0f, 1.0f}; -constexpr float AF_BLUE[] = {0.0588f, 0.1137f, 0.2745f, 1.0f}; +constexpr int ARCBALL_CIRCLE_POINTS = 100; +constexpr float MOVE_SPEED = 0.005f; +constexpr float ZOOM_SPEED = 0.0075f; +constexpr float EPSILON = 1.0e-6f; +constexpr float ARC_BALL_RADIUS = 0.75f; +constexpr double PI = 3.14159265358979323846; +constexpr float BLACK[] = {0.0f, 0.0f, 0.0f, 1.0f}; +constexpr float GRAY[] = {0.75f, 0.75f, 0.75f, 1.0f}; +constexpr float WHITE[] = {1.0f, 1.0f, 1.0f, 1.0f}; +constexpr float AF_BLUE[] = {0.0588f, 0.1137f, 0.2745f, 1.0f}; static const glm::mat4 IDENTITY(1.0f); #if defined(OS_WIN) diff --git a/src/backend/common/util.cpp b/src/backend/common/util.cpp index 5fff2643..2945f808 100644 --- a/src/backend/common/util.cpp +++ b/src/backend/common/util.cpp @@ -20,6 +20,11 @@ #include #endif +using glm::vec2; +using glm::vec3; +using glm::vec4; +using std::make_pair; +using std::pair; using std::string; namespace forge { @@ -142,20 +147,32 @@ std::ostream& operator<<(std::ostream& pOut, const glm::mat4& pMat) { return pOut; } -glm::vec3 trackballPoint(const float pX, const float pY, const float pWidth, - const float pHeight) { - glm::vec3 P = - glm::vec3(1.0 * pX / pWidth * 2 - 1.0, 1.0 * pY / pHeight * 2 - 1.0, 0); - - P.y = -P.y; - float OP_squared = P.x * P.x + P.y * P.y; - if (OP_squared <= 1 * 1) { - P.z = sqrt(1 * 1 - OP_squared); - } else { - P.z = 0; - P = glm::normalize(P); - } - return P; +pair calcRotationFromArcBall(const vec2& lastPos, + const vec2& currPos, + const vec4& viewport) { + auto project = [](const float pX, const float pY, const float pWidth, + const float pHeight) { + glm::vec3 P = glm::vec3((2.0f * pX) / pWidth - 1.0f, + (2.0f * pY) / pHeight - 1.0f, 0.0f); + float xySqrdSum = P.x * P.x + P.y * P.y; + float rSqrd = (ARC_BALL_RADIUS * ARC_BALL_RADIUS); + float rSqrdBy2 = rSqrd / 2.0f; + // Project to Hyperbolic Sheet if Sum of X^2 and Y^2 is + // greater than (RADIUS^2)/2 ; Otherwise to a sphere + P.z = (xySqrdSum > rSqrdBy2 ? rSqrdBy2 / sqrt(xySqrdSum) + : sqrt(rSqrd - xySqrdSum)); + return P; + }; + auto ORG = vec2(viewport[0], viewport[1]); + // Offset window position to viewport frame of reference + auto p1 = lastPos - ORG; + auto p2 = currPos - ORG; + auto op1 = project(p1.x, p1.y, viewport[2], viewport[3]); + auto op2 = project(p2.x, p2.y, viewport[2], viewport[3]); + auto n1 = glm::normalize(op1); + auto n2 = glm::normalize(op2); + + return make_pair(glm::cross(op2, op1), std::acos(glm::dot(n1, n2))); } } // namespace common diff --git a/src/backend/common/util.hpp b/src/backend/common/util.hpp index 34b30884..80bf0806 100644 --- a/src/backend/common/util.hpp +++ b/src/backend/common/util.hpp @@ -16,6 +16,7 @@ #include #include +#include #include namespace forge { @@ -54,9 +55,19 @@ std::string toString(const float pVal, const std::string pFormat); /* Print glm::mat4 to std::cout stream */ std::ostream& operator<<(std::ostream&, const glm::mat4&); -/* get the point of the surface of track ball */ -glm::vec3 trackballPoint(const float pX, const float pY, const float pWidth, - const float pHeight); +/* Calculate rotation axis and amount of rotation of Arc Ball + * + * This computation requires previous and current mouse cursor positions + * which are the input parameters to this function call + * + * @lastPos previous mouse position + * @currPos current mouse position + * + * @return Rotation axis vector and the angle of rotation + * */ +std::pair calcRotationFromArcBall(const glm::vec2& lastPos, + const glm::vec2& currPos, + const glm::vec4& viewport); } // namespace common } // namespace forge diff --git a/src/backend/glsl_shaders/plot3_fs.glsl b/src/backend/glsl_shaders/plot3_fs.glsl index 15ba9a23..eadff72c 100644 --- a/src/backend/glsl_shaders/plot3_fs.glsl +++ b/src/backend/glsl_shaders/plot3_fs.glsl @@ -3,31 +3,33 @@ uniform vec2 minmaxs[3]; uniform bool isPVCOn; uniform bool isPVAOn; +uniform bool isAssistDraw; +uniform vec4 lineColor; in vec4 hpoint; in vec4 pervcol; out vec4 outColor; -vec3 hsv2rgb(vec3 c) -{ - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } -void main(void) -{ - bool nin_bounds = (hpoint.x > minmaxs[0].y || hpoint.x < minmaxs[0].x - || hpoint.y > minmaxs[1].y || hpoint.y < minmaxs[1].x - || hpoint.z < minmaxs[2].x); +void main(void) { + bool nin_bounds = (hpoint.x > minmaxs[0].y || hpoint.x < minmaxs[0].x || + hpoint.y > minmaxs[1].y || hpoint.y < minmaxs[1].x || + hpoint.z < minmaxs[2].x); - float height = (minmaxs[2].y- hpoint.z)/(minmaxs[2].y-minmaxs[2].x); + float height = (minmaxs[2].y - hpoint.z) / (minmaxs[2].y - minmaxs[2].x); - float a = isPVAOn ? pervcol.w : 1.0; + float a = isPVAOn ? pervcol.w : 1.0; - if(nin_bounds) - discard; - else - outColor = isPVCOn ? vec4(pervcol.xyz, a) : vec4(hsv2rgb(vec3(height, 1, 1)),a); + if (nin_bounds) + discard; + else + outColor = isPVCOn ? vec4(pervcol.xyz, a) + : isAssistDraw ? lineColor + : vec4(hsv2rgb(vec3(height, 1, 1)), a); } diff --git a/src/backend/opengl/abstract_renderable.hpp b/src/backend/opengl/abstract_renderable.hpp index 3cf257f2..d6ff791b 100644 --- a/src/backend/opengl/abstract_renderable.hpp +++ b/src/backend/opengl/abstract_renderable.hpp @@ -36,6 +36,7 @@ class AbstractRenderable { std::string mLegend; bool mIsPVCOn; bool mIsPVAOn; + bool mIsInternalObject; AbstractRenderable() : mVBO(0) @@ -45,7 +46,8 @@ class AbstractRenderable { , mCBOSize(0) , mABOSize(0) , mIsPVCOn(0) - , mIsPVAOn(0) { + , mIsPVAOn(0) + , mIsInternalObject(false) { mColor[0] = 0; mColor[1] = 0; mColor[2] = 0; @@ -147,6 +149,16 @@ class AbstractRenderable { virtual void render(const int pWindowId, const int pX, const int pY, const int pVPW, const int pVPH, const glm::mat4& pView, const glm::mat4& pOrient) = 0; + + /* + * Mark the renderable is for internal use for assistive help + */ + inline void markAsInternalObject() { mIsInternalObject = true; } + + /* + * Only rotatble renderables need to show 3d dimenionsional helper objects + */ + virtual bool isRotatable() const = 0; }; } // namespace opengl diff --git a/src/backend/opengl/chart_impl.hpp b/src/backend/opengl/chart_impl.hpp index f6882317..6fa8edff 100644 --- a/src/backend/opengl/chart_impl.hpp +++ b/src/backend/opengl/chart_impl.hpp @@ -170,6 +170,8 @@ class chart2d_impl : public AbstractChart { void render(const int pWindowId, const int pX, const int pY, const int pVPW, const int pVPH, const glm::mat4& pView, const glm::mat4& pOrient); + + bool isRotatable() const { return false; } }; class chart3d_impl : public AbstractChart { @@ -191,6 +193,8 @@ class chart3d_impl : public AbstractChart { void render(const int pWindowId, const int pX, const int pY, const int pVPW, const int pVPH, const glm::mat4& pView, const glm::mat4& pOrient); + + bool isRotatable() const { return true; } }; } // namespace opengl diff --git a/src/backend/opengl/glfw/window.cpp b/src/backend/opengl/glfw/window.cpp index 6d006330..19045d6e 100644 --- a/src/backend/opengl/glfw/window.cpp +++ b/src/backend/opengl/glfw/window.cpp @@ -12,14 +12,21 @@ #include #include +#include #include +#include -#include #include +using glm::epsilonNotEqual; +using glm::make_vec4; +using glm::mat4; using glm::rotate; using glm::scale; using glm::translate; +using glm::vec2; +using glm::vec3; + using namespace forge::common; #define GLFW_THROW_ERROR(msg, err) FG_ERROR("Windows Constructor: " #msg, err) @@ -36,11 +43,11 @@ void initWindowToolkit() { void destroyWindowToolkit() { glfwTerminate(); } -const glm::mat4 Widget::findTransform(const MatrixHashMap& pMap, const float pX, - const float pY) { +const mat4 Widget::findTransform(const MatrixHashMap& pMap, const float pX, + const float pY) { for (auto it : pMap) { const CellIndex& idx = it.first; - const glm::mat4& mat = it.second; + const mat4& mat = it.second; const int rows = std::get<0>(idx); const int cols = std::get<1>(idx); @@ -57,18 +64,17 @@ const glm::mat4 Widget::findTransform(const MatrixHashMap& pMap, const float pX, return IDENTITY; } -const glm::mat4 Widget::getCellViewMatrix(const float pXPos, - const float pYPos) { +const mat4 Widget::getCellViewMatrix(const float pXPos, const float pYPos) { return findTransform(mViewMatrices, pXPos, pYPos); } -const glm::mat4 Widget::getCellOrientationMatrix(const float pXPos, - const float pYPos) { +const mat4 Widget::getCellOrientationMatrix(const float pXPos, + const float pYPos) { return findTransform(mOrientMatrices, pXPos, pYPos); } void Widget::setTransform(MatrixHashMap& pMap, const float pX, const float pY, - const glm::mat4& pMat) { + const mat4& pMat) { for (auto it : pMap) { const CellIndex& idx = it.first; @@ -86,12 +92,12 @@ void Widget::setTransform(MatrixHashMap& pMap, const float pX, const float pY, } void Widget::setCellViewMatrix(const float pXPos, const float pYPos, - const glm::mat4& pMatrix) { + const mat4& pMatrix) { return setTransform(mViewMatrices, pXPos, pYPos, pMatrix); } void Widget::setCellOrientationMatrix(const float pXPos, const float pYPos, - const glm::mat4& pMatrix) { + const mat4& pMatrix) { return setTransform(mOrientMatrices, pXPos, pYPos, pMatrix); } @@ -106,15 +112,19 @@ void Widget::resetOrientationMatrices() { Widget::Widget() : mWindow(NULL) , mClose(false) - , mLastXPos(0) - , mLastYPos(0) + , mLastPos(0, 0) , mButton(-1) + , mRotationFlag(false) , mWidth(512) , mHeight(512) {} Widget::Widget(int pWidth, int pHeight, const char* pTitle, const std::unique_ptr& pWidget, const bool invisible) - : mWindow(NULL), mClose(false), mLastXPos(0), mLastYPos(0), mButton(-1) { + : mWindow(NULL) + , mClose(false) + , mLastPos(0, 0) + , mButton(-1) + , mRotationFlag(false) { auto wndErrCallback = [](int errCode, const char* pDescription) { fputs(pDescription, stderr); }; @@ -175,10 +185,16 @@ Widget::Widget(int pWidth, int pHeight, const char* pTitle, glfwSetMouseButtonCallback(mWindow, mouseButtonCallback); glfwGetFramebufferSize(mWindow, &mWidth, &mHeight); + + // Set Hand cursor for Rotation and Zoom Modes + mRotationCursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR); + mZoomCursor = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); } Widget::~Widget() { if (mWindow) glfwDestroyWindow(mWindow); + if (mRotationCursor) glfwDestroyCursor(mRotationCursor); + if (mZoomCursor) glfwDestroyCursor(mZoomCursor); } GLFWwindow* Widget::getNativeHandle() const { return mWindow; } @@ -235,101 +251,94 @@ void Widget::keyboardHandler(int pKey, int pScancode, int pAction, int pMods) { } void Widget::cursorHandler(const float pXPos, const float pYPos) { - static const float SPEED = 0.005f; + constexpr auto ZOOM_ENABLER = + GLFW_MOUSE_BUTTON_LEFT + 10 * GLFW_MOD_CONTROL; - float deltaX = mLastXPos - pXPos; - float deltaY = mLastYPos - pYPos; + const vec2 currPos(pXPos, pYPos); - const glm::mat4 viewMat = getCellViewMatrix(pXPos, pYPos); + if (mButtonAction == GLFW_PRESS) { + auto delta = mLastPos - currPos; + auto viewMat = getCellViewMatrix(pXPos, pYPos); - if (mButton == GLFW_MOUSE_BUTTON_LEFT) { - // Translate - glm::mat4 vMat = - translate(viewMat, glm::vec3(-deltaX, deltaY, 0.0f) * SPEED); - - setCellViewMatrix(pXPos, pYPos, vMat); - - } else if (mButton == GLFW_MOUSE_BUTTON_LEFT + 10 * GLFW_MOD_ALT || - mButton == GLFW_MOUSE_BUTTON_LEFT + 10 * GLFW_MOD_CONTROL) { - // Zoom - if (deltaY != 0.0f) { - if (deltaY < 0.0f) { deltaY = 1.0f / (-deltaY); } - glm::mat4 vMat = scale(viewMat, glm::vec3(pow(deltaY, SPEED))); + if (mButton == GLFW_MOUSE_BUTTON_LEFT) { + // Translate + mat4 vMat = translate(viewMat, + vec3(-delta[0], delta[1], 0.0f) * MOVE_SPEED); setCellViewMatrix(pXPos, pYPos, vMat); - } - } else if (mButton == GLFW_MOUSE_BUTTON_RIGHT) { - const glm::mat4 orientationMat = getCellOrientationMatrix(pXPos, pYPos); - - // Rotation - int width, height; - glfwGetWindowSize(mWindow, &width, &height); - - if (mLastXPos != pXPos || mLastYPos != pYPos) { - glm::vec3 op1 = trackballPoint(mLastXPos, mLastYPos, float(width), - float(height)); - glm::vec3 op2 = - trackballPoint(pXPos, pYPos, float(width), float(height)); - - float angle = std::acos(std::min(1.0f, glm::dot(op1, op2))); - - glm::vec3 axisInCamCoord = glm::cross(op1, op2); - - glm::mat3 camera2object = glm::inverse(glm::mat3(viewMat)); - glm::vec3 axisInObjCoord = camera2object * axisInCamCoord; - - glm::mat4 oMat = glm::rotate(orientationMat, glm::degrees(angle), - axisInObjCoord); - - setCellOrientationMatrix(pXPos, pYPos, oMat); + } else if (mButton == ZOOM_ENABLER) { + // Zoom + float dy = delta[1]; + if (!(std::abs(dy) < EPSILON)) { + if (dy < 0.0f) { dy = -1.0f / dy; } + mat4 vMat = scale(viewMat, vec3(pow(dy, ZOOM_SPEED))); + setCellViewMatrix(pXPos, pYPos, vMat); + } + } else if (mButton == GLFW_MOUSE_BUTTON_RIGHT) { + // Rotation + auto compCmp = epsilonNotEqual(mLastPos, currPos, vec2(EPSILON)); + if (compCmp[0] || compCmp[1]) { + const mat4 oMat = getCellOrientationMatrix(pXPos, pYPos); + + int view[4]; + glGetIntegerv(GL_VIEWPORT, view); + + auto rotParams = + calcRotationFromArcBall(mLastPos, currPos, make_vec4(view)); + + setCellOrientationMatrix( + pXPos, pYPos, + rotate(oMat, rotParams.second, rotParams.first)); + } } } - - mLastXPos = pXPos; - mLastYPos = pYPos; + mLastPos = currPos; } void Widget::mouseButtonHandler(int pButton, int pAction, int pMods) { + mButton = pButton; + mButtonAction = pAction; + + const bool isZoomModifierOn = pMods == GLFW_MOD_CONTROL; + double x, y; glfwGetCursorPos(mWindow, &x, &y); - mLastXPos = float(x); - mLastYPos = float(y); - - mButton = -1; - if (pAction == GLFW_PRESS) { - switch (pButton) { - case GLFW_MOUSE_BUTTON_LEFT: - mButton = GLFW_MOUSE_BUTTON_LEFT; - break; - case GLFW_MOUSE_BUTTON_RIGHT: - mButton = GLFW_MOUSE_BUTTON_RIGHT; - break; - case GLFW_MOUSE_BUTTON_MIDDLE: - mButton = GLFW_MOUSE_BUTTON_MIDDLE; - break; + auto pos = vec2(float(x), float(y)); + + if (mButtonAction == GLFW_PRESS) { + if (mButton == GLFW_MOUSE_BUTTON_RIGHT) { + glfwSetCursor(mWindow, mRotationCursor); + mRotationFlag = true; + } else if (mButton == GLFW_MOUSE_BUTTON_LEFT && isZoomModifierOn) { + glfwSetCursor(mWindow, mZoomCursor); } + mLastPos = pos; + } else if (mButtonAction == GLFW_RELEASE) { + mRotationFlag = false; + glfwSetCursor(mWindow, NULL); } - if (pMods == GLFW_MOD_ALT || pMods == GLFW_MOD_CONTROL) { - mButton += 10 * pMods; - } + mButton += (10 * pMods * isZoomModifierOn); + // reset UI transforms upon mouse middle click - if (pButton == GLFW_MOUSE_BUTTON_MIDDLE && pMods == GLFW_MOD_CONTROL && + if (pMods == GLFW_MOD_CONTROL && pButton == GLFW_MOUSE_BUTTON_MIDDLE && pAction == GLFW_PRESS) { - setCellViewMatrix(float(x), float(y), IDENTITY); - setCellOrientationMatrix(float(x), float(y), IDENTITY); + setCellViewMatrix(pos[0], pos[1], IDENTITY); + setCellOrientationMatrix(pos[0], pos[1], IDENTITY); + mButton = -1; + mButtonAction = -1; } } void Widget::pollEvents() { glfwPollEvents(); } -const glm::mat4 Widget::getViewMatrix(const CellIndex& pIndex) { +const mat4 Widget::getViewMatrix(const CellIndex& pIndex) { if (mViewMatrices.find(pIndex) == mViewMatrices.end()) { mViewMatrices.emplace(pIndex, IDENTITY); } return mViewMatrices[pIndex]; } -const glm::mat4 Widget::getOrientationMatrix(const CellIndex& pIndex) { +const mat4 Widget::getOrientationMatrix(const CellIndex& pIndex) { if (mOrientMatrices.find(pIndex) == mOrientMatrices.end()) { mOrientMatrices.emplace(pIndex, IDENTITY); } diff --git a/src/backend/opengl/glfw/window.hpp b/src/backend/opengl/glfw/window.hpp index 11556d52..47398b4c 100644 --- a/src/backend/opengl/glfw/window.hpp +++ b/src/backend/opengl/glfw/window.hpp @@ -29,11 +29,13 @@ void destroyWindowToolkit(); class Widget { private: GLFWwindow* mWindow; + GLFWcursor* mRotationCursor; + GLFWcursor* mZoomCursor; bool mClose; - float mLastXPos; - float mLastYPos; + glm::vec2 mLastPos; int mButton; - glm::vec3 mLastPos; + int mButtonAction; + bool mRotationFlag; forge::common::MatrixHashMap mViewMatrices; forge::common::MatrixHashMap mOrientMatrices; @@ -104,6 +106,8 @@ class Widget { const forge::common::CellIndex& pIndex); void resetViewMatrices(); void resetOrientationMatrices(); + + inline bool isBeingRotated() const { return mRotationFlag; } }; } // namespace wtk diff --git a/src/backend/opengl/histogram_impl.cpp b/src/backend/opengl/histogram_impl.cpp index 97f673b8..3b593955 100644 --- a/src/backend/opengl/histogram_impl.cpp +++ b/src/backend/opengl/histogram_impl.cpp @@ -160,5 +160,7 @@ void histogram_impl::render(const int pWindowId, const int pX, const int pY, CheckGL("End histogram_impl::render"); } +bool histogram_impl::isRotatable() const { return false; } + } // namespace opengl } // namespace forge diff --git a/src/backend/opengl/histogram_impl.hpp b/src/backend/opengl/histogram_impl.hpp index 307c6190..d8940b74 100644 --- a/src/backend/opengl/histogram_impl.hpp +++ b/src/backend/opengl/histogram_impl.hpp @@ -54,6 +54,8 @@ class histogram_impl : public AbstractRenderable { void render(const int pWindowId, const int pX, const int pY, const int pVPW, const int pVPH, const glm::mat4 &pView, const glm::mat4 &pOrient); + + bool isRotatable() const; }; } // namespace opengl diff --git a/src/backend/opengl/image_impl.cpp b/src/backend/opengl/image_impl.cpp index 18ce33b5..21125d0e 100644 --- a/src/backend/opengl/image_impl.cpp +++ b/src/backend/opengl/image_impl.cpp @@ -190,5 +190,7 @@ void image_impl::render(const int pWindowId, const int pX, const int pY, CheckGL("End image_impl::render"); } +bool image_impl::isRotatable() const { return false; } + } // namespace opengl } // namespace forge diff --git a/src/backend/opengl/image_impl.hpp b/src/backend/opengl/image_impl.hpp index 78acf8c5..67792362 100644 --- a/src/backend/opengl/image_impl.hpp +++ b/src/backend/opengl/image_impl.hpp @@ -67,6 +67,8 @@ class image_impl : public AbstractRenderable { void render(const int pWindowId, const int pX, const int pY, const int pVPW, const int pVPH, const glm::mat4 &pView, const glm::mat4 &pOrient); + + bool isRotatable() const; }; } // namespace opengl diff --git a/src/backend/opengl/plot_impl.cpp b/src/backend/opengl/plot_impl.cpp index fba8e00e..15a748c5 100644 --- a/src/backend/opengl/plot_impl.cpp +++ b/src/backend/opengl/plot_impl.cpp @@ -96,7 +96,8 @@ void plot_impl::bindDimSpecificUniforms() { plot_impl::plot_impl(const uint32_t pNumPoints, const forge::dtype pDataType, const forge::PlotType pPlotType, - const forge::MarkerType pMarkerType, const int pD) + const forge::MarkerType pMarkerType, const int pD, + const bool pIsInternalObject) : mDimension(pD) , mMarkerSize(12) , mNumPoints(pNumPoints) @@ -119,6 +120,8 @@ plot_impl::plot_impl(const uint32_t pNumPoints, const forge::dtype pDataType, , mPlotPointIndex(-1) , mPlotColorIndex(-1) , mPlotAlphaIndex(-1) + , mPlotAssistDrawFlagIndex(-1) + , mPlotLineColorIndex(-1) , mMarkerPVCOnIndex(-1) , mMarkerPVAOnIndex(-1) , mMarkerTypeIndex(-1) @@ -131,13 +134,17 @@ plot_impl::plot_impl(const uint32_t pNumPoints, const forge::dtype pDataType, CheckGL("Begin plot_impl::plot_impl"); setColor(0, 1, 0, 1); + if (pIsInternalObject) { markAsInternalObject(); } if (mDimension == 2) { mPlotUColorIndex = mPlotProgram.getUniformLocation("barColor"); mVBOSize = 2 * mNumPoints; } else { - mPlotRangeIndex = mPlotProgram.getUniformLocation("minmaxs"); - mVBOSize = 3 * mNumPoints; + mPlotRangeIndex = mPlotProgram.getUniformLocation("minmaxs"); + mPlotLineColorIndex = mPlotProgram.getUniformLocation("lineColor"); + mPlotAssistDrawFlagIndex = + mPlotProgram.getUniformLocation("isAssistDraw"); + mVBOSize = 3 * mNumPoints; } mCBOSize = 3 * mNumPoints; @@ -229,6 +236,8 @@ void plot_impl::render(const int pWindowId, const int pX, const int pY, glm::value_ptr(viewModelMatrix)); glUniform1i(mPlotPVCOnIndex, mIsPVCOn); glUniform1i(mPlotPVAOnIndex, mIsPVAOn); + glUniform1i(mPlotAssistDrawFlagIndex, mIsInternalObject); + glUniform4fv(mPlotLineColorIndex, 1, mColor); plot_impl::bindResources(pWindowId); glDrawArrays(GL_LINE_STRIP, 0, mNumPoints); @@ -265,6 +274,8 @@ void plot_impl::render(const int pWindowId, const int pX, const int pY, CheckGL("End plot_impl::render"); } +bool plot_impl::isRotatable() const { return true; } + glm::mat4 plot2d_impl::computeTransformMat(const glm::mat4& pView, const glm::mat4& /*pOrient*/) { float xRange = mRange[1] - mRange[0]; @@ -287,5 +298,7 @@ void plot2d_impl::bindDimSpecificUniforms() { glUniform4fv(mPlotUColorIndex, 1, mColor); } +bool plot2d_impl::isRotatable() const { return false; } + } // namespace opengl } // namespace forge diff --git a/src/backend/opengl/plot_impl.hpp b/src/backend/opengl/plot_impl.hpp index 87316239..0a16f8ca 100644 --- a/src/backend/opengl/plot_impl.hpp +++ b/src/backend/opengl/plot_impl.hpp @@ -43,6 +43,8 @@ class plot_impl : public AbstractRenderable { uint32_t mPlotPointIndex; uint32_t mPlotColorIndex; uint32_t mPlotAlphaIndex; + uint32_t mPlotAssistDrawFlagIndex; + uint32_t mPlotLineColorIndex; uint32_t mMarkerPVCOnIndex; uint32_t mMarkerPVAOnIndex; @@ -72,7 +74,8 @@ class plot_impl : public AbstractRenderable { public: plot_impl(const uint32_t pNumPoints, const forge::dtype pDataType, const forge::PlotType pPlotType, - const forge::MarkerType pMarkerType, const int pDimension = 3); + const forge::MarkerType pMarkerType, const int pDimension = 3, + const bool pIsInternalObject = false); ~plot_impl(); void setMarkerSize(const float pMarkerSize); @@ -83,6 +86,8 @@ class plot_impl : public AbstractRenderable { virtual void render(const int pWindowId, const int pX, const int pY, const int pVPW, const int pVPH, const glm::mat4& pView, const glm::mat4& pOrient); + + virtual bool isRotatable() const; }; class plot2d_impl : public plot_impl { @@ -98,6 +103,8 @@ class plot2d_impl : public plot_impl { const forge::PlotType pPlotType, const forge::MarkerType pMarkerType) : plot_impl(pNumPoints, pDataType, pPlotType, pMarkerType, 2) {} + + bool isRotatable() const; }; } // namespace opengl diff --git a/src/backend/opengl/sdl/window.cpp b/src/backend/opengl/sdl/window.cpp index 1911bb67..bb087099 100644 --- a/src/backend/opengl/sdl/window.cpp +++ b/src/backend/opengl/sdl/window.cpp @@ -12,13 +12,21 @@ #include #include +#include #include +#include -#include +#include +using glm::epsilonNotEqual; +using glm::make_vec4; +using glm::mat4; using glm::rotate; using glm::scale; using glm::translate; +using glm::vec2; +using glm::vec3; + using namespace forge::common; #define SDL_THROW_ERROR(msg, err) FG_ERROR("Window constructor " #msg, err) @@ -35,11 +43,11 @@ void initWindowToolkit() { void destroyWindowToolkit() { SDL_Quit(); } -const glm::mat4 Widget::findTransform(const MatrixHashMap& pMap, const float pX, - const float pY) { +const mat4 Widget::findTransform(const MatrixHashMap& pMap, const float pX, + const float pY) { for (auto it : pMap) { const CellIndex& idx = it.first; - const glm::mat4& mat = it.second; + const mat4& mat = it.second; const int rows = std::get<0>(idx); const int cols = std::get<1>(idx); @@ -56,18 +64,17 @@ const glm::mat4 Widget::findTransform(const MatrixHashMap& pMap, const float pX, return IDENTITY; } -const glm::mat4 Widget::getCellViewMatrix(const float pXPos, - const float pYPos) { +const mat4 Widget::getCellViewMatrix(const float pXPos, const float pYPos) { return findTransform(mViewMatrices, pXPos, pYPos); } -const glm::mat4 Widget::getCellOrientationMatrix(const float pXPos, - const float pYPos) { +const mat4 Widget::getCellOrientationMatrix(const float pXPos, + const float pYPos) { return findTransform(mOrientMatrices, pXPos, pYPos); } void Widget::setTransform(MatrixHashMap& pMap, const float pX, const float pY, - const glm::mat4& pMat) { + const mat4& pMat) { for (auto it : pMap) { const CellIndex& idx = it.first; @@ -85,12 +92,12 @@ void Widget::setTransform(MatrixHashMap& pMap, const float pX, const float pY, } void Widget::setCellViewMatrix(const float pXPos, const float pYPos, - const glm::mat4& pMatrix) { + const mat4& pMatrix) { return setTransform(mViewMatrices, pXPos, pYPos, pMatrix); } void Widget::setCellOrientationMatrix(const float pXPos, const float pYPos, - const glm::mat4& pMatrix) { + const mat4& pMatrix) { return setTransform(mOrientMatrices, pXPos, pYPos, pMatrix); } @@ -104,16 +111,26 @@ void Widget::resetOrientationMatrices() { Widget::Widget() : mWindow(nullptr) + , mDefaultCursor(nullptr) + , mRotationCursor(nullptr) + , mZoomCursor(nullptr) + , mMoveCursor(nullptr) , mClose(false) - , mLastXPos(0) - , mLastYPos(0) - , mButton(-1) + , mLastPos(0, 0) + , mRotationFlag(false) , mWidth(512) , mHeight(512) {} Widget::Widget(int pWidth, int pHeight, const char* pTitle, const std::unique_ptr& pWidget, const bool invisible) - : mWindow(nullptr), mClose(false), mLastXPos(0), mLastYPos(0), mButton(-1) { + : mWindow(nullptr) + , mDefaultCursor(nullptr) + , mRotationCursor(nullptr) + , mZoomCursor(nullptr) + , mMoveCursor(nullptr) + , mClose(false) + , mLastPos(0, 0) + , mRotationFlag(false) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, @@ -151,11 +168,19 @@ Widget::Widget(int pWidth, int pHeight, const char* pTitle, SDL_GL_SetSwapInterval(1); mWindowId = SDL_GetWindowID(mWindow); SDL_GetWindowSize(mWindow, &mWidth, &mHeight); + + // Set Hand cursor for Rotation and Zoom Modes + mDefaultCursor = SDL_GetDefaultCursor(); + mRotationCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); + mZoomCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); + mMoveCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); } Widget::~Widget() { - SDL_GL_DeleteContext(mContext); - SDL_DestroyWindow(mWindow); + if (mContext) SDL_GL_DeleteContext(mContext); + if (mWindow) SDL_DestroyWindow(mWindow); + if (mRotationCursor) SDL_FreeCursor(mRotationCursor); + if (mZoomCursor) SDL_FreeCursor(mZoomCursor); } SDL_Window* Widget::getNativeHandle() const { return mWindow; } @@ -205,14 +230,13 @@ void Widget::resetCloseFlag() { } void Widget::pollEvents() { - static const float SPEED = 0.005f; SDL_Event evnt; - while (SDL_PollEvent(&evnt)) { /* handle window events that are triggered when the window with window id 'mWindowId' is in focus */ if (evnt.key.windowID == mWindowId) { + // Window Events if (evnt.type == SDL_WINDOWEVENT) { switch (evnt.window.event) { case SDL_WINDOWEVENT_CLOSE: mClose = true; break; @@ -222,98 +246,95 @@ void Widget::pollEvents() { break; } } - + // Keyboard Events if (evnt.type == SDL_KEYDOWN) { switch (evnt.key.keysym.sym) { case SDLK_ESCAPE: mClose = true; break; - default: mMod = evnt.key.keysym.sym; break; + default: break; } - } else if (evnt.type == SDL_KEYUP) { - mMod = -1; } - - int x, y; - SDL_GetMouseState(&x, &y); - // reset UI transforms upon mouse middle click - if (evnt.type == SDL_MOUSEBUTTONUP) { - if (evnt.button.button == SDL_BUTTON_MIDDLE && - (mMod == SDLK_LCTRL || mMod == SDLK_RCTRL)) { - setCellViewMatrix(x, y, IDENTITY); - setCellOrientationMatrix(x, y, IDENTITY); - } - } - - const glm::mat4 viewMat = getCellViewMatrix(x, y); + // Mouse Events + auto kbState = SDL_GetKeyboardState(NULL); + bool isCtrl = + kbState[SDL_SCANCODE_LCTRL] || kbState[SDL_SCANCODE_RCTRL]; if (evnt.type == SDL_MOUSEMOTION) { + auto currPos = vec2(evnt.motion.x, evnt.motion.y); if (evnt.motion.state == SDL_BUTTON_LMASK) { - double deltaX = -evnt.motion.xrel; - double deltaY = -evnt.motion.yrel; - - glm::mat4 vMat(1); - - if (mMod == SDLK_LALT || mMod == SDLK_RALT || - mMod == SDLK_LCTRL || mMod == SDLK_RCTRL) { + auto viewMat = getCellViewMatrix(currPos[0], currPos[1]); + auto delta = mLastPos - currPos; + if (isCtrl) { // Zoom - if (deltaY != 0) { - if (deltaY < 0) { deltaY = 1.0 / (-deltaY); } - vMat = - scale(viewMat, glm::vec3(pow(deltaY, SPEED))); + float dy = delta[1]; + if (!(std::abs(dy) < EPSILON)) { + if (dy < 0.0f) { dy = -1.0 / dy; } + mat4 vMat = + scale(viewMat, vec3(pow(dy, ZOOM_SPEED))); + setCellViewMatrix(currPos[0], currPos[1], vMat); } } else { // Translate - vMat = translate( - viewMat, glm::vec3(-deltaX, deltaY, 0.0f) * SPEED); + mat4 vMat = + translate(viewMat, vec3(-delta[0], delta[1], 0.0f) * + MOVE_SPEED); + setCellViewMatrix(currPos[0], currPos[1], vMat); } - setCellViewMatrix(x, y, vMat); } else if (evnt.motion.state == SDL_BUTTON_RMASK) { - const glm::mat4 orientationMat = - getCellOrientationMatrix(x, y); // Rotations - int width, height; - SDL_GetWindowSize(mWindow, &width, &height); - - int xPos = evnt.motion.x; - int yPos = evnt.motion.y; - - if (mLastXPos != xPos || mLastYPos != yPos) { - glm::vec3 op1 = - trackballPoint(mLastXPos, mLastYPos, width, height); - glm::vec3 op2 = - trackballPoint(xPos, yPos, width, height); - - float angle = - std::acos(std::min(1.0f, glm::dot(op1, op2))); - - glm::vec3 axisInCamCoord = glm::cross(op1, op2); - - glm::mat3 camera2object = - glm::inverse(glm::mat3(viewMat)); - glm::vec3 axisInObjCoord = - camera2object * axisInCamCoord; - - glm::mat4 oMat = - glm::rotate(orientationMat, glm::degrees(angle), - axisInObjCoord); - setCellOrientationMatrix(x, y, oMat); + auto compCmp = + epsilonNotEqual(mLastPos, currPos, vec2(EPSILON)); + if (compCmp[0] || compCmp[1]) { + const mat4 oMat = + getCellOrientationMatrix(currPos[0], currPos[1]); + int view[4]; + glGetIntegerv(GL_VIEWPORT, view); + + auto rotParams = calcRotationFromArcBall( + mLastPos, currPos, make_vec4(view)); + + setCellOrientationMatrix( + currPos[0], currPos[1], + rotate(oMat, rotParams.second, rotParams.first)); } } - - mLastXPos = evnt.motion.x; - mLastYPos = evnt.motion.y; + mLastPos = currPos; + } else if (evnt.type == SDL_MOUSEBUTTONDOWN) { + auto button = evnt.button.button; + if (button == SDL_BUTTON_LEFT && isCtrl) { + // Zoom left mouse button special case first + SDL_SetCursor(mZoomCursor); + } else if (button == SDL_BUTTON_LEFT) { + // Translation + SDL_SetCursor(mMoveCursor); + } else if (button == SDL_BUTTON_RIGHT) { + // Rotation + mRotationFlag = true; + SDL_SetCursor(mRotationCursor); + } else if (button == SDL_BUTTON_MIDDLE && isCtrl) { + // reset UI transforms upon mouse middle click + setCellViewMatrix(evnt.button.x, evnt.button.y, IDENTITY); + setCellOrientationMatrix(evnt.button.x, evnt.button.y, + IDENTITY); + mRotationFlag = false; + SDL_SetCursor(mDefaultCursor); + } + mLastPos = vec2(evnt.button.x, evnt.button.y); + } else if (evnt.type == SDL_MOUSEBUTTONUP) { + mRotationFlag = false; + SDL_SetCursor(mDefaultCursor); } } } } -const glm::mat4 Widget::getViewMatrix(const CellIndex& pIndex) { +const mat4 Widget::getViewMatrix(const CellIndex& pIndex) { if (mViewMatrices.find(pIndex) == mViewMatrices.end()) { mViewMatrices.emplace(pIndex, IDENTITY); } return mViewMatrices[pIndex]; } -const glm::mat4 Widget::getOrientationMatrix(const CellIndex& pIndex) { +const mat4 Widget::getOrientationMatrix(const CellIndex& pIndex) { if (mOrientMatrices.find(pIndex) == mOrientMatrices.end()) { mOrientMatrices.emplace(pIndex, IDENTITY); } diff --git a/src/backend/opengl/sdl/window.hpp b/src/backend/opengl/sdl/window.hpp index d67a9d8d..687888d1 100644 --- a/src/backend/opengl/sdl/window.hpp +++ b/src/backend/opengl/sdl/window.hpp @@ -29,13 +29,14 @@ class Widget { private: SDL_Window* mWindow; SDL_GLContext mContext; + SDL_Cursor* mDefaultCursor; + SDL_Cursor* mRotationCursor; + SDL_Cursor* mZoomCursor; + SDL_Cursor* mMoveCursor; bool mClose; uint32_t mWindowId; - float mLastXPos; - float mLastYPos; - int mButton; - SDL_Keycode mMod; - glm::vec3 mLastPos; + glm::vec2 mLastPos; + bool mRotationFlag; forge::common::MatrixHashMap mViewMatrices; forge::common::MatrixHashMap mOrientMatrices; @@ -102,6 +103,8 @@ class Widget { const forge::common::CellIndex& pIndex); void resetViewMatrices(); void resetOrientationMatrices(); + + inline bool isBeingRotated() const { return mRotationFlag; } }; } // namespace wtk diff --git a/src/backend/opengl/surface_impl.cpp b/src/backend/opengl/surface_impl.cpp index b7a8d7e6..65fa91fa 100644 --- a/src/backend/opengl/surface_impl.cpp +++ b/src/backend/opengl/surface_impl.cpp @@ -121,6 +121,8 @@ void surface_impl::renderGraph(const int pWindowId, glUniform2fv(mSurfRangeIndex, 3, mRange); glUniform1i(mSurfPVCIndex, mIsPVCOn); glUniform1i(mSurfPVAIndex, mIsPVAOn); + glUniform1i(mSurfAssistDrawFlagIndex, false); + glUniform4fv(mSurfUniformColorIndex, 1, mColor); bindResources(pWindowId); glDrawElements(GL_TRIANGLE_STRIP, GLsizei(mIBOSize), GL_UNSIGNED_INT, @@ -174,7 +176,9 @@ surface_impl::surface_impl(unsigned pNumXPoints, unsigned pNumYPoints, , mSurfColorIndex(-1) , mSurfAlphaIndex(-1) , mSurfPVCIndex(-1) - , mSurfPVAIndex(-1) { + , mSurfPVAIndex(-1) + , mSurfUniformColorIndex(-1) + , mSurfAssistDrawFlagIndex(-1) { CheckGL("Begin surface_impl::surface_impl"); setColor(0.9f, 0.5f, 0.6f, 1.0f); @@ -187,13 +191,15 @@ surface_impl::surface_impl(unsigned pNumXPoints, unsigned pNumYPoints, mMarkerColorIndex = mMarkerProgram.getAttributeLocation("color"); mMarkerAlphaIndex = mMarkerProgram.getAttributeLocation("alpha"); - mSurfMatIndex = mSurfProgram.getUniformLocation("transform"); - mSurfRangeIndex = mSurfProgram.getUniformLocation("minmaxs"); - mSurfPVCIndex = mSurfProgram.getUniformLocation("isPVCOn"); - mSurfPVAIndex = mSurfProgram.getUniformLocation("isPVAOn"); - mSurfPointIndex = mSurfProgram.getAttributeLocation("point"); - mSurfColorIndex = mSurfProgram.getAttributeLocation("color"); - mSurfAlphaIndex = mSurfProgram.getAttributeLocation("alpha"); + mSurfMatIndex = mSurfProgram.getUniformLocation("transform"); + mSurfRangeIndex = mSurfProgram.getUniformLocation("minmaxs"); + mSurfPVCIndex = mSurfProgram.getUniformLocation("isPVCOn"); + mSurfPVAIndex = mSurfProgram.getUniformLocation("isPVAOn"); + mSurfPointIndex = mSurfProgram.getAttributeLocation("point"); + mSurfColorIndex = mSurfProgram.getAttributeLocation("color"); + mSurfAlphaIndex = mSurfProgram.getAttributeLocation("alpha"); + mSurfUniformColorIndex = mSurfProgram.getUniformLocation("lineColor"); + mSurfAssistDrawFlagIndex = mSurfProgram.getUniformLocation("isAssistDraw"); unsigned totalPoints = mNumXPoints * mNumYPoints; diff --git a/src/backend/opengl/surface_impl.hpp b/src/backend/opengl/surface_impl.hpp index c5977177..3a13dbdc 100644 --- a/src/backend/opengl/surface_impl.hpp +++ b/src/backend/opengl/surface_impl.hpp @@ -47,6 +47,8 @@ class surface_impl : public AbstractRenderable { uint32_t mSurfAlphaIndex; uint32_t mSurfPVCIndex; uint32_t mSurfPVAIndex; + uint32_t mSurfUniformColorIndex; + uint32_t mSurfAssistDrawFlagIndex; std::map mVAOMap; @@ -75,6 +77,8 @@ class surface_impl : public AbstractRenderable { inline void usePerVertexAlphas(const bool pFlag = true) { mIsPVAOn = pFlag; } + + bool isRotatable() const { return true; } }; class scatter3_impl : public surface_impl { diff --git a/src/backend/opengl/vector_field_impl.cpp b/src/backend/opengl/vector_field_impl.cpp index 84aad9cb..f4e51f77 100644 --- a/src/backend/opengl/vector_field_impl.cpp +++ b/src/backend/opengl/vector_field_impl.cpp @@ -216,6 +216,8 @@ void vector_field_impl::render(const int pWindowId, const int pX, const int pY, CheckGL("End vector_field_impl::render"); } +bool vector_field_impl::isRotatable() const { return true; } + glm::mat4 vector_field2d_impl::computeModelMatrix( const glm::mat4& /*pOrient*/) { float xRange = mRange[1] - mRange[0]; diff --git a/src/backend/opengl/vector_field_impl.hpp b/src/backend/opengl/vector_field_impl.hpp index de0859cb..dd8948d2 100644 --- a/src/backend/opengl/vector_field_impl.hpp +++ b/src/backend/opengl/vector_field_impl.hpp @@ -64,6 +64,8 @@ class vector_field_impl : public AbstractRenderable { virtual void render(const int pWindowId, const int pX, const int pY, const int pVPW, const int pVPH, const glm::mat4& pView, const glm::mat4& pOrient); + + virtual bool isRotatable() const; }; class vector_field2d_impl : public vector_field_impl { @@ -73,6 +75,8 @@ class vector_field2d_impl : public vector_field_impl { public: vector_field2d_impl(const uint32_t pNumPoints, const forge::dtype pDataType) : vector_field_impl(pNumPoints, pDataType, 2) {} + + bool isRotatable() const { return false; } }; } // namespace opengl diff --git a/src/backend/opengl/window_impl.cpp b/src/backend/opengl/window_impl.cpp index 0f31c95f..0d3fedf3 100644 --- a/src/backend/opengl/window_impl.cpp +++ b/src/backend/opengl/window_impl.cpp @@ -11,15 +11,20 @@ // https://gist.github.com/SnopyDogy/a9a22497a893ec86aa3e #include +#include #include #include #include +#include #include +#include using namespace forge; using namespace forge::common; +using std::vector; + namespace forge { #ifdef USE_FREEIMAGE @@ -87,6 +92,44 @@ void destroyWtkIfDone() { namespace opengl { +void window_impl::prepArcBallObjects() { + constexpr double angleStep = + (2 * PI) / static_cast(ARCBALL_CIRCLE_POINTS); + + vector loop0(3 * (ARCBALL_CIRCLE_POINTS + 1)), + loop1(3 * (ARCBALL_CIRCLE_POINTS + 1)); + int i = 0; + while (i < ARCBALL_CIRCLE_POINTS) { + loop0[3 * i + 0] = ARC_BALL_RADIUS * cos(i * angleStep); + loop0[3 * i + 1] = ARC_BALL_RADIUS * sin(i * angleStep); + loop0[3 * i + 2] = 0.0f; + loop1[3 * i + 0] = 0.0f; + loop1[3 * i + 1] = ARC_BALL_RADIUS * cos(i * angleStep); + loop1[3 * i + 2] = ARC_BALL_RADIUS * sin(i * angleStep); + i++; + } + // Since plot_impl's FG_PLOT_LINE is GL_LINE_STRIP + // Add the first point again to make it a loop + loop0[3 * i + 0] = ARC_BALL_RADIUS; + loop0[3 * i + 1] = 0.0f; + loop0[3 * i + 2] = 0.0f; + loop1[3 * i + 0] = 0.0f; + loop1[3 * i + 1] = ARC_BALL_RADIUS; + loop1[3 * i + 2] = 0.0f; + + // Set Loop Colors + mArcBallLoop0->setRanges(-1.0f, 1.0f, -1.0f, 1.0, -1.0f, 1.0f); + mArcBallLoop1->setRanges(-1.0f, 1.0f, -1.0f, 1.0, -1.0f, 1.0f); + mArcBallLoop0->setColor(AF_BLUE[0], AF_BLUE[1], AF_BLUE[2], AF_BLUE[3]); + mArcBallLoop1->setColor(AF_BLUE[0], AF_BLUE[1], AF_BLUE[2], AF_BLUE[3]); + + // Update the respective plot_impl OpenGL buffers + fg_update_vertex_buffer(mArcBallLoop0->vbo(), mArcBallLoop0->vboSize(), + loop0.data()); + fg_update_vertex_buffer(mArcBallLoop1->vbo(), mArcBallLoop1->vboSize(), + loop1.data()); +} + window_impl::window_impl(int pWidth, int pHeight, const char* pTitle, std::weak_ptr pWindow, const bool invisible) @@ -120,6 +163,13 @@ window_impl::window_impl(int pWidth, int pHeight, const char* pTitle, mCMap = std::make_shared(); } + /* Create a plot for rendering Arc Ball orthogonal circles */ + mArcBallLoop0 = std::make_shared( + ARCBALL_CIRCLE_POINTS + 1, f32, FG_PLOT_LINE, FG_MARKER_NONE, 3, true); + mArcBallLoop1 = std::make_shared( + ARCBALL_CIRCLE_POINTS + 1, f32, FG_PLOT_LINE, FG_MARKER_NONE, 3, true); + prepArcBallObjects(); + /* set the colormap to default */ mColorMapUBO = mCMap->cmapUniformBufferId(FG_COLOR_MAP_DEFAULT); mUBOSize = mCMap->cmapLength(FG_COLOR_MAP_DEFAULT); @@ -208,7 +258,17 @@ void window_impl::draw(const std::shared_ptr& pRenderable) { pRenderable->setColorMapUBOParams(mColorMapUBO, mUBOSize); pRenderable->render(mID, 0, 0, mWidget->mWidth, mWidget->mHeight, viewMatrix, orientMatrix); - + // Render Arcball + if (pRenderable->isRotatable() && mWidget->isBeingRotated()) { + // TODO FIXME Figure out a better way to + // render arc ball loops to include depth test for any + // objects inside it + glClear(GL_DEPTH_BUFFER_BIT); + mArcBallLoop0->render(mID, 0, 0, mWidget->mWidth, mWidget->mHeight, + IDENTITY, orientMatrix); + mArcBallLoop1->render(mID, 0, 0, mWidget->mWidth, mWidget->mHeight, + IDENTITY, orientMatrix); + } mWidget->swapBuffers(); mWidget->pollEvents(); CheckGL("End window_impl::draw"); diff --git a/src/backend/opengl/window_impl.hpp b/src/backend/opengl/window_impl.hpp index 6e865421..dd39364f 100644 --- a/src/backend/opengl/window_impl.hpp +++ b/src/backend/opengl/window_impl.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -31,10 +32,14 @@ class window_impl { std::shared_ptr mFont; std::shared_ptr mCMap; + std::shared_ptr mArcBallLoop0; + std::shared_ptr mArcBallLoop1; uint32_t mColorMapUBO; uint32_t mUBOSize; + void prepArcBallObjects(); + public: window_impl(int pWidth, int pHeight, const char* pTitle, std::weak_ptr pWindow,