From 737e41887811766c4021b44858bbfebbd4a93e69 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 13 Jun 2015 10:02:54 -0700 Subject: [PATCH 01/35] Revert "Revert "Merge pull request #5106 from jherico/render_cursor"" This reverts commit 0cdc2b53fe7815bba21a98ecbdfd91bcbb2898f5. --- interface/resources/images/arrow.png | Bin 0 -> 3562 bytes interface/src/Application.cpp | 3 +- interface/src/devices/OculusManager.cpp | 5 +- interface/src/devices/TV3DManager.cpp | 18 +- interface/src/ui/ApplicationOverlay.cpp | 642 ++++++------------ interface/src/ui/ApplicationOverlay.h | 53 +- libraries/render-utils/src/GeometryCache.cpp | 15 + libraries/render-utils/src/GeometryCache.h | 3 + .../render-utils/src/standardDrawTexture.slf | 24 + .../src/standardTransformPNTC.slv | 33 + 10 files changed, 327 insertions(+), 469 deletions(-) create mode 100644 interface/resources/images/arrow.png create mode 100644 libraries/render-utils/src/standardDrawTexture.slf create mode 100644 libraries/render-utils/src/standardTransformPNTC.slv diff --git a/interface/resources/images/arrow.png b/interface/resources/images/arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..408881b5856eb47a30875e3e56d5864721161587 GIT binary patch literal 3562 zcmdUt`#;nD8^_v>&Q++7{9N_Zsz0M==* zod*CASSkfH0KhjS_b&hd`q(~4J0M#M?u(ox0DvNG?~zad?EY9Ph;*vvAOHXp=Hz0J z=|L}3w2K>L>R8^PZ{I7O2$vdaap<*#Uu|_gIy~UBicJicDlZ z#oT*?_=N1+o**hKQJS&OwQ2S|FMIjks+^{Z*q`l9A9H%nCjQ}c>;Esnf^cn5k*X9 zDtIJvM-E6hnmauB8V%s$m0YR(j6(AOYaQ=Zxaf|nXhd}U)5Il`{<|J=u`8-l2Sk+)w2fZTFpVq+^VNbOIHDWEzXw;!TEupOl!#6bSK1;*{YJ|c(d%UezwJosB zc=QMJ+fywpC=OIGT`wp7eo4?o-Kyti6+y{Jdkx&njTs4=L~;x?`X$w+4)SuSIXQJ7 z;%Z)Hey~5FtC%13ZoZkBJQ8*cCYk7&2G7(TD)XtyWAc3?k#+2 zSg#k#mdRYRgv~@}Xlfje8{_+BNfv2R`Fs}BbLcl>eN-Ll zMq8jYR7x=HKDvahQ;p)T@CmDn4b1Nh(-MoAGg{jUe@WM|HsDuQOMVVAOFPKspjE0i z^C8vZ%pJ%+uK%uMP2K<22L?4~Io;K?zlnUro6GjvZ49$6g1A#&a2?86$9#wCOcS%C zb7}sG62)C19qs#(J5bIzi7QG=>-u$yEsR=h>f(-34m0xR(${nu&{RhA&@Mg7`B#`P zhV5a20jVJ##O)R@7r(@&R%V1)k5!_PR;jZ%Nic^nQe9LZq&r0-(CXstay#9kZWT(y zdgH7OM)5F71^WF%`v@Z|XC$dE{=2L=mdYTn>$X5itWF7S%qGfL>f`G0z@vPRSW|2% zzLM%PjXHas*`f@r_~wQ}Gmu43 z5G-)iN^`2uk6mWYBtokb!0*#IwiWpKfC!6##XRQBC1`bV$0fxT!VRorXxhHGe*NoA zdt6ZxwAvV!ZiN`4;o>WhDAGg8(B`wek@fWhHJ(dW4MtCkbqC#y7JcedE@AK> zMB$xO&3k{;t&x;emmKUb zowqJ(lDd|Eb9|sC!Ygpx87ARjG#b5XFF{X9cXw1L?RMHVsRnMv$I0lQd^=+#V#@$MlpA5irYfng==?oLWQ=QK^D$Ky_U!*jQH#m zjVjH$7E1+|ase%BK+M|o{iTaQ(Ml4H1$pu8=4el6V&M__e7G&_O(9<;GE^Al=TpLf zxO&AC{>m}9nRav|v8Os%CgDtTL=_WpJt#}L4rmX;ZoUwBsJ>kVxxfDKL-u*4inEt~ zepM8kiQ750nR9cof?+F3&-L4?2*iEAb9@@Ud-g}wloO$m!0J7N70PeSEAq6p3OKcD zep<3P?;@P-BzLYtBN~F-LxAwG5KLr9GB*Xf%Wrfd?2tQ{5+_qIEe`S;h*BXPA9!7* z{O#kEZB7c7JjG)*ytp`?abYuf2GbW035JF@wq+^JD38vuuKEw%F$L!JOIq~=P8ymV zjt6yfX60aCNrHV1;cN&@dcdu!k|na6-H;<1X+!&G)r4i5?y*WzOxI>K;!yduCwQRw zp)oRGk=9n3Lq4Fc>bY<x^GMN*P>x zcT=W7G2!>9k+a{t5kw;+2YqAccKgH6tOWRO&IL`{vOX8<_+#gs2a?glJn|}=3`vz{ zHAviptXq=nB%o57Oo#&v(9S0q<1wQCaMM$X_~?zvlB$ z{BS{j>8BHv>(J^Cw&VWjNl%$l^~FK0xgm}RHN;P_yMCbJkgqy!`%3TxmFo>4xWOMOk)+EW^_O9C7dZ>}I8Kh? zj}RS#{?`Mw|KiNv1NTC!8{_L%x!B03r-qWd`3rX$w%2JIqbaP0;T@MIu#e4!O_jj9 z<#;=AYz=r`={Y4aD2T>=*o5mw?KfuiR+fF=OW1)P@;v$;gLp~OWmI`C8Lwoc_?A2G z8WZjK)fU#5&sGhWjChlZ8*bHMkESnnvKsURQ^7~i%5%;9utUpqF}fdSqfACu_qEX4 zp@vXlm3Du40m?O>|E>T2`lg zkA8{BTL{+P!L`w6k!iPQ#a#f=|m#Qf(WK|Ir2F`ayL>?xU&W1IW5W%|H^ z!@V6^Km}^O#=A4H!qZs`D;m4uMCc;TUi*(Pr73>oa{>8VKq2hz#bW*--8Nf%b$@+! zHoF^Jp|5>CN4DvW)pU)?%g3N~Y6FGpe0l}{L^6G`j-fi~Y@`DQ5BtMg5BbtdGn`80 zlhZ=aS9n=RRb*}nf8eMeJDEc8%&t(^e!P8ZC+e-xs;70|LZCeN?35MHzihjDJDea% z_nQ6_6TRHs&%{vEqnm%C6bpLLKSPBjQ&ibGJ*2EF0a>wL|BHog#{CKrZI>`=PhmuX z1|o8of6J{XA8}XluF6Ue3=0>|PEYY5F8#6~g%gS*=5Vs(@K@8a<4r38-e(uhuy6R~b&w4cQHxqLICs#~XeF!9xRs?fdLjDOKWTh7 R^nCyTaI$x`E7(oB_+R=!c8UN1 literal 0 HcmV?d00001 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 22cbad6775..0757e6790f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -955,6 +955,7 @@ void Application::paintGL() { glPushMatrix(); glLoadIdentity(); displaySide(&renderArgs, _myCamera); + _applicationOverlay.displayOverlayTexture(&renderArgs); glPopMatrix(); renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; @@ -974,8 +975,6 @@ void Application::paintGL() { 0, 0, _glWidget->getDeviceSize().width(), _glWidget->getDeviceSize().height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - - _applicationOverlay.displayOverlayTexture(); } if (!OculusManager::isConnected() || OculusManager::allowSwap()) { diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index a7383ae4bb..414c7f6199 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -615,12 +615,10 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const renderArgs->_renderSide = RenderArgs::MONO; qApp->displaySide(renderArgs, *_camera, false); - qApp->getApplicationOverlay().displayOverlayTextureHmd(*_camera); + qApp->getApplicationOverlay().displayOverlayTextureHmd(renderArgs, *_camera); }); _activeEye = ovrEye_Count; - glPopMatrix(); - gpu::FramebufferPointer finalFbo; //Bind the output texture from the glow shader. If glow effect is disabled, we just grab the texture if (Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect)) { @@ -631,6 +629,7 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const finalFbo = DependencyManager::get()->getPrimaryFramebuffer(); glBindFramebuffer(GL_FRAMEBUFFER, 0); } + glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index 09edb03e5a..b0a2ff7e3b 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -12,6 +12,7 @@ #include "InterfaceConfig.h" #include +#include #include #include "gpu/GLBackend.h" @@ -106,21 +107,20 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) { _activeEye = &eye; glViewport(portalX, portalY, portalW, portalH); glScissor(portalX, portalY, portalW, portalH); + + glm::mat4 projection = glm::frustum(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ); + float fov = atan(1.0f / projection[1][1]); + projection = glm::translate(projection, vec3(eye.modelTranslation, 0, 0)); + eyeCamera.setProjection(projection); + glMatrixMode(GL_PROJECTION); glLoadIdentity(); // reset projection matrix - glFrustum(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ); // set left view frustum - GLfloat p[4][4]; - // Really? - glGetFloatv(GL_PROJECTION_MATRIX, &(p[0][0])); - float cotangent = p[1][1]; - GLfloat fov = atan(1.0f / cotangent); - glTranslatef(eye.modelTranslation, 0.0, 0.0); // translate to cancel parallax - + glLoadMatrixf(glm::value_ptr(projection)); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); renderArgs->_renderSide = RenderArgs::MONO; qApp->displaySide(renderArgs, eyeCamera, false); - qApp->getApplicationOverlay().displayOverlayTextureStereo(whichCamera, _aspect, fov); + qApp->getApplicationOverlay().displayOverlayTexture(renderArgs); _activeEye = NULL; }, [&]{ // render right side view diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 0c1783f8ac..ba0d3a60a9 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -14,13 +14,16 @@ #include #include +#include + #include +#include #include -#include #include #include -#include #include +#include +#include #include "AudioClient.h" #include "audio/AudioIOStatsRenderer.h" @@ -33,6 +36,9 @@ #include "Util.h" #include "ui/Stats.h" +#include "../../libraries/render-utils/standardTransformPNTC_vert.h" +#include "../../libraries/render-utils/standardDrawTexture_frag.h" + // Used to animate the magnification windows const float MAG_SPEED = 0.08f; @@ -114,27 +120,6 @@ bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, } } -void ApplicationOverlay::renderReticle(glm::quat orientation, float alpha) { - glPushMatrix(); { - glm::vec3 axis = glm::axis(orientation); - glRotatef(glm::degrees(glm::angle(orientation)), axis.x, axis.y, axis.z); - glm::vec3 topLeft = getPoint(reticleSize / 2.0f, -reticleSize / 2.0f); - glm::vec3 topRight = getPoint(-reticleSize / 2.0f, -reticleSize / 2.0f); - glm::vec3 bottomLeft = getPoint(reticleSize / 2.0f, reticleSize / 2.0f); - glm::vec3 bottomRight = getPoint(-reticleSize / 2.0f, reticleSize / 2.0f); - - // TODO: this version of renderQuad() needs to take a color - glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], alpha }; - - - - DependencyManager::get()->renderQuad(topLeft, bottomLeft, bottomRight, topRight, - glm::vec2(0.0f, 0.0f), glm::vec2(1.0f, 0.0f), - glm::vec2(1.0f, 1.0f), glm::vec2(0.0f, 1.0f), - reticleColor, _reticleQuad); - } glPopMatrix(); -} - ApplicationOverlay::ApplicationOverlay() : _textureFov(glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE)), _textureAspectRatio(1.0f), @@ -148,7 +133,8 @@ ApplicationOverlay::ApplicationOverlay() : _previousMagnifierBottomLeft(), _previousMagnifierBottomRight(), _previousMagnifierTopLeft(), - _previousMagnifierTopRight() + _previousMagnifierTopRight(), + _framebufferObject(nullptr) { memset(_reticleActive, 0, sizeof(_reticleActive)); memset(_magActive, 0, sizeof(_reticleActive)); @@ -195,16 +181,17 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { //Handle fading and deactivation/activation of UI // Render 2D overlay - glMatrixMode(GL_PROJECTION); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - _overlays.buildFramebufferObject(); - _overlays.bind(); + buildFramebufferObject(); + + _framebufferObject->bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, size.x, size.y); + glMatrixMode(GL_PROJECTION); glPushMatrix(); { const float NEAR_CLIP = -10000; const float FAR_CLIP = 10000; @@ -226,6 +213,22 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { renderPointers(); renderDomainConnectionStatusBorder(); + if (_newUiTexture) { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBindTexture(GL_TEXTURE_2D, _newUiTexture); + DependencyManager::get()->renderUnitQuad(); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + } + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); } glPopMatrix(); @@ -235,258 +238,160 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { glEnable(GL_LIGHTING); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - _overlays.release(); + _framebufferObject->release(); } -// A quick and dirty solution for compositing the old overlay -// texture with the new one -template -void with_each_texture(GLuint firstPassTexture, GLuint secondPassTexture, F f) { - glEnable(GL_TEXTURE_2D); - glActiveTexture(GL_TEXTURE0); - if (firstPassTexture) { - glBindTexture(GL_TEXTURE_2D, firstPassTexture); - f(); +gpu::PipelinePointer ApplicationOverlay::getDrawPipeline() { + if (!_standardDrawPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(standardDrawTexture_frag))); + auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + gpu::Shader::makeProgram((*program)); + + auto state = gpu::StatePointer(new gpu::State()); + + // enable decal blend + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); + + _standardDrawPipeline.reset(gpu::Pipeline::create(program, state)); } - if (secondPassTexture) { - glBindTexture(GL_TEXTURE_2D, secondPassTexture); - f(); - } - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); + + return _standardDrawPipeline; } +void ApplicationOverlay::bindCursorTexture(gpu::Batch& batch, uint8_t cursorIndex) { + auto& cursorManager = Cursor::Manager::instance(); + auto cursor = cursorManager.getCursor(cursorIndex); + auto iconId = cursor->getIcon(); + if (!_cursors.count(iconId)) { + auto iconPath = cursorManager.getIconImage(cursor->getIcon()); + _cursors[iconId] = DependencyManager::get()-> + getImageTexture(iconPath); + } + batch.setUniformTexture(0, _cursors[iconId]); +} + +#define CURSOR_PIXEL_SIZE 32.0f + // Draws the FBO texture for the screen -void ApplicationOverlay::displayOverlayTexture() { +void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { if (_alpha == 0.0f) { return; } - glMatrixMode(GL_PROJECTION); - glPushMatrix(); { - glLoadIdentity(); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height()); + + renderArgs->_context->syncCache(); - static const glm::vec2 topLeft(-1, 1); - static const glm::vec2 bottomRight(1, -1); - static const glm::vec2 texCoordTopLeft(0.0f, 1.0f); - static const glm::vec2 texCoordBottomRight(1.0f, 0.0f); - with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { - DependencyManager::get()->renderQuad( - topLeft, bottomRight, - texCoordTopLeft, texCoordBottomRight, - glm::vec4(1.0f, 1.0f, 1.0f, _alpha)); - }); + gpu::Batch batch; + Transform model; + //DependencyManager::get()->bindSimpleProgram(batch, true); + batch.setPipeline(getDrawPipeline()); + batch.setModelTransform(Transform()); + batch.setProjectionTransform(mat4()); + batch.setViewTransform(model); + batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + DependencyManager::get()->renderUnitQuad(batch, vec4(vec3(1), _alpha)); - if (!_crosshairTexture) { - _crosshairTexture = DependencyManager::get()-> - getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); - } + //draw the mouse pointer + glm::vec2 canvasSize = qApp->getCanvasSize(); - //draw the mouse pointer - glm::vec2 canvasSize = qApp->getCanvasSize(); - glm::vec2 mouseSize = 32.0f / canvasSize; - auto mouseTopLeft = topLeft * mouseSize; - auto mouseBottomRight = bottomRight * mouseSize; - vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY()); - mousePosition /= canvasSize; - mousePosition *= 2.0f; - mousePosition -= 1.0f; - mousePosition.y *= -1.0f; + // Get the mouse coordinates and convert to NDC [-1, 1] + vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY()); + mousePosition /= canvasSize; + mousePosition *= 2.0f; + mousePosition -= 1.0f; + mousePosition.y *= -1.0f; + model.setTranslation(vec3(mousePosition, 0)); + glm::vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize; + model.setScale(vec3(mouseSize, 1.0f)); + batch.setModelTransform(model); + bindCursorTexture(batch); + glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; + DependencyManager::get()->renderUnitQuad(batch, vec4(1)); + renderArgs->_context->render(batch); +} - glEnable(GL_TEXTURE_2D); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); - glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; - DependencyManager::get()->renderQuad( - mouseTopLeft + mousePosition, mouseBottomRight + mousePosition, - texCoordTopLeft, texCoordBottomRight, - reticleColor); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glDisable(GL_TEXTURE_2D); - } glPopMatrix(); + +static gpu::BufferPointer _hemiVertices; +static gpu::BufferPointer _hemiIndices; +static int _hemiIndexCount{ 0 }; + +glm::vec2 getPolarCoordinates(const PalmData& palm) { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + auto avatarOrientation = myAvatar->getOrientation(); + auto eyePos = myAvatar->getDefaultEyePosition(); + glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm); + // Direction of the tip relative to the eye + glm::vec3 tipDirection = tip - eyePos; + // orient into avatar space + tipDirection = glm::inverse(avatarOrientation) * tipDirection; + // Normalize for trig functions + tipDirection = glm::normalize(tipDirection); + // Convert to polar coordinates + glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y)); + return polar; } // Draws the FBO texture for Oculus rift. -void ApplicationOverlay::displayOverlayTextureHmd(Camera& whichCamera) { +void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera) { if (_alpha == 0.0f) { return; } - glEnable(GL_BLEND); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); - glDisable(GL_LIGHTING); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.01f); + renderArgs->_context->syncCache(); + gpu::Batch batch; + batch.setPipeline(getDrawPipeline()); + batch._glDisable(GL_DEPTH_TEST); + batch._glDisable(GL_CULL_FACE); + batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + batch.setProjectionTransform(whichCamera.getProjection()); + batch.setViewTransform(Transform()); - //Update and draw the magnifiers MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - const glm::quat& orientation = myAvatar->getOrientation(); - // Always display the HMD overlay relative to the camera position but - // remove the HMD pose offset. This results in an overlay that sticks with you - // even in third person mode, but isn't drawn at a fixed distance. - glm::vec3 position = whichCamera.getPosition(); - position -= qApp->getCamera()->getHmdPosition(); - const float scale = myAvatar->getScale() * _oculusUIRadius; - -// glm::vec3 eyeOffset = setEyeOffsetPosition; - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); { - glTranslatef(position.x, position.y, position.z); - glm::mat4 rotation = glm::toMat4(orientation); - glMultMatrixf(&rotation[0][0]); - glScalef(scale, scale, scale); - for (int i = 0; i < NUMBER_OF_RETICLES; i++) { - - if (_magActive[i]) { - _magSizeMult[i] += MAG_SPEED; - if (_magSizeMult[i] > 1.0f) { - _magSizeMult[i] = 1.0f; - } - } else { - _magSizeMult[i] -= MAG_SPEED; - if (_magSizeMult[i] < 0.0f) { - _magSizeMult[i] = 0.0f; - } - } - - if (_magSizeMult[i] > 0.0f) { - //Render magnifier, but dont show border for mouse magnifier - glm::vec2 projection = screenToOverlay(glm::vec2(_reticlePosition[MOUSE].x(), - _reticlePosition[MOUSE].y())); - with_each_texture(_overlays.getTexture(), 0, [&] { - renderMagnifier(projection, _magSizeMult[i], i != MOUSE); - }); - } - } - - glDepthMask(GL_FALSE); - glDisable(GL_ALPHA_TEST); - - static float textureFOV = 0.0f, textureAspectRatio = 1.0f; - if (textureFOV != _textureFov || - textureAspectRatio != _textureAspectRatio) { - textureFOV = _textureFov; - textureAspectRatio = _textureAspectRatio; - - _overlays.buildVBO(_textureFov, _textureAspectRatio, 80, 80); - } + const quat& avatarOrientation = myAvatar->getOrientation(); + quat hmdOrientation = qApp->getCamera()->getHmdRotation(); + vec3 hmdPosition = glm::inverse(avatarOrientation) * qApp->getCamera()->getHmdPosition(); + mat4 overlayXfm = glm::mat4_cast(glm::inverse(hmdOrientation)) * glm::translate(mat4(), -hmdPosition); + batch.setModelTransform(Transform(overlayXfm)); + drawSphereSection(batch); - with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { - _overlays.render(); - }); - if (!Application::getInstance()->isMouseHidden()) { - renderPointersOculus(); + bindCursorTexture(batch); + auto geometryCache = DependencyManager::get(); + vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize); + //Controller Pointers + for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { + PalmData& palm = myAvatar->getHand()->getPalms()[i]; + if (palm.isActive()) { + glm::vec2 polar = getPolarCoordinates(palm); + // Convert to quaternion + mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); + mat4 reticleXfm = overlayXfm * pointerXfm; + reticleXfm = glm::scale(reticleXfm, reticleScale); + batch.setModelTransform(reticleXfm); + // Render reticle at location + geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); } - glDepthMask(GL_TRUE); - glDisable(GL_TEXTURE_2D); - - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glEnable(GL_LIGHTING); - } glPopMatrix(); + } + + //Mouse Pointer + if (_reticleActive[MOUSE]) { + glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(), + _reticlePosition[MOUSE].y())); + mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); + mat4 reticleXfm = overlayXfm * pointerXfm; + reticleXfm = glm::scale(reticleXfm, reticleScale); + batch.setModelTransform(reticleXfm); + geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); + } + + renderArgs->_context->render(batch); } -// Draws the FBO texture for 3DTV. -void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov) { - if (_alpha == 0.0f) { - return; - } - - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - const glm::vec3& viewMatrixTranslation = qApp->getViewMatrixTranslation(); - - glEnable(GL_BLEND); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glEnable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - - glMatrixMode(GL_MODELVIEW); - - glPushMatrix(); - glLoadIdentity(); - // Transform to world space - glm::quat rotation = whichCamera.getRotation(); - glm::vec3 axis2 = glm::axis(rotation); - glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z); - glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z); - - // Translate to the front of the camera - glm::vec3 pos = whichCamera.getPosition(); - glm::quat rot = myAvatar->getOrientation(); - glm::vec3 axis = glm::axis(rot); - - glTranslatef(pos.x, pos.y, pos.z); - glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z); - - glm::vec4 overlayColor = {1.0f, 1.0f, 1.0f, _alpha}; - - //Render - const GLfloat distance = 1.0f; - - const GLfloat halfQuadHeight = distance * tan(fov); - const GLfloat halfQuadWidth = halfQuadHeight * aspectRatio; - const GLfloat quadWidth = halfQuadWidth * 2.0f; - const GLfloat quadHeight = halfQuadHeight * 2.0f; - - GLfloat x = -halfQuadWidth; - GLfloat y = -halfQuadHeight; - glDisable(GL_DEPTH_TEST); - - with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { - DependencyManager::get()->renderQuad(glm::vec3(x, y + quadHeight, -distance), - glm::vec3(x + quadWidth, y + quadHeight, -distance), - glm::vec3(x + quadWidth, y, -distance), - glm::vec3(x, y, -distance), - glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f), - glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f), - overlayColor); - }); - - if (!_crosshairTexture) { - _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + - "images/sixense-reticle.png"); - } - - //draw the mouse pointer - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); - glm::vec2 canvasSize = qApp->getCanvasSize(); - const float reticleSize = 40.0f / canvasSize.x * quadWidth; - x -= reticleSize / 2.0f; - y += reticleSize / 2.0f; - const float mouseX = (qApp->getMouseX() / (float)canvasSize.x) * quadWidth; - const float mouseY = (1.0 - (qApp->getMouseY() / (float)canvasSize.y)) * quadHeight; - - glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; - - DependencyManager::get()->renderQuad(glm::vec3(x + mouseX, y + mouseY, -distance), - glm::vec3(x + mouseX + reticleSize, y + mouseY, -distance), - glm::vec3(x + mouseX + reticleSize, y + mouseY - reticleSize, -distance), - glm::vec3(x + mouseX, y + mouseY - reticleSize, -distance), - glm::vec2(0.0f, 0.0f), glm::vec2(1.0f, 0.0f), - glm::vec2(1.0f, 1.0f), glm::vec2(0.0f, 1.0f), - reticleColor, _reticleQuad); - - glEnable(GL_DEPTH_TEST); - - glPopMatrix(); - - glDepthMask(GL_TRUE); - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glEnable(GL_LIGHTING); -} void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const { cursorPos *= qApp->getCanvasSize(); @@ -516,22 +421,6 @@ void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origi direction = glm::normalize(intersectionWithUi - origin); } -glm::vec2 getPolarCoordinates(const PalmData& palm) { - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - auto avatarOrientation = myAvatar->getOrientation(); - auto eyePos = myAvatar->getDefaultEyePosition(); - glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm); - // Direction of the tip relative to the eye - glm::vec3 tipDirection = tip - eyePos; - // orient into avatar space - tipDirection = glm::inverse(avatarOrientation) * tipDirection; - // Normalize for trig functions - tipDirection = glm::normalize(tipDirection); - // Convert to polar coordinates - glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y)); - return polar; -} - //Caculate the click location using one of the sixense controllers. Scale is not applied QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { QPoint rv; @@ -582,13 +471,9 @@ bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position, //Renders optional pointers void ApplicationOverlay::renderPointers() { - //lazily load crosshair texture - if (_crosshairTexture == 0) { - _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); - } - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glEnable(GL_TEXTURE_2D); + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //glActiveTexture(GL_TEXTURE0); //bindCursorTexture(); @@ -750,43 +635,6 @@ void ApplicationOverlay::renderControllerPointers() { } } -void ApplicationOverlay::renderPointersOculus() { - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_TEXTURE_2D); - - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); - glDisable(GL_DEPTH_TEST); - - glMatrixMode(GL_MODELVIEW); - - //Controller Pointers - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { - PalmData& palm = myAvatar->getHand()->getPalms()[i]; - if (palm.isActive()) { - glm::vec2 polar = getPolarCoordinates(palm); - // Convert to quaternion - glm::quat orientation = glm::quat(glm::vec3(polar.y, -polar.x, 0.0f)); - // Render reticle at location - renderReticle(orientation, _alpha); - } - } - - //Mouse Pointer - if (_reticleActive[MOUSE]) { - glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(), - _reticlePosition[MOUSE].y())); - glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f)); - renderReticle(orientation, _alpha); - } - - glEnable(GL_DEPTH_TEST); - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); -} - //Renders a small magnification of the currently bound texture at the coordinates void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder) { if (!_magnifier) { @@ -1067,119 +915,109 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder() { } } -ApplicationOverlay::TexturedHemisphere::TexturedHemisphere() : - _vertices(0), - _indices(0), - _framebufferObject(NULL), - _vbo(0, 0) { -} -ApplicationOverlay::TexturedHemisphere::~TexturedHemisphere() { - cleanupVBO(); - if (_framebufferObject != NULL) { - delete _framebufferObject; +void ApplicationOverlay::buildHemiVertices( + const float fov, const float aspectRatio, const int slices, const int stacks) { + static float textureFOV = 0.0f, textureAspectRatio = 1.0f; + if (textureFOV == fov && textureAspectRatio == aspectRatio) { + return; } -} -void ApplicationOverlay::TexturedHemisphere::bind() { - _framebufferObject->bind(); -} + textureFOV = fov; + textureAspectRatio = aspectRatio; + + auto geometryCache = DependencyManager::get(); + + _hemiVertices = gpu::BufferPointer(new gpu::Buffer()); + _hemiIndices = gpu::BufferPointer(new gpu::Buffer()); -void ApplicationOverlay::TexturedHemisphere::release() { - _framebufferObject->release(); -} -void ApplicationOverlay::TexturedHemisphere::buildVBO(const float fov, - const float aspectRatio, - const int slices, - const int stacks) { if (fov >= PI) { qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues"; } - // Cleanup old VBO if necessary - cleanupVBO(); - + //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm - // Compute number of vertices needed - _vertices = slices * stacks; - + vec3 pos; + vec2 uv; // Compute vertices positions and texture UV coordinate - TextureVertex* vertexData = new TextureVertex[_vertices]; - TextureVertex* vertexPtr = &vertexData[0]; + // Create and write to buffer for (int i = 0; i < stacks; i++) { - float stacksRatio = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f + uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f // abs(theta) <= fov / 2.0f - float pitch = -fov * (stacksRatio - 0.5f); - + float pitch = -fov * (uv.y - 0.5f); for (int j = 0; j < slices; j++) { - float slicesRatio = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f + uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f // abs(phi) <= fov * aspectRatio / 2.0f - float yaw = -fov * aspectRatio * (slicesRatio - 0.5f); - - vertexPtr->position = getPoint(yaw, pitch); - vertexPtr->uv.x = slicesRatio; - vertexPtr->uv.y = stacksRatio; - vertexPtr++; + float yaw = -fov * aspectRatio * (uv.x - 0.5f); + pos = getPoint(yaw, pitch); + static const vec4 color(1); + _hemiVertices->append(sizeof(pos), (gpu::Byte*)&pos); + _hemiVertices->append(sizeof(vec2), (gpu::Byte*)&uv); + _hemiVertices->append(sizeof(vec4), (gpu::Byte*)&color); } } - // Create and write to buffer - glGenBuffers(1, &_vbo.first); - glBindBuffer(GL_ARRAY_BUFFER, _vbo.first); - static const int BYTES_PER_VERTEX = sizeof(TextureVertex); - glBufferData(GL_ARRAY_BUFFER, _vertices * BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW); - delete[] vertexData; - // Compute number of indices needed static const int VERTEX_PER_TRANGLE = 3; static const int TRIANGLE_PER_RECTANGLE = 2; int numberOfRectangles = (slices - 1) * (stacks - 1); - _indices = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; + _hemiIndexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; // Compute indices order - GLushort* indexData = new GLushort[_indices]; - GLushort* indexPtr = indexData; + std::vector indices; for (int i = 0; i < stacks - 1; i++) { for (int j = 0; j < slices - 1; j++) { GLushort bottomLeftIndex = i * slices + j; GLushort bottomRightIndex = bottomLeftIndex + 1; GLushort topLeftIndex = bottomLeftIndex + slices; GLushort topRightIndex = topLeftIndex + 1; - - *(indexPtr++) = topLeftIndex; - *(indexPtr++) = bottomLeftIndex; - *(indexPtr++) = topRightIndex; - - *(indexPtr++) = topRightIndex; - *(indexPtr++) = bottomLeftIndex; - *(indexPtr++) = bottomRightIndex; + // FIXME make a z-order curve for better vertex cache locality + indices.push_back(topLeftIndex); + indices.push_back(bottomLeftIndex); + indices.push_back(topRightIndex); + + indices.push_back(topRightIndex); + indices.push_back(bottomLeftIndex); + indices.push_back(bottomRightIndex); } } - // Create and write to buffer - glGenBuffers(1, &_vbo.second); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo.second); - static const int BYTES_PER_INDEX = sizeof(GLushort); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices * BYTES_PER_INDEX, indexData, GL_STATIC_DRAW); - delete[] indexData; + _hemiIndices->append(sizeof(GLushort) * indices.size(), (gpu::Byte*)&indices[0]); } -void ApplicationOverlay::TexturedHemisphere::cleanupVBO() { - if (_vbo.first != 0) { - glDeleteBuffers(1, &_vbo.first); - _vbo.first = 0; - } - if (_vbo.second != 0) { - glDeleteBuffers(1, &_vbo.second); - _vbo.second = 0; - } + +void ApplicationOverlay::drawSphereSection(gpu::Batch& batch) { + buildHemiVertices(_textureFov, _textureAspectRatio, 80, 80); + static const int VERTEX_DATA_SLOT = 0; + static const int TEXTURE_DATA_SLOT = 1; + static const int COLOR_DATA_SLOT = 2; + gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 1 for everyone + streamFormat->setAttribute(gpu::Stream::POSITION, VERTEX_DATA_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); + streamFormat->setAttribute(gpu::Stream::TEXCOORD, TEXTURE_DATA_SLOT, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_DATA_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA)); + batch.setInputFormat(streamFormat); + + static const int VERTEX_STRIDE = sizeof(vec3) + sizeof(vec2) + sizeof(vec4); + gpu::BufferView posView(_hemiVertices, 0, _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::POSITION)._element); + gpu::BufferView uvView(_hemiVertices, sizeof(vec3), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::TEXCOORD)._element); + gpu::BufferView colView(_hemiVertices, sizeof(vec3) + sizeof(vec2), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element); + batch.setInputBuffer(VERTEX_DATA_SLOT, posView); + batch.setInputBuffer(TEXTURE_DATA_SLOT, uvView); + batch.setInputBuffer(COLOR_DATA_SLOT, colView); + batch.setIndexBuffer(gpu::UINT16, _hemiIndices, 0); + batch.drawIndexed(gpu::TRIANGLES, _hemiIndexCount); } -void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() { + +GLuint ApplicationOverlay::getOverlayTexture() { + return _framebufferObject->texture(); +} + +void ApplicationOverlay::buildFramebufferObject() { auto canvasSize = qApp->getCanvasSize(); QSize fboSize = QSize(canvasSize.x, canvasSize.y); if (_framebufferObject != NULL && fboSize == _framebufferObject->size()) { - // Already build + // Already built return; } @@ -1188,7 +1026,7 @@ void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() { } _framebufferObject = new QOpenGLFramebufferObject(fboSize, QOpenGLFramebufferObject::Depth); - glBindTexture(GL_TEXTURE_2D, getTexture()); + glBindTexture(GL_TEXTURE_2D, getOverlayTexture()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); @@ -1198,38 +1036,6 @@ void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() { glBindTexture(GL_TEXTURE_2D, 0); } -//Renders a hemisphere with texture coordinates. -void ApplicationOverlay::TexturedHemisphere::render() { - if (_framebufferObject == NULL || _vbo.first == 0 || _vbo.second == 0) { - qDebug() << "TexturedHemisphere::render(): Incorrect initialisation"; - return; - } - - glBindBuffer(GL_ARRAY_BUFFER, _vbo.first); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo.second); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - static const int STRIDE = sizeof(TextureVertex); - static const void* VERTEX_POINTER = 0; - static const void* TEX_COORD_POINTER = (void*)sizeof(glm::vec3); - glVertexPointer(3, GL_FLOAT, STRIDE, VERTEX_POINTER); - glTexCoordPointer(2, GL_FLOAT, STRIDE, TEX_COORD_POINTER); - - glDrawRangeElements(GL_TRIANGLES, 0, _vertices - 1, _indices, GL_UNSIGNED_SHORT, 0); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); -} - -GLuint ApplicationOverlay::TexturedHemisphere::getTexture() { - return _framebufferObject->texture(); -} - glm::vec2 ApplicationOverlay::directionToSpherical(const glm::vec3& direction) { glm::vec2 result; // Compute yaw diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index eb397fe3c6..e8b5a77b1f 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -33,9 +33,9 @@ public: ~ApplicationOverlay(); void renderOverlay(RenderArgs* renderArgs); - void displayOverlayTexture(); - void displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov); - void displayOverlayTextureHmd(Camera& whichCamera); + void displayOverlayTexture(RenderArgs* renderArgs); + void displayOverlayTextureStereo(RenderArgs* renderArgs, Camera& whichCamera, float aspectRatio, float fov); + void displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera); QPoint getPalmClickLocation(const PalmData *palm) const; bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; @@ -59,6 +59,7 @@ public: glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const; glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const; void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const; + GLuint getOverlayTexture(); static glm::vec2 directionToSpherical(const glm::vec3 & direction); static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos); @@ -66,38 +67,12 @@ public: static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos); private: - // Interleaved vertex data - struct TextureVertex { - glm::vec3 position; - glm::vec2 uv; - }; - - typedef QPair VerticesIndices; - class TexturedHemisphere { - public: - TexturedHemisphere(); - ~TexturedHemisphere(); - - void bind(); - void release(); - GLuint getTexture(); - - void buildFramebufferObject(); - void buildVBO(const float fov, const float aspectRatio, const int slices, const int stacks); - void render(); - - private: - void cleanupVBO(); - - GLuint _vertices; - GLuint _indices; - QOpenGLFramebufferObject* _framebufferObject; - VerticesIndices _vbo; - }; - + void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks); + void drawSphereSection(gpu::Batch& batch); float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; - void renderReticle(glm::quat orientation, float alpha); - void renderPointers();; + QOpenGLFramebufferObject* _framebufferObject; + + void renderPointers(); void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder); void renderControllerPointers(); @@ -109,10 +84,12 @@ private: void renderDomainConnectionStatusBorder(); void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0); - TexturedHemisphere _overlays; + void buildFramebufferObject(); float _textureFov; float _textureAspectRatio; + int _hemiVerticesID{ GeometryCache::UNKNOWN_ID }; + enum Reticles { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_RETICLES }; bool _reticleActive[NUMBER_OF_RETICLES]; @@ -125,8 +102,6 @@ private: float _alpha = 1.0f; float _oculusUIRadius; float _trailingAudioLoudness; - - gpu::TexturePointer _crosshairTexture; QMap _cursors; @@ -149,6 +124,10 @@ private: glm::vec3 _previousMagnifierTopLeft; glm::vec3 _previousMagnifierTopRight; + gpu::PipelinePointer _standardDrawPipeline; + + gpu::PipelinePointer getDrawPipeline(); + }; #endif // hifi_ApplicationOverlay_h diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 303d63bef8..3066fd4890 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1179,6 +1179,21 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co batch.draw(gpu::QUADS, 4, 0); } +void GeometryCache::renderUnitQuad(const glm::vec4& color, int id) { + gpu::Batch batch; + renderUnitQuad(batch, color, id); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderUnitQuad(gpu::Batch& batch, const glm::vec4& color, int id) { + static const glm::vec2 topLeft(-1, 1); + static const glm::vec2 bottomRight(1, -1); + static const glm::vec2 texCoordTopLeft(0.0f, 1.0f); + static const glm::vec2 texCoordBottomRight(1.0f, 0.0f); + renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color, id); +} + + void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, const glm::vec4& color, int id) { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index b438eb2d3b..76e03f8669 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -155,6 +155,9 @@ public: void renderBevelCornersRect(int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID); void renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID); + void renderUnitQuad(const glm::vec4& color = glm::vec4(1), int id = UNKNOWN_ID); + void renderUnitQuad(gpu::Batch& batch, const glm::vec4& color = glm::vec4(1), int id = UNKNOWN_ID); + void renderQuad(int x, int y, int width, int height, const glm::vec4& color, int id = UNKNOWN_ID) { renderQuad(glm::vec2(x,y), glm::vec2(x + width, y + height), color, id); } void renderQuad(gpu::Batch& batch, int x, int y, int width, int height, const glm::vec4& color, int id = UNKNOWN_ID) diff --git a/libraries/render-utils/src/standardDrawTexture.slf b/libraries/render-utils/src/standardDrawTexture.slf new file mode 100644 index 0000000000..4fbeb6eb7f --- /dev/null +++ b/libraries/render-utils/src/standardDrawTexture.slf @@ -0,0 +1,24 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// standardDrawTexture.frag +// fragment shader +// +// Created by Sam Gateau on 6/10/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the texture +uniform sampler2D colorMap; + +varying vec2 varTexcoord; +varying vec4 varColor; + + +void main(void) { + vec4 color = texture2D(colorMap, varTexcoord); + gl_FragColor = color * varColor; +} diff --git a/libraries/render-utils/src/standardTransformPNTC.slv b/libraries/render-utils/src/standardTransformPNTC.slv new file mode 100644 index 0000000000..fd2c28049f --- /dev/null +++ b/libraries/render-utils/src/standardTransformPNTC.slv @@ -0,0 +1,33 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// standardTransformPNTC.slv +// vertex shader +// +// Created by Sam Gateau on 6/10/2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + +varying vec3 varNormal; +varying vec2 varTexcoord; +varying vec4 varColor; + +void main(void) { + varTexcoord = gl_MultiTexCoord0.xy; + varColor = gl_Color; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> + <$transformModelToEyeDir(cam, obj, gl_Normal, varNormal)$> + varNormal = normalize(varNormal); +} \ No newline at end of file From 49f4ab99df0272a260d9b559f4d249eda21b00ab Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 13 Jun 2015 10:03:10 -0700 Subject: [PATCH 02/35] Revert "Revert "Fix broken identity transforms on OSX"" This reverts commit 1feceec0c7b5b3c6d2225c26ddbb56a605bd99a4. --- libraries/gpu/src/gpu/GLBackendTransform.cpp | 25 +++++++------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/libraries/gpu/src/gpu/GLBackendTransform.cpp b/libraries/gpu/src/gpu/GLBackendTransform.cpp index 3f760e4cc8..4e524ec24a 100755 --- a/libraries/gpu/src/gpu/GLBackendTransform.cpp +++ b/libraries/gpu/src/gpu/GLBackendTransform.cpp @@ -133,11 +133,11 @@ void GLBackend::updateTransform() { } if (_transform._invalidModel || _transform._invalidView) { + if (_transform._lastMode != GL_MODELVIEW) { + glMatrixMode(GL_MODELVIEW); + _transform._lastMode = GL_MODELVIEW; + } if (!_transform._model.isIdentity()) { - if (_transform._lastMode != GL_MODELVIEW) { - glMatrixMode(GL_MODELVIEW); - _transform._lastMode = GL_MODELVIEW; - } Transform::Mat4 modelView; if (!_transform._view.isIdentity()) { Transform mvx; @@ -147,19 +147,12 @@ void GLBackend::updateTransform() { _transform._model.getMatrix(modelView); } glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); + } else if (!_transform._view.isIdentity()) { + Transform::Mat4 modelView; + _transform._view.getInverseMatrix(modelView); + glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); } else { - if (!_transform._view.isIdentity()) { - if (_transform._lastMode != GL_MODELVIEW) { - glMatrixMode(GL_MODELVIEW); - _transform._lastMode = GL_MODELVIEW; - } - Transform::Mat4 modelView; - _transform._view.getInverseMatrix(modelView); - glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); - } else { - // TODO: eventually do something about the matrix when neither view nor model is specified? - // glLoadIdentity(); - } + glLoadIdentity(); } (void) CHECK_GL_ERROR(); } From e44b21ee0bef85200a1d1ed48cd2d24463be9023 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 13 Jun 2015 10:36:52 -0700 Subject: [PATCH 03/35] Working on functional overlays --- interface/src/Application.cpp | 11 ++++++----- interface/src/ui/ApplicationOverlay.cpp | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0757e6790f..cee203f733 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -878,11 +878,6 @@ void Application::paintGL() { PerformanceWarning warn(showWarnings, "Application::paintGL()"); resizeGL(); - { - PerformanceTimer perfTimer("renderOverlay"); - _applicationOverlay.renderOverlay(&renderArgs); - } - glEnable(GL_LINE_SMOOTH); if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { @@ -977,6 +972,12 @@ void Application::paintGL() { glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); } + { + PerformanceTimer perfTimer("renderOverlay"); + _applicationOverlay.renderOverlay(&renderArgs); + } + + if (!OculusManager::isConnected() || OculusManager::allowSwap()) { _glWidget->swapBuffers(); } diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index ba0d3a60a9..cb17569b86 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -275,7 +275,7 @@ void ApplicationOverlay::bindCursorTexture(gpu::Batch& batch, uint8_t cursorInde // Draws the FBO texture for the screen void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { - if (_alpha == 0.0f) { + if (_alpha == 0.0f || !_framebufferObject) { return; } @@ -335,7 +335,7 @@ glm::vec2 getPolarCoordinates(const PalmData& palm) { // Draws the FBO texture for Oculus rift. void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera) { - if (_alpha == 0.0f) { + if (_alpha == 0.0f || !_framebufferObject) { return; } From 00d8fe75ab7296919a6bcf5baac10287ca946726 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 13 Jun 2015 17:36:08 -0700 Subject: [PATCH 04/35] Breaking up overlay into rendering and compositing classes --- interface/src/Application.cpp | 102 ++- interface/src/Application.h | 42 +- interface/src/Camera.cpp | 16 - interface/src/Camera.h | 14 +- interface/src/audio/AudioToolBox.h | 2 +- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/devices/OculusManager.cpp | 49 +- interface/src/devices/OculusManager.h | 3 + interface/src/devices/SixenseManager.cpp | 2 +- interface/src/devices/TV3DManager.cpp | 2 +- .../src/scripting/HMDScriptingInterface.cpp | 6 +- .../src/scripting/HMDScriptingInterface.h | 4 +- interface/src/ui/ApplicationCompositor.cpp | 757 ++++++++++++++++++ interface/src/ui/ApplicationCompositor.h | 118 +++ interface/src/ui/ApplicationOverlay.cpp | 746 +---------------- interface/src/ui/ApplicationOverlay.h | 84 +- interface/src/ui/PreferencesDialog.cpp | 4 +- interface/src/ui/overlays/Overlays.cpp | 2 +- libraries/render-utils/src/GeometryCache.cpp | 21 + libraries/render-utils/src/GeometryCache.h | 6 +- 20 files changed, 1032 insertions(+), 952 deletions(-) create mode 100644 interface/src/ui/ApplicationCompositor.cpp create mode 100644 interface/src/ui/ApplicationCompositor.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cee203f733..b540751c8d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -333,8 +333,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _cursorVisible(true), _lastMouseMove(usecTimestampNow()), _lastMouseMoveWasSimulated(false), - _touchAvgX(0.0f), - _touchAvgY(0.0f), _isTouchPressed(false), _mousePressed(false), _enableProcessOctreeThread(true), @@ -840,7 +838,7 @@ void Application::initializeUi() { VrMenu::executeQueuedLambdas(); offscreenUi->setMouseTranslator([this](const QPointF& p){ if (OculusManager::isConnected()) { - glm::vec2 pos = _applicationOverlay.screenToOverlay(toGlm(p)); + glm::vec2 pos = _compositor.screenToOverlay(toGlm(p)); return QPointF(pos.x, pos.y); } return QPointF(p); @@ -950,7 +948,7 @@ void Application::paintGL() { glPushMatrix(); glLoadIdentity(); displaySide(&renderArgs, _myCamera); - _applicationOverlay.displayOverlayTexture(&renderArgs); + _compositor.displayOverlayTexture(&renderArgs); glPopMatrix(); renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; @@ -1551,8 +1549,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { _keyboardMouseDevice.mousePressEvent(event); if (event->button() == Qt::LeftButton) { - _mouseDragStartedX = getTrueMouseX(); - _mouseDragStartedY = getTrueMouseY(); + _mouseDragStarted = getTrueMouse(); _mousePressed = true; if (mouseOnScreen()) { @@ -1654,22 +1651,18 @@ void Application::touchUpdateEvent(QTouchEvent* event) { bool validTouch = false; if (activeWindow() == _window) { const QList& tPoints = event->touchPoints(); - _touchAvgX = 0.0f; - _touchAvgY = 0.0f; + _touchAvg = vec2(); int numTouches = tPoints.count(); if (numTouches > 1) { for (int i = 0; i < numTouches; ++i) { - _touchAvgX += tPoints[i].pos().x(); - _touchAvgY += tPoints[i].pos().y(); + _touchAvg += toGlm(tPoints[i].pos()); } - _touchAvgX /= (float)(numTouches); - _touchAvgY /= (float)(numTouches); + _touchAvg /= (float)(numTouches); validTouch = true; } } if (!_isTouchPressed) { - _touchDragStartedAvgX = _touchAvgX; - _touchDragStartedAvgY = _touchAvgY; + _touchDragStartedAvg = _touchAvg; } _isTouchPressed = validTouch; } @@ -1705,8 +1698,7 @@ void Application::touchEndEvent(QTouchEvent* event) { _keyboardMouseDevice.touchEndEvent(event); // put any application specific touch behavior below here.. - _touchDragStartedAvgX = _touchAvgX; - _touchDragStartedAvgY = _touchAvgY; + _touchDragStartedAvg = _touchAvg; _isTouchPressed = false; } @@ -1855,9 +1847,9 @@ void Application::setFullscreen(bool fullscreen) { Menu::getInstance()->getActionForOption(MenuOption::Fullscreen)->setChecked(fullscreen); } -// The following code block is useful on platforms that can have a visible -// app menu in a fullscreen window. However the OSX mechanism hides the -// application menu for fullscreen apps, so the check is not required. + // The following code block is useful on platforms that can have a visible + // app menu in a fullscreen window. However the OSX mechanism hides the + // application menu for fullscreen apps, so the check is not required. #ifndef Q_OS_MAC if (Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { if (fullscreen) { @@ -1927,11 +1919,6 @@ void Application::setEnableVRMode(bool enableVRMode) { OculusManager::recalibrate(); } else { OculusManager::abandonCalibration(); - - _mirrorCamera.setHmdPosition(glm::vec3()); - _mirrorCamera.setHmdRotation(glm::quat()); - _myCamera.setHmdPosition(glm::vec3()); - _myCamera.setHmdRotation(glm::quat()); } resizeGL(); @@ -1943,44 +1930,34 @@ void Application::setLowVelocityFilter(bool lowVelocityFilter) { bool Application::mouseOnScreen() const { if (OculusManager::isConnected()) { - return getMouseX() >= 0 && getMouseX() <= _glWidget->getDeviceWidth() && - getMouseY() >= 0 && getMouseY() <= _glWidget->getDeviceHeight(); + ivec2 mouse = getMouse(); + if (!glm::all(glm::greaterThanEqual(mouse, ivec2()))) { + return false; + } + ivec2 size = toGlm(_glWidget->getDeviceSize()); + if (!glm::all(glm::lessThanEqual(mouse, size))) { + return false; + } } return true; } -int Application::getMouseX() const { +ivec2 Application::getMouse() const { if (OculusManager::isConnected()) { - glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseX(), getTrueMouseY())); - return pos.x; + return _compositor.screenToOverlay(getTrueMouse()); } - return getTrueMouseX(); + return getTrueMouse(); } -int Application::getMouseY() const { +ivec2 Application::getMouseDragStarted() const { if (OculusManager::isConnected()) { - glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseX(), getTrueMouseY())); - return pos.y; + return _compositor.screenToOverlay(getTrueMouseDragStarted()); } - return getTrueMouseY(); + return getTrueMouseDragStarted(); } -int Application::getMouseDragStartedX() const { - if (OculusManager::isConnected()) { - glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseDragStartedX(), - getTrueMouseDragStartedY())); - return pos.x; - } - return getTrueMouseDragStartedX(); -} - -int Application::getMouseDragStartedY() const { - if (OculusManager::isConnected()) { - glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseDragStartedX(), - getTrueMouseDragStartedY())); - return pos.y; - } - return getTrueMouseDragStartedY(); +ivec2 Application::getTrueMouseDragStarted() const { + return _mouseDragStarted; } FaceTracker* Application::getActiveFaceTracker() { @@ -3127,7 +3104,7 @@ PickRay Application::computePickRay(float x, float y) const { y /= size.y; PickRay result; if (isHMDMode()) { - getApplicationOverlay().computeHmdPickRay(glm::vec2(x, y), result.origin, result.direction); + getApplicationCompositor().computeHmdPickRay(glm::vec2(x, y), result.origin, result.direction); } else { if (QThread::currentThread() == activeRenderingThread) { getDisplayViewFrustum()->computePickRay(x, y, result.origin, result.direction); @@ -4809,12 +4786,8 @@ QSize Application::getDeviceSize() const { return _glWidget->getDeviceSize(); } -int Application::getTrueMouseX() const { - return _glWidget->mapFromGlobal(QCursor::pos()).x(); -} - -int Application::getTrueMouseY() const { - return _glWidget->mapFromGlobal(QCursor::pos()).y(); +ivec2 Application::getTrueMouse() const { + return toGlm(_glWidget->mapFromGlobal(QCursor::pos())); } bool Application::isThrottleRendering() const { @@ -4844,3 +4817,18 @@ qreal Application::getDevicePixelRatio() { return _window ? _window->windowHandle()->devicePixelRatio() : 1.0; } +mat4 Application::getEyeProjection(int eye) const { + if (isHMDMode()) { + return OculusManager::getEyeProjection(eye); + } + + return _viewFrustum.getProjection(); +} + +mat4 Application::getEyePose(int eye) const { + if (isHMDMode()) { + return OculusManager::getEyePose(eye); + } + + return mat4(); +} diff --git a/interface/src/Application.h b/interface/src/Application.h index 68bf60781a..bb5c50687a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -69,6 +69,7 @@ #include "ui/UpdateDialog.h" #include "ui/overlays/Overlays.h" #include "ui/ApplicationOverlay.h" +#include "ui/ApplicationCompositor.h" #include "ui/RunningScriptsWidget.h" #include "ui/ToolWindow.h" #include "ui/UserInputMapper.h" @@ -222,15 +223,21 @@ public: const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; } const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } bool mouseOnScreen() const; - int getMouseX() const; - int getMouseY() const; - glm::ivec2 getTrueMousePosition() const; - int getTrueMouseX() const; - int getTrueMouseY() const; - int getMouseDragStartedX() const; - int getMouseDragStartedY() const; - int getTrueMouseDragStartedX() const { return _mouseDragStartedX; } - int getTrueMouseDragStartedY() const { return _mouseDragStartedY; } + + ivec2 getMouse() const; + ivec2 getTrueMouse() const; + ivec2 getMouseDragStarted() const; + ivec2 getTrueMouseDragStarted() const; + + // TODO get rid of these and use glm types directly + int getMouseX() const { return getMouse().x; } + int getMouseY() const { return getMouse().y; } + int getTrueMouseX() const { return getTrueMouse().x; } + int getTrueMouseY() const { return getTrueMouse().y; } + int getMouseDragStartedX() const { return getMouseDragStarted().x; } + int getMouseDragStartedY() const { return getMouseDragStarted().y; } + int getTrueMouseDragStartedX() const { return getTrueMouseDragStarted().x; } + int getTrueMouseDragStartedY() const { return getTrueMouseDragStarted().y; } bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated; } FaceTracker* getActiveFaceTracker(); @@ -239,6 +246,8 @@ public: QSystemTrayIcon* getTrayIcon() { return _trayIcon; } ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; } const ApplicationOverlay& getApplicationOverlay() const { return _applicationOverlay; } + ApplicationCompositor& getApplicationCompositor() { return _compositor; } + const ApplicationCompositor& getApplicationCompositor() const { return _compositor; } Overlays& getOverlays() { return _overlays; } float getFps() const { return _fps; } @@ -333,6 +342,9 @@ public: bool isHMDMode() const; glm::quat getHeadOrientation() const; glm::vec3 getHeadPosition() const; + glm::mat4 getHeadPose() const; + glm::mat4 getEyePose(int eye) const; + glm::mat4 getEyeProjection(int eye) const; QRect getDesirableApplicationGeometry(); RunningScriptsWidget* getRunningScriptsWidget() { return _runningScriptsWidget; } @@ -584,18 +596,17 @@ private: Environment _environment; bool _cursorVisible; - int _mouseDragStartedX; - int _mouseDragStartedY; + ivec2 _mouseDragStarted; + quint64 _lastMouseMove; bool _lastMouseMoveWasSimulated; glm::vec3 _mouseRayOrigin; glm::vec3 _mouseRayDirection; - float _touchAvgX; - float _touchAvgY; - float _touchDragStartedAvgX; - float _touchDragStartedAvgY; + vec2 _touchAvg; + vec2 _touchDragStartedAvg; + bool _isTouchPressed; // true if multitouch has been pressed (clear when finished) bool _mousePressed; // true if mouse has been pressed (clear when finished) @@ -676,6 +687,7 @@ private: Overlays _overlays; ApplicationOverlay _applicationOverlay; + ApplicationCompositor _compositor; }; #endif // hifi_Application_h diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index e501b91dea..42ac30dbf3 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -49,8 +49,6 @@ Camera::Camera() : _mode(CAMERA_MODE_THIRD_PERSON), _position(0.0f, 0.0f, 0.0f), _projection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), 16.0f/9.0f, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)), - _hmdPosition(), - _hmdRotation(), _isKeepLookingAt(false), _lookingAt(0.0f, 0.0f, 0.0f) { @@ -74,20 +72,6 @@ void Camera::setRotation(const glm::quat& rotation) { } } -void Camera::setHmdPosition(const glm::vec3& hmdPosition) { - _hmdPosition = hmdPosition; - if (_isKeepLookingAt) { - lookAt(_lookingAt); - } -} - -void Camera::setHmdRotation(const glm::quat& hmdRotation) { - _hmdRotation = hmdRotation; - if (_isKeepLookingAt) { - lookAt(_lookingAt); - } -} - void Camera::setMode(CameraMode mode) { _mode = mode; emit modeUpdated(modeToString(mode)); diff --git a/interface/src/Camera.h b/interface/src/Camera.h index fc1dbcaf00..6eed39cf16 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -45,26 +45,22 @@ public: void setRotation(const glm::quat& rotation); void setProjection(const glm::mat4 & projection); - void setHmdPosition(const glm::vec3& hmdPosition); - void setHmdRotation(const glm::quat& hmdRotation); void setMode(CameraMode m); - glm::quat getRotation() const { return _rotation * _hmdRotation; } + glm::quat getRotation() const { return _rotation; } const glm::mat4& getProjection() const { return _projection; } - const glm::vec3& getHmdPosition() const { return _hmdPosition; } - const glm::quat& getHmdRotation() const { return _hmdRotation; } CameraMode getMode() const { return _mode; } public slots: QString getModeString() const; void setModeString(const QString& mode); - glm::vec3 getPosition() const { return _position + _hmdPosition; } + glm::vec3 getPosition() const { return _position; } void setPosition(const glm::vec3& position); - void setOrientation(const glm::quat& orientation) { setRotation(orientation); } glm::quat getOrientation() const { return getRotation(); } - + void setOrientation(const glm::quat& orientation) { setRotation(orientation); } + PickRay computePickRay(float x, float y); // These only work on independent cameras @@ -86,8 +82,6 @@ private: glm::vec3 _position; glm::quat _rotation; glm::mat4 _projection; - glm::vec3 _hmdPosition; - glm::quat _hmdRotation; bool _isKeepLookingAt; glm::vec3 _lookingAt; }; diff --git a/interface/src/audio/AudioToolBox.h b/interface/src/audio/AudioToolBox.h index e07e6857aa..ea2ef9f96a 100644 --- a/interface/src/audio/AudioToolBox.h +++ b/interface/src/audio/AudioToolBox.h @@ -14,7 +14,7 @@ #include #include -#include +#include class AudioToolBox : public Dependency { SINGLETON_DEPENDENCY diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c4e08b5dba..2b3997077b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1559,7 +1559,6 @@ void MyAvatar::renderLaserPointers() { //Gets the tip position for the laser pointer glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { - const ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition()); glm::vec3 position = palm->getPosition(); @@ -1568,7 +1567,8 @@ glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { glm::vec3 result; - if (applicationOverlay.calculateRayUICollisionPoint(position, direction, result)) { + const auto& compositor = Application::getInstance()->getApplicationCompositor(); + if (compositor.calculateRayUICollisionPoint(position, direction, result)) { return result; } diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 414c7f6199..f9d354e809 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -104,7 +104,9 @@ bool OculusManager::_eyePerFrameMode = false; ovrEyeType OculusManager::_lastEyeRendered = ovrEye_Count; ovrSizei OculusManager::_recommendedTexSize = { 0, 0 }; float OculusManager::_offscreenRenderScale = 1.0; - +static glm::mat4 _eyeProjections[ovrEye_Count]; +static glm::mat4 _combinedProjection; +static ovrPosef _eyeRenderPoses[ovrEye_Count]; void OculusManager::initSdk() { ovr_Initialize(); @@ -173,9 +175,15 @@ void OculusManager::connect() { _eyeTextures[eye].Header.API = ovrRenderAPI_OpenGL; _eyeTextures[eye].Header.TextureSize = _renderTargetSize; _eyeTextures[eye].Header.RenderViewport.Pos = { 0, 0 }; + ovrMatrix4f eyeProjection = ovrMatrix4f_Projection(_ovrHmd->MaxEyeFov[eye], DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); + _eyeProjections[eye] = toGlm(eyeProjection); }); _eyeTextures[ovrEye_Right].Header.RenderViewport.Pos.x = _recommendedTexSize.w; + ovrFovPort combinedFov = _ovrHmd->MaxEyeFov[0]; + combinedFov.RightTan = _ovrHmd->MaxEyeFov[1].RightTan; + _combinedProjection = toGlm(ovrMatrix4f_Projection(combinedFov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded)); + ovrHmd_SetEnabledCaps(_ovrHmd, ovrHmdCap_LowPersistence | ovrHmdCap_DynamicPrediction); ovrHmd_ConfigureTracking(_ovrHmd, ovrTrackingCap_Orientation | ovrTrackingCap_Position | @@ -566,7 +574,6 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const ovrPosef eyePoses[ovrEye_Count]; ovrHmd_GetEyePoses(_ovrHmd, _frameIndex, eyeOffsets, eyePoses, nullptr); ovrHmd_BeginFrame(_ovrHmd, _frameIndex); - static ovrPosef eyeRenderPose[ovrEye_Count]; //Render each eye into an fbo for_each_eye(_ovrHmd, [&](ovrEyeType eye){ // If we're in eye-per-frame mode, only render one eye @@ -576,29 +583,17 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const return; } _lastEyeRendered = _activeEye = eye; - eyeRenderPose[eye] = eyePoses[eye]; + _eyeRenderPoses[eye] = eyePoses[eye]; // Set the camera rotation for this eye - orientation.x = eyeRenderPose[eye].Orientation.x; - orientation.y = eyeRenderPose[eye].Orientation.y; - orientation.z = eyeRenderPose[eye].Orientation.z; - orientation.w = eyeRenderPose[eye].Orientation.w; - - // Update the application camera with the latest HMD position - whichCamera.setHmdPosition(trackerPosition); - whichCamera.setHmdRotation(orientation); - + vec3 eyePosition = toGlm(_eyeRenderPoses[eye].Position); + eyePosition = whichCamera.getRotation() * eyePosition; + quat eyeRotation = toGlm(_eyeRenderPoses[eye].Orientation); + // Update our camera to what the application camera is doing - _camera->setRotation(whichCamera.getRotation()); - _camera->setPosition(whichCamera.getPosition()); + _camera->setRotation(whichCamera.getRotation() * eyeRotation); + _camera->setPosition(whichCamera.getPosition() + eyePosition); configureCamera(*_camera); - - // Store the latest left and right eye render locations for things that need to know - glm::vec3 thisEyePosition = position + trackerPosition + - (bodyOrientation * glm::quat(orientation.x, orientation.y, orientation.z, orientation.w) * - glm::vec3(_eyeRenderDesc[eye].HmdToEyeViewOffset.x, _eyeRenderDesc[eye].HmdToEyeViewOffset.y, _eyeRenderDesc[eye].HmdToEyeViewOffset.z)); - - _eyePositions[eye] = thisEyePosition; _camera->update(1.0f / Application::getInstance()->getFps()); glMatrixMode(GL_PROJECTION); @@ -615,7 +610,8 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const renderArgs->_renderSide = RenderArgs::MONO; qApp->displaySide(renderArgs, *_camera, false); - qApp->getApplicationOverlay().displayOverlayTextureHmd(renderArgs, *_camera); + //qApp->getApplicationCompositor().displayOverlayTexture(renderArgs); + qApp->getApplicationCompositor().displayOverlayTextureHmd(renderArgs, eye); }); _activeEye = ovrEye_Count; @@ -661,7 +657,7 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const glClear(GL_COLOR_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(finalFbo->getRenderBuffer(0))); //Renders the distorted mesh onto the screen - renderDistortionMesh(eyeRenderPose); + renderDistortionMesh(_eyeRenderPoses); glBindTexture(GL_TEXTURE_2D, 0); #endif glCanvas->swapBuffers(); @@ -877,3 +873,10 @@ int OculusManager::getHMDScreen() { return hmdScreenIndex; } +mat4 OculusManager::getEyeProjection(int eye) { + return _eyeProjections[eye]; +} + +mat4 OculusManager::getEyePose(int eye) { + return toGlm(_eyeRenderPoses[eye]); +} \ No newline at end of file diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 799f07ea35..1dfce7ee7e 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -81,6 +81,9 @@ public: static glm::vec3 getRightEyePosition() { return _eyePositions[ovrEye_Right]; } static int getHMDScreen(); + + static glm::mat4 getEyeProjection(int eye); + static glm::mat4 getEyePose(int eye); private: static void initSdk(); diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index f1a762e64f..5934d2630c 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -489,7 +489,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers) || Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { - pos = qApp->getApplicationOverlay().getPalmClickLocation(palm); + pos = qApp->getApplicationCompositor().getPalmClickLocation(palm); } else { // Get directon relative to avatar orientation glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection(); diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index b0a2ff7e3b..41e549a861 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -120,7 +120,7 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) { glLoadIdentity(); renderArgs->_renderSide = RenderArgs::MONO; qApp->displaySide(renderArgs, eyeCamera, false); - qApp->getApplicationOverlay().displayOverlayTexture(renderArgs); + qApp->getApplicationCompositor().displayOverlayTexture(renderArgs); _activeEye = NULL; }, [&]{ // render right side view diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index f258fd695c..f65d638ccc 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -25,9 +25,9 @@ bool HMDScriptingInterface::getHUDLookAtPosition3D(glm::vec3& result) const { glm::vec3 direction = orientation * glm::vec3(0.0f, 0.0f, -1.0f); - ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); + const auto& compositor = Application::getInstance()->getApplicationCompositor(); - return applicationOverlay.calculateRayUICollisionPoint(position, direction, result); + return compositor.calculateRayUICollisionPoint(position, direction, result); } QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) { @@ -40,7 +40,7 @@ QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* conte glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * (hudIntersection - sphereCenter); glm::quat rotation = ::rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), direction); glm::vec3 eulers = ::safeEulerAngles(rotation); - return qScriptValueFromValue(engine, Application::getInstance()->getApplicationOverlay() + return qScriptValueFromValue(engine, Application::getInstance()->getApplicationCompositor() .sphericalToOverlay(glm::vec2(eulers.y, -eulers.x))); } return QScriptValue::NullValue; diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index 9b0f8dc4d1..4bcced1fa2 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -27,11 +27,11 @@ public: static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine); public slots: - void toggleMagnifier() { Application::getInstance()->getApplicationOverlay().toggleMagnifier(); }; + void toggleMagnifier() { Application::getInstance()->getApplicationCompositor().toggleMagnifier(); }; private: HMDScriptingInterface() {}; - bool getMagnifier() const { return Application::getInstance()->getApplicationOverlay().hasMagnifier(); }; + bool getMagnifier() const { return Application::getInstance()->getApplicationCompositor().hasMagnifier(); }; bool isHMDMode() const { return Application::getInstance()->isHMDMode(); } bool getHUDLookAtPosition3D(glm::vec3& result) const; diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp new file mode 100644 index 0000000000..f6a79f2bc2 --- /dev/null +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -0,0 +1,757 @@ +// +// ApplicationCompositor.cpp +// interface/src/ui/overlays +// +// Created by Benjamin Arnold on 5/27/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "InterfaceConfig.h" + +#include "ApplicationCompositor.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + + +#include "AudioClient.h" +#include "audio/AudioIOStatsRenderer.h" +#include "audio/AudioScope.h" +#include "audio/AudioToolBox.h" +#include "Application.h" +#include "devices/CameraToolBox.h" + +#include "Util.h" +#include "ui/Stats.h" + +#include "../../libraries/render-utils/standardTransformPNTC_vert.h" +#include "../../libraries/render-utils/standardDrawTexture_frag.h" + +// Used to animate the magnification windows +static const float MAG_SPEED = 0.08f; + +static const quint64 MSECS_TO_USECS = 1000ULL; + +static const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; +static const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; +static const float reticleSize = TWO_PI / 100.0f; + +static const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f }; +static const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; + +static const float CURSOR_PIXEL_SIZE = 32.0f; +static const float MOUSE_PITCH_RANGE = 1.0f * PI; +static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI; +static const glm::vec2 MOUSE_RANGE(MOUSE_YAW_RANGE, MOUSE_PITCH_RANGE); + +static gpu::BufferPointer _hemiVertices; +static gpu::BufferPointer _hemiIndices; +static int _hemiIndexCount{ 0 }; + +// Return a point's cartesian coordinates on a sphere from pitch and yaw +glm::vec3 getPoint(float yaw, float pitch) { + return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)), + glm::sin(-pitch), + glm::cos(-pitch) * (-glm::cos(yaw))); +} + + +//Checks if the given ray intersects the sphere at the origin. result will store a multiplier that should +//be multiplied by dir and added to origin to get the location of the collision +bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, float* result) +{ + //Source: http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection + + //Compute A, B and C coefficients + float a = glm::dot(dir, dir); + float b = 2 * glm::dot(dir, origin); + float c = glm::dot(origin, origin) - (r * r); + + //Find discriminant + float disc = b * b - 4 * a * c; + + // if discriminant is negative there are no real roots, so return + // false as ray misses sphere + if (disc < 0) { + return false; + } + + // compute q as described above + float distSqrt = sqrtf(disc); + float q; + if (b < 0) { + q = (-b - distSqrt) / 2.0; + } else { + q = (-b + distSqrt) / 2.0; + } + + // compute t0 and t1 + float t0 = q / a; + float t1 = c / q; + + // make sure t0 is smaller than t1 + if (t0 > t1) { + // if t0 is bigger than t1 swap them around + float temp = t0; + t0 = t1; + t1 = temp; + } + + // if t1 is less than zero, the object is in the ray's negative direction + // and consequently the ray misses the sphere + if (t1 < 0) { + return false; + } + + // if t0 is less than zero, the intersection point is at t1 + if (t0 < 0) { + *result = t1; + return true; + } else { // else the intersection point is at t0 + *result = t0; + return true; + } +} + +ApplicationCompositor::ApplicationCompositor() { + memset(_reticleActive, 0, sizeof(_reticleActive)); + memset(_magActive, 0, sizeof(_reticleActive)); + memset(_magSizeMult, 0, sizeof(_magSizeMult)); + + auto geometryCache = DependencyManager::get(); + + _reticleQuad = geometryCache->allocateID(); + _magnifierQuad = geometryCache->allocateID(); + _audioRedQuad = geometryCache->allocateID(); + _audioGreenQuad = geometryCache->allocateID(); + _audioBlueQuad = geometryCache->allocateID(); + _domainStatusBorder = geometryCache->allocateID(); + _magnifierBorder = geometryCache->allocateID(); +} + +ApplicationCompositor::~ApplicationCompositor() { +} + +void ApplicationCompositor::bindCursorTexture(gpu::Batch& batch, uint8_t cursorIndex) { + auto& cursorManager = Cursor::Manager::instance(); + auto cursor = cursorManager.getCursor(cursorIndex); + auto iconId = cursor->getIcon(); + if (!_cursors.count(iconId)) { + auto iconPath = cursorManager.getIconImage(cursor->getIcon()); + _cursors[iconId] = DependencyManager::get()-> + getImageTexture(iconPath); + } + batch.setUniformTexture(0, _cursors[iconId]); +} + + +// Draws the FBO texture for the screen +void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) { + if (_alpha == 0.0f) { + return; + } + + GLuint texture = qApp->getApplicationOverlay().getOverlayTexture(); + if (!texture) { + return; + } + renderArgs->_context->syncCache(); + auto geometryCache = DependencyManager::get(); + + gpu::Batch batch; + geometryCache->useSimpleDrawPipeline(batch); + batch.setModelTransform(Transform()); + batch.setViewTransform(Transform()); + batch.setProjectionTransform(mat4()); + batch._glBindTexture(GL_TEXTURE_2D, texture); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha)); + + // Doesn't actually render + renderPointers(); + + + //draw the mouse pointer + vec2 canvasSize = qApp->getCanvasSize(); + + // Get the mouse coordinates and convert to NDC [-1, 1] + vec2 mousePosition = vec2(qApp->getMouse()); + mousePosition /= canvasSize; + mousePosition *= 2.0f; + mousePosition -= 1.0f; + mousePosition.y *= -1.0f; + + Transform model; + model.setTranslation(vec3(mousePosition, 0)); + vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize; + model.setScale(vec3(mouseSize, 1.0f)); + batch.setModelTransform(model); + bindCursorTexture(batch); + vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; + geometryCache->renderUnitQuad(batch, vec4(1)); + renderArgs->_context->render(batch); +} + + +vec2 getPolarCoordinates(const PalmData& palm) { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + auto avatarOrientation = myAvatar->getOrientation(); + auto eyePos = myAvatar->getDefaultEyePosition(); + glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm); + // Direction of the tip relative to the eye + glm::vec3 tipDirection = tip - eyePos; + // orient into avatar space + tipDirection = glm::inverse(avatarOrientation) * tipDirection; + // Normalize for trig functions + tipDirection = glm::normalize(tipDirection); + // Convert to polar coordinates + glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y)); + return polar; +} + +// Draws the FBO texture for Oculus rift. +void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int eye) { + if (_alpha == 0.0f) { + return; + } + + GLuint texture = qApp->getApplicationOverlay().getOverlayTexture(); + if (!texture) { + return; + } + + renderArgs->_context->syncCache(); + auto geometryCache = DependencyManager::get(); + + gpu::Batch batch; + DependencyManager::get()->useSimpleDrawPipeline(batch); + batch._glDisable(GL_DEPTH_TEST); + batch._glDisable(GL_CULL_FACE); + batch._glBindTexture(GL_TEXTURE_2D, texture); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + batch.setViewTransform(Transform()); + batch.setProjectionTransform(qApp->getEyeProjection(eye)); + mat4 eyePose = qApp->getEyePose(eye); + glm::mat4 overlayXfm = glm::inverse(eyePose); + batch.setModelTransform(overlayXfm); + drawSphereSection(batch); + + // Doesn't actually render + renderPointers(); + vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize); + + bindCursorTexture(batch); + + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + //Controller Pointers + for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { + PalmData& palm = myAvatar->getHand()->getPalms()[i]; + if (palm.isActive()) { + glm::vec2 polar = getPolarCoordinates(palm); + // Convert to quaternion + mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); + mat4 reticleXfm = overlayXfm * pointerXfm; + reticleXfm = glm::scale(reticleXfm, reticleScale); + batch.setModelTransform(reticleXfm); + // Render reticle at location + geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); + } + } + + //Mouse Pointer + if (_reticleActive[MOUSE]) { + glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(), + _reticlePosition[MOUSE].y())); + mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); + mat4 reticleXfm = overlayXfm * pointerXfm; + reticleXfm = glm::scale(reticleXfm, reticleScale); + batch.setModelTransform(reticleXfm); + geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); + } + + renderArgs->_context->render(batch); +} + + +void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const { + cursorPos *= qApp->getCanvasSize(); + const glm::vec2 projection = screenToSpherical(cursorPos); + // The overlay space orientation of the mouse coordinates + const glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f)); + // FIXME We now have the direction of the ray FROM THE DEFAULT HEAD POSE. + // Now we need to account for the actual camera position relative to the overlay + glm::vec3 overlaySpaceDirection = glm::normalize(orientation * IDENTITY_FRONT); + + + // We need the RAW camera orientation and position, because this is what the overlay is + // rendered relative to + const glm::vec3 overlayPosition = qApp->getCamera()->getPosition(); + const glm::quat overlayOrientation = qApp->getCamera()->getRotation(); + + // Intersection UI overlay space + glm::vec3 worldSpaceDirection = overlayOrientation * overlaySpaceDirection; + glm::vec3 intersectionWithUi = glm::normalize(worldSpaceDirection) * _oculusUIRadius; + intersectionWithUi += overlayPosition; + + // Intersection in world space + origin = overlayPosition; + direction = glm::normalize(intersectionWithUi - origin); +} + +//Caculate the click location using one of the sixense controllers. Scale is not applied +QPoint ApplicationCompositor::getPalmClickLocation(const PalmData *palm) const { + QPoint rv; + auto canvasSize = qApp->getCanvasSize(); + if (qApp->isHMDMode()) { + glm::vec2 polar = getPolarCoordinates(*palm); + glm::vec2 point = sphericalToScreen(-polar); + rv.rx() = point.x; + rv.ry() = point.y; + } else { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + glm::dmat4 projection; + qApp->getProjectionMatrix(&projection); + glm::quat invOrientation = glm::inverse(myAvatar->getOrientation()); + glm::vec3 eyePos = myAvatar->getDefaultEyePosition(); + glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm); + glm::vec3 tipPos = invOrientation * (tip - eyePos); + + glm::vec4 clipSpacePos = glm::vec4(projection * glm::dvec4(tipPos, 1.0)); + glm::vec3 ndcSpacePos; + if (clipSpacePos.w != 0) { + ndcSpacePos = glm::vec3(clipSpacePos) / clipSpacePos.w; + } + + rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * canvasSize.x); + rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * canvasSize.y); + } + return rv; +} + +//Finds the collision point of a world space ray +bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + + glm::quat inverseOrientation = glm::inverse(myAvatar->getOrientation()); + + glm::vec3 relativePosition = inverseOrientation * (position - myAvatar->getDefaultEyePosition()); + glm::vec3 relativeDirection = glm::normalize(inverseOrientation * direction); + + float t; + if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getScale(), &t)){ + result = position + direction * t; + return true; + } + + return false; +} + +//Renders optional pointers +void ApplicationCompositor::renderPointers() { + //glEnable(GL_TEXTURE_2D); + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + //glActiveTexture(GL_TEXTURE0); + //bindCursorTexture(); + + if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) { + //If we are in oculus, render reticle later + if (_lastMouseMove == 0) { + _lastMouseMove = usecTimestampNow(); + } + QPoint position = QPoint(qApp->getTrueMouseX(), qApp->getTrueMouseY()); + + static const int MAX_IDLE_TIME = 3; + if (_reticlePosition[MOUSE] != position) { + _lastMouseMove = usecTimestampNow(); + } else if (usecTimestampNow() - _lastMouseMove > MAX_IDLE_TIME * USECS_PER_SECOND) { + //float pitch = 0.0f, yaw = 0.0f, roll = 0.0f; // radians + //OculusManager::getEulerAngles(yaw, pitch, roll); + glm::quat orientation = qApp->getHeadOrientation(); // (glm::vec3(pitch, yaw, roll)); + glm::vec3 result; + + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + if (calculateRayUICollisionPoint(myAvatar->getEyePosition(), + myAvatar->getOrientation() * orientation * IDENTITY_FRONT, + result)) { + glm::vec3 lookAtDirection = glm::inverse(myAvatar->getOrientation()) * (result - myAvatar->getDefaultEyePosition()); + glm::vec2 spericalPos = directionToSpherical(glm::normalize(lookAtDirection)); + glm::vec2 screenPos = sphericalToScreen(spericalPos); + position = QPoint(screenPos.x, screenPos.y); + // FIXME + //glCanvas->cursor().setPos(glCanvas->mapToGlobal(position)); + } else { + qDebug() << "No collision point"; + } + } + + _reticlePosition[MOUSE] = position; + _reticleActive[MOUSE] = true; + _magActive[MOUSE] = _magnifier; + _reticleActive[LEFT_CONTROLLER] = false; + _reticleActive[RIGHT_CONTROLLER] = false; + } else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { + _lastMouseMove = 0; + //only render controller pointer if we aren't already rendering a mouse pointer + _reticleActive[MOUSE] = false; + _magActive[MOUSE] = false; + renderControllerPointers(); + } + //glBindTexture(GL_TEXTURE_2D, 0); + //glDisable(GL_TEXTURE_2D); +} + +void ApplicationCompositor::renderControllerPointers() { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + + //Static variables used for storing controller state + static quint64 pressedTime[NUMBER_OF_RETICLES] = { 0ULL, 0ULL, 0ULL }; + static bool isPressed[NUMBER_OF_RETICLES] = { false, false, false }; + static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false }; + + const HandData* handData = DependencyManager::get()->getMyAvatar()->getHandData(); + + for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { + const int index = palmIndex - 1; + + const PalmData* palmData = NULL; + + if (palmIndex >= handData->getPalms().size()) { + return; + } + + if (handData->getPalms()[palmIndex].isActive()) { + palmData = &handData->getPalms()[palmIndex]; + } else { + continue; + } + + int controllerButtons = palmData->getControllerButtons(); + + //Check for if we should toggle or drag the magnification window + if (controllerButtons & BUTTON_3) { + if (isPressed[index] == false) { + //We are now dragging the window + isPressed[index] = true; + //set the pressed time in us + pressedTime[index] = usecTimestampNow(); + stateWhenPressed[index] = _magActive[index]; + } + } else if (isPressed[index]) { + isPressed[index] = false; + //If the button was only pressed for < 250 ms + //then disable it. + + const int MAX_BUTTON_PRESS_TIME = 250 * MSECS_TO_USECS; + if (usecTimestampNow() < pressedTime[index] + MAX_BUTTON_PRESS_TIME) { + _magActive[index] = !stateWhenPressed[index]; + } + } + + //if we have the oculus, we should make the cursor smaller since it will be + //magnified + if (qApp->isHMDMode()) { + + QPoint point = getPalmClickLocation(palmData); + + _reticlePosition[index] = point; + + //When button 2 is pressed we drag the mag window + if (isPressed[index]) { + _magActive[index] = true; + } + + // If oculus is enabled, we draw the crosshairs later + continue; + } + + auto canvasSize = qApp->getCanvasSize(); + int mouseX, mouseY; + if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { + QPoint res = getPalmClickLocation(palmData); + mouseX = res.x(); + mouseY = res.y(); + } else { + // Get directon relative to avatar orientation + glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); + + // Get the angles, scaled between (-0.5,0.5) + float xAngle = (atan2(direction.z, direction.x) + M_PI_2); + float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); + + // Get the pixel range over which the xAngle and yAngle are scaled + float cursorRange = canvasSize.x * SixenseManager::getInstance().getCursorPixelRangeMult(); + + mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle); + mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle); + } + + //If the cursor is out of the screen then don't render it + if (mouseX < 0 || mouseX >= (int)canvasSize.x || mouseY < 0 || mouseY >= (int)canvasSize.y) { + _reticleActive[index] = false; + continue; + } + _reticleActive[index] = true; + + + const float reticleSize = 40.0f; + + mouseX -= reticleSize / 2.0f; + mouseY += reticleSize / 2.0f; + + + glm::vec2 topLeft(mouseX, mouseY); + glm::vec2 bottomRight(mouseX + reticleSize, mouseY - reticleSize); + glm::vec2 texCoordTopLeft(0.0f, 0.0f); + glm::vec2 texCoordBottomRight(1.0f, 1.0f); + + DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + glm::vec4(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f)); + + } +} + +//Renders a small magnification of the currently bound texture at the coordinates +void ApplicationCompositor::renderMagnifier(const glm::vec2& magPos, float sizeMult, bool showBorder) { + if (!_magnifier) { + return; + } + auto canvasSize = qApp->getCanvasSize(); + + const int widgetWidth = canvasSize.x; + const int widgetHeight = canvasSize.y; + + const float halfWidth = (MAGNIFY_WIDTH / _textureAspectRatio) * sizeMult / 2.0f; + const float halfHeight = MAGNIFY_HEIGHT * sizeMult / 2.0f; + // Magnification Texture Coordinates + const float magnifyULeft = (magPos.x - halfWidth) / (float)widgetWidth; + const float magnifyURight = (magPos.x + halfWidth) / (float)widgetWidth; + const float magnifyVTop = 1.0f - (magPos.y - halfHeight) / (float)widgetHeight; + const float magnifyVBottom = 1.0f - (magPos.y + halfHeight) / (float)widgetHeight; + + const float newHalfWidth = halfWidth * MAGNIFY_MULT; + const float newHalfHeight = halfHeight * MAGNIFY_MULT; + //Get yaw / pitch value for the corners + const glm::vec2 topLeftYawPitch = overlayToSpherical(glm::vec2(magPos.x - newHalfWidth, + magPos.y - newHalfHeight)); + const glm::vec2 bottomRightYawPitch = overlayToSpherical(glm::vec2(magPos.x + newHalfWidth, + magPos.y + newHalfHeight)); + + const glm::vec3 bottomLeft = getPoint(topLeftYawPitch.x, bottomRightYawPitch.y); + const glm::vec3 bottomRight = getPoint(bottomRightYawPitch.x, bottomRightYawPitch.y); + const glm::vec3 topLeft = getPoint(topLeftYawPitch.x, topLeftYawPitch.y); + const glm::vec3 topRight = getPoint(bottomRightYawPitch.x, topLeftYawPitch.y); + + auto geometryCache = DependencyManager::get(); + + if (bottomLeft != _previousMagnifierBottomLeft || bottomRight != _previousMagnifierBottomRight + || topLeft != _previousMagnifierTopLeft || topRight != _previousMagnifierTopRight) { + QVector border; + border << topLeft; + border << bottomLeft; + border << bottomRight; + border << topRight; + border << topLeft; + geometryCache->updateVertices(_magnifierBorder, border, glm::vec4(1.0f, 0.0f, 0.0f, _alpha)); + + _previousMagnifierBottomLeft = bottomLeft; + _previousMagnifierBottomRight = bottomRight; + _previousMagnifierTopLeft = topLeft; + _previousMagnifierTopRight = topRight; + } + + glPushMatrix(); { + if (showBorder) { + glDisable(GL_TEXTURE_2D); + glLineWidth(1.0f); + //Outer Line + geometryCache->renderVertices(gpu::LINE_STRIP, _magnifierBorder); + glEnable(GL_TEXTURE_2D); + } + glm::vec4 magnifierColor = { 1.0f, 1.0f, 1.0f, _alpha }; + + DependencyManager::get()->renderQuad(bottomLeft, bottomRight, topRight, topLeft, + glm::vec2(magnifyULeft, magnifyVBottom), + glm::vec2(magnifyURight, magnifyVBottom), + glm::vec2(magnifyURight, magnifyVTop), + glm::vec2(magnifyULeft, magnifyVTop), + magnifierColor, _magnifierQuad); + + } glPopMatrix(); +} + +void ApplicationCompositor::buildHemiVertices( + const float fov, const float aspectRatio, const int slices, const int stacks) { + static float textureFOV = 0.0f, textureAspectRatio = 1.0f; + if (textureFOV == fov && textureAspectRatio == aspectRatio) { + return; + } + + textureFOV = fov; + textureAspectRatio = aspectRatio; + + auto geometryCache = DependencyManager::get(); + + _hemiVertices = gpu::BufferPointer(new gpu::Buffer()); + _hemiIndices = gpu::BufferPointer(new gpu::Buffer()); + + + if (fov >= PI) { + qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues"; + } + + //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm + + vec3 pos; + vec2 uv; + // Compute vertices positions and texture UV coordinate + // Create and write to buffer + for (int i = 0; i < stacks; i++) { + uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f + // abs(theta) <= fov / 2.0f + float pitch = -fov * (uv.y - 0.5f); + for (int j = 0; j < slices; j++) { + uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f + // abs(phi) <= fov * aspectRatio / 2.0f + float yaw = -fov * aspectRatio * (uv.x - 0.5f); + pos = getPoint(yaw, pitch); + static const vec4 color(1); + _hemiVertices->append(sizeof(pos), (gpu::Byte*)&pos); + _hemiVertices->append(sizeof(vec2), (gpu::Byte*)&uv); + _hemiVertices->append(sizeof(vec4), (gpu::Byte*)&color); + } + } + + // Compute number of indices needed + static const int VERTEX_PER_TRANGLE = 3; + static const int TRIANGLE_PER_RECTANGLE = 2; + int numberOfRectangles = (slices - 1) * (stacks - 1); + _hemiIndexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; + + // Compute indices order + std::vector indices; + for (int i = 0; i < stacks - 1; i++) { + for (int j = 0; j < slices - 1; j++) { + GLushort bottomLeftIndex = i * slices + j; + GLushort bottomRightIndex = bottomLeftIndex + 1; + GLushort topLeftIndex = bottomLeftIndex + slices; + GLushort topRightIndex = topLeftIndex + 1; + // FIXME make a z-order curve for better vertex cache locality + indices.push_back(topLeftIndex); + indices.push_back(bottomLeftIndex); + indices.push_back(topRightIndex); + + indices.push_back(topRightIndex); + indices.push_back(bottomLeftIndex); + indices.push_back(bottomRightIndex); + } + } + _hemiIndices->append(sizeof(GLushort) * indices.size(), (gpu::Byte*)&indices[0]); +} + + +void ApplicationCompositor::drawSphereSection(gpu::Batch& batch) { + buildHemiVertices(_textureFov, _textureAspectRatio, 80, 80); + static const int VERTEX_DATA_SLOT = 0; + static const int TEXTURE_DATA_SLOT = 1; + static const int COLOR_DATA_SLOT = 2; + gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 1 for everyone + streamFormat->setAttribute(gpu::Stream::POSITION, VERTEX_DATA_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); + streamFormat->setAttribute(gpu::Stream::TEXCOORD, TEXTURE_DATA_SLOT, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_DATA_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA)); + batch.setInputFormat(streamFormat); + + static const int VERTEX_STRIDE = sizeof(vec3) + sizeof(vec2) + sizeof(vec4); + gpu::BufferView posView(_hemiVertices, 0, _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::POSITION)._element); + gpu::BufferView uvView(_hemiVertices, sizeof(vec3), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::TEXCOORD)._element); + gpu::BufferView colView(_hemiVertices, sizeof(vec3) + sizeof(vec2), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element); + batch.setInputBuffer(VERTEX_DATA_SLOT, posView); + batch.setInputBuffer(TEXTURE_DATA_SLOT, uvView); + batch.setInputBuffer(COLOR_DATA_SLOT, colView); + batch.setIndexBuffer(gpu::UINT16, _hemiIndices, 0); + batch.drawIndexed(gpu::TRIANGLES, _hemiIndexCount); +} + + +glm::vec2 ApplicationCompositor::directionToSpherical(const glm::vec3& direction) { + glm::vec2 result; + // Compute yaw + glm::vec3 normalProjection = glm::normalize(glm::vec3(direction.x, 0.0f, direction.z)); + result.x = glm::acos(glm::dot(IDENTITY_FRONT, normalProjection)); + if (glm::dot(IDENTITY_RIGHT, normalProjection) > 0.0f) { + result.x = -glm::abs(result.x); + } else { + result.x = glm::abs(result.x); + } + // Compute pitch + result.y = angleBetween(IDENTITY_UP, direction) - PI_OVER_TWO; + + return result; +} + +glm::vec3 ApplicationCompositor::sphericalToDirection(const glm::vec2& sphericalPos) { + glm::quat rotation(glm::vec3(sphericalPos.y, sphericalPos.x, 0.0f)); + return rotation * IDENTITY_FRONT; +} + +glm::vec2 ApplicationCompositor::screenToSpherical(const glm::vec2& screenPos) { + auto screenSize = qApp->getCanvasSize(); + glm::vec2 result; + result.x = -(screenPos.x / screenSize.x - 0.5f); + result.y = (screenPos.y / screenSize.y - 0.5f); + result.x *= MOUSE_YAW_RANGE; + result.y *= MOUSE_PITCH_RANGE; + + return result; +} + +glm::vec2 ApplicationCompositor::sphericalToScreen(const glm::vec2& sphericalPos) { + glm::vec2 result = sphericalPos; + result.x *= -1.0; + result /= MOUSE_RANGE; + result += 0.5f; + result *= qApp->getCanvasSize(); + return result; +} + +glm::vec2 ApplicationCompositor::sphericalToOverlay(const glm::vec2& sphericalPos) const { + glm::vec2 result = sphericalPos; + result.x *= -1.0; + result /= _textureFov; + result.x /= _textureAspectRatio; + result += 0.5f; + result *= qApp->getCanvasSize(); + return result; +} + +glm::vec2 ApplicationCompositor::overlayToSpherical(const glm::vec2& overlayPos) const { + glm::vec2 result = overlayPos; + result /= qApp->getCanvasSize(); + result -= 0.5f; + result *= _textureFov; + result.x *= _textureAspectRatio; + result.x *= -1.0f; + return result; +} + +glm::vec2 ApplicationCompositor::screenToOverlay(const glm::vec2& screenPos) const { + return sphericalToOverlay(screenToSpherical(screenPos)); +} + +glm::vec2 ApplicationCompositor::overlayToScreen(const glm::vec2& overlayPos) const { + return sphericalToScreen(overlayToSpherical(overlayPos)); +} diff --git a/interface/src/ui/ApplicationCompositor.h b/interface/src/ui/ApplicationCompositor.h new file mode 100644 index 0000000000..61bbafaba7 --- /dev/null +++ b/interface/src/ui/ApplicationCompositor.h @@ -0,0 +1,118 @@ +// +// Created by Bradley Austin Davis Arnold on 2015/06/13 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ApplicationOverlayCompositor_h +#define hifi_ApplicationOverlayCompositor_h + +#include +#include + +#include +#include +#include +#include + +class Camera; +class PalmData; +class RenderArgs; + +const float MAGNIFY_WIDTH = 220.0f; +const float MAGNIFY_HEIGHT = 100.0f; +const float MAGNIFY_MULT = 2.0f; + +const float DEFAULT_HMD_UI_ANGULAR_SIZE = 72.0f; + +// Handles the drawing of the overlays to the screen +// TODO, move divide up the rendering, displaying and input handling +// facilities of this class +class ApplicationCompositor : public QObject { + Q_OBJECT +public: + ApplicationCompositor(); + ~ApplicationCompositor(); + + void displayOverlayTexture(RenderArgs* renderArgs); + void displayOverlayTextureHmd(RenderArgs* renderArgs, int eye); + + QPoint getPalmClickLocation(const PalmData *palm) const; + bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; + + bool hasMagnifier() const { return _magnifier; } + void toggleMagnifier() { _magnifier = !_magnifier; } + + float getHmdUIAngularSize() const { return _hmdUIAngularSize; } + void setHmdUIAngularSize(float hmdUIAngularSize) { _hmdUIAngularSize = hmdUIAngularSize; } + + // Converter from one frame of reference to another. + // Frame of reference: + // Direction: Ray that represents the spherical values + // Screen: Position on the screen (x,y) + // Spherical: Pitch and yaw that gives the position on the sphere we project on (yaw,pitch) + // Overlay: Position on the overlay (x,y) + // (x,y) in Overlay are similar than (x,y) in Screen except they can be outside of the bound of te screen. + // This allows for picking outside of the screen projection in 3D. + glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const; + glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const; + glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const; + glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const; + void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const; + GLuint getOverlayTexture(); + + static glm::vec2 directionToSpherical(const glm::vec3 & direction); + static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos); + static glm::vec2 screenToSpherical(const glm::vec2 & screenPos); + static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos); + +private: + void displayOverlayTextureStereo(RenderArgs* renderArgs, float aspectRatio, float fov); + void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0); + void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks); + void drawSphereSection(gpu::Batch& batch); + + void renderPointers(); + void renderMagnifier(const glm::vec2& magPos, float sizeMult, bool showBorder); + + void renderControllerPointers(); + void renderPointersOculus(); + + float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; + float _textureFov{ glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE) }; + float _textureAspectRatio{ 1.0f }; + int _hemiVerticesID{ GeometryCache::UNKNOWN_ID }; + + enum Reticles { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_RETICLES }; + bool _reticleActive[NUMBER_OF_RETICLES]; + QPoint _reticlePosition[NUMBER_OF_RETICLES]; + bool _magActive[NUMBER_OF_RETICLES]; + float _magSizeMult[NUMBER_OF_RETICLES]; + quint64 _lastMouseMove{ 0 }; + bool _magnifier{ true }; + + float _alpha{ 1.0f }; + float _oculusUIRadius{ 1.0f }; + + QMap _cursors; + + int _reticleQuad; + int _magnifierQuad; + int _audioRedQuad; + int _audioGreenQuad; + int _audioBlueQuad; + int _domainStatusBorder; + int _magnifierBorder; + + int _previousBorderWidth{ -1 }; + int _previousBorderHeight{ -1 }; + + glm::vec3 _previousMagnifierBottomLeft; + glm::vec3 _previousMagnifierBottomRight; + glm::vec3 _previousMagnifierTopLeft; + glm::vec3 _previousMagnifierTopRight; +}; + +#endif // hifi_ApplicationOverlayCompositor_h diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index cb17569b86..ea56eed73d 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -36,114 +36,20 @@ #include "Util.h" #include "ui/Stats.h" -#include "../../libraries/render-utils/standardTransformPNTC_vert.h" -#include "../../libraries/render-utils/standardDrawTexture_frag.h" - -// Used to animate the magnification windows -const float MAG_SPEED = 0.08f; - -const quint64 MSECS_TO_USECS = 1000ULL; - const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; -const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; -const float reticleSize = TWO_PI / 100.0f; - - +const int AUDIO_METER_GAP = 5; +const int MUTE_ICON_PADDING = 10; const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f }; const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; -static const float MOUSE_PITCH_RANGE = 1.0f * PI; -static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI; -static const glm::vec2 MOUSE_RANGE(MOUSE_YAW_RANGE, MOUSE_PITCH_RANGE); - -// Return a point's cartesian coordinates on a sphere from pitch and yaw -glm::vec3 getPoint(float yaw, float pitch) { - return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)), - glm::sin(-pitch), - glm::cos(-pitch) * (-glm::cos(yaw))); -} - -//Checks if the given ray intersects the sphere at the origin. result will store a multiplier that should -//be multiplied by dir and added to origin to get the location of the collision -bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, float* result) -{ - //Source: http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection - - //Compute A, B and C coefficients - float a = glm::dot(dir, dir); - float b = 2 * glm::dot(dir, origin); - float c = glm::dot(origin, origin) - (r * r); - - //Find discriminant - float disc = b * b - 4 * a * c; - - // if discriminant is negative there are no real roots, so return - // false as ray misses sphere - if (disc < 0) { - return false; - } - - // compute q as described above - float distSqrt = sqrtf(disc); - float q; - if (b < 0) { - q = (-b - distSqrt) / 2.0; - } else { - q = (-b + distSqrt) / 2.0; - } - - // compute t0 and t1 - float t0 = q / a; - float t1 = c / q; - - // make sure t0 is smaller than t1 - if (t0 > t1) { - // if t0 is bigger than t1 swap them around - float temp = t0; - t0 = t1; - t1 = temp; - } - - // if t1 is less than zero, the object is in the ray's negative direction - // and consequently the ray misses the sphere - if (t1 < 0) { - return false; - } - - // if t0 is less than zero, the intersection point is at t1 - if (t0 < 0) { - *result = t1; - return true; - } else { // else the intersection point is at t0 - *result = t0; - return true; - } -} - ApplicationOverlay::ApplicationOverlay() : - _textureFov(glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE)), - _textureAspectRatio(1.0f), - _lastMouseMove(0), - _magnifier(true), _alpha(1.0f), - _oculusUIRadius(1.0f), _trailingAudioLoudness(0.0f), _previousBorderWidth(-1), _previousBorderHeight(-1), - _previousMagnifierBottomLeft(), - _previousMagnifierBottomRight(), - _previousMagnifierTopLeft(), - _previousMagnifierTopRight(), _framebufferObject(nullptr) { - memset(_reticleActive, 0, sizeof(_reticleActive)); - memset(_magActive, 0, sizeof(_reticleActive)); - memset(_magSizeMult, 0, sizeof(_magSizeMult)); - auto geometryCache = DependencyManager::get(); - - _reticleQuad = geometryCache->allocateID(); - _magnifierQuad = geometryCache->allocateID(); _audioRedQuad = geometryCache->allocateID(); _audioGreenQuad = geometryCache->allocateID(); _audioBlueQuad = geometryCache->allocateID(); @@ -159,7 +65,7 @@ ApplicationOverlay::ApplicationOverlay() : auto offscreenUi = DependencyManager::get(); offscreenUi->lockTexture(textureId); assert(!glGetError()); - std::swap(_newUiTexture, textureId); + std::swap(_uiTexture, textureId); if (textureId) { offscreenUi->releaseTexture(textureId); } @@ -172,13 +78,10 @@ ApplicationOverlay::~ApplicationOverlay() { // Renders the overlays either to a texture or to the screen void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); - Overlays& overlays = qApp->getOverlays(); - - _textureFov = glm::radians(_hmdUIAngularSize); - glm::vec2 size = qApp->getCanvasSize(); - _textureAspectRatio = aspect(size); - //Handle fading and deactivation/activation of UI + Overlays& overlays = qApp->getOverlays(); + glm::vec2 size = qApp->getCanvasSize(); + // TODO Handle fading and deactivation/activation of UI // Render 2D overlay glDisable(GL_DEPTH_TEST); @@ -210,10 +113,8 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { overlays.renderHUD(renderArgs); - renderPointers(); - renderDomainConnectionStatusBorder(); - if (_newUiTexture) { + if (_uiTexture) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); @@ -221,7 +122,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBindTexture(GL_TEXTURE_2D, _newUiTexture); + glBindTexture(GL_TEXTURE_2D, _uiTexture); DependencyManager::get()->renderUnitQuad(); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); @@ -241,472 +142,6 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { _framebufferObject->release(); } -gpu::PipelinePointer ApplicationOverlay::getDrawPipeline() { - if (!_standardDrawPipeline) { - auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert))); - auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(standardDrawTexture_frag))); - auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); - gpu::Shader::makeProgram((*program)); - - auto state = gpu::StatePointer(new gpu::State()); - - // enable decal blend - state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); - - _standardDrawPipeline.reset(gpu::Pipeline::create(program, state)); - } - - return _standardDrawPipeline; -} - -void ApplicationOverlay::bindCursorTexture(gpu::Batch& batch, uint8_t cursorIndex) { - auto& cursorManager = Cursor::Manager::instance(); - auto cursor = cursorManager.getCursor(cursorIndex); - auto iconId = cursor->getIcon(); - if (!_cursors.count(iconId)) { - auto iconPath = cursorManager.getIconImage(cursor->getIcon()); - _cursors[iconId] = DependencyManager::get()-> - getImageTexture(iconPath); - } - batch.setUniformTexture(0, _cursors[iconId]); -} - -#define CURSOR_PIXEL_SIZE 32.0f - -// Draws the FBO texture for the screen -void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { - if (_alpha == 0.0f || !_framebufferObject) { - return; - } - - renderArgs->_context->syncCache(); - - gpu::Batch batch; - Transform model; - //DependencyManager::get()->bindSimpleProgram(batch, true); - batch.setPipeline(getDrawPipeline()); - batch.setModelTransform(Transform()); - batch.setProjectionTransform(mat4()); - batch.setViewTransform(model); - batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); - batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - DependencyManager::get()->renderUnitQuad(batch, vec4(vec3(1), _alpha)); - - //draw the mouse pointer - glm::vec2 canvasSize = qApp->getCanvasSize(); - - // Get the mouse coordinates and convert to NDC [-1, 1] - vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY()); - mousePosition /= canvasSize; - mousePosition *= 2.0f; - mousePosition -= 1.0f; - mousePosition.y *= -1.0f; - model.setTranslation(vec3(mousePosition, 0)); - glm::vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize; - model.setScale(vec3(mouseSize, 1.0f)); - batch.setModelTransform(model); - bindCursorTexture(batch); - glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; - DependencyManager::get()->renderUnitQuad(batch, vec4(1)); - renderArgs->_context->render(batch); -} - - -static gpu::BufferPointer _hemiVertices; -static gpu::BufferPointer _hemiIndices; -static int _hemiIndexCount{ 0 }; - -glm::vec2 getPolarCoordinates(const PalmData& palm) { - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - auto avatarOrientation = myAvatar->getOrientation(); - auto eyePos = myAvatar->getDefaultEyePosition(); - glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm); - // Direction of the tip relative to the eye - glm::vec3 tipDirection = tip - eyePos; - // orient into avatar space - tipDirection = glm::inverse(avatarOrientation) * tipDirection; - // Normalize for trig functions - tipDirection = glm::normalize(tipDirection); - // Convert to polar coordinates - glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y)); - return polar; -} - -// Draws the FBO texture for Oculus rift. -void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera) { - if (_alpha == 0.0f || !_framebufferObject) { - return; - } - - renderArgs->_context->syncCache(); - - gpu::Batch batch; - batch.setPipeline(getDrawPipeline()); - batch._glDisable(GL_DEPTH_TEST); - batch._glDisable(GL_CULL_FACE); - batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); - batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - batch.setProjectionTransform(whichCamera.getProjection()); - batch.setViewTransform(Transform()); - - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - const quat& avatarOrientation = myAvatar->getOrientation(); - quat hmdOrientation = qApp->getCamera()->getHmdRotation(); - vec3 hmdPosition = glm::inverse(avatarOrientation) * qApp->getCamera()->getHmdPosition(); - mat4 overlayXfm = glm::mat4_cast(glm::inverse(hmdOrientation)) * glm::translate(mat4(), -hmdPosition); - batch.setModelTransform(Transform(overlayXfm)); - drawSphereSection(batch); - - - bindCursorTexture(batch); - auto geometryCache = DependencyManager::get(); - vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize); - //Controller Pointers - for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { - PalmData& palm = myAvatar->getHand()->getPalms()[i]; - if (palm.isActive()) { - glm::vec2 polar = getPolarCoordinates(palm); - // Convert to quaternion - mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); - mat4 reticleXfm = overlayXfm * pointerXfm; - reticleXfm = glm::scale(reticleXfm, reticleScale); - batch.setModelTransform(reticleXfm); - // Render reticle at location - geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); - } - } - - //Mouse Pointer - if (_reticleActive[MOUSE]) { - glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(), - _reticlePosition[MOUSE].y())); - mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); - mat4 reticleXfm = overlayXfm * pointerXfm; - reticleXfm = glm::scale(reticleXfm, reticleScale); - batch.setModelTransform(reticleXfm); - geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); - } - - renderArgs->_context->render(batch); -} - - -void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const { - cursorPos *= qApp->getCanvasSize(); - const glm::vec2 projection = screenToSpherical(cursorPos); - // The overlay space orientation of the mouse coordinates - const glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f)); - // FIXME We now have the direction of the ray FROM THE DEFAULT HEAD POSE. - // Now we need to account for the actual camera position relative to the overlay - glm::vec3 overlaySpaceDirection = glm::normalize(orientation * IDENTITY_FRONT); - - - const glm::vec3& hmdPosition = qApp->getCamera()->getHmdPosition(); - const glm::quat& hmdOrientation = qApp->getCamera()->getHmdRotation(); - - // We need the RAW camera orientation and position, because this is what the overlay is - // rendered relative to - const glm::vec3 overlayPosition = qApp->getCamera()->getPosition() - hmdPosition; - const glm::quat overlayOrientation = qApp->getCamera()->getRotation() * glm::inverse(hmdOrientation); - - // Intersection UI overlay space - glm::vec3 worldSpaceDirection = overlayOrientation * overlaySpaceDirection; - glm::vec3 intersectionWithUi = glm::normalize(worldSpaceDirection) * _oculusUIRadius; - intersectionWithUi += overlayPosition; - - // Intersection in world space - origin = overlayPosition + hmdPosition; - direction = glm::normalize(intersectionWithUi - origin); -} - -//Caculate the click location using one of the sixense controllers. Scale is not applied -QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { - QPoint rv; - auto canvasSize = qApp->getCanvasSize(); - if (qApp->isHMDMode()) { - glm::vec2 polar = getPolarCoordinates(*palm); - glm::vec2 point = sphericalToScreen(-polar); - rv.rx() = point.x; - rv.ry() = point.y; - } else { - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - glm::dmat4 projection; - qApp->getProjectionMatrix(&projection); - glm::quat invOrientation = glm::inverse(myAvatar->getOrientation()); - glm::vec3 eyePos = myAvatar->getDefaultEyePosition(); - glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm); - glm::vec3 tipPos = invOrientation * (tip - eyePos); - - glm::vec4 clipSpacePos = glm::vec4(projection * glm::dvec4(tipPos, 1.0)); - glm::vec3 ndcSpacePos; - if (clipSpacePos.w != 0) { - ndcSpacePos = glm::vec3(clipSpacePos) / clipSpacePos.w; - } - - rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * canvasSize.x); - rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * canvasSize.y); - } - return rv; -} - -//Finds the collision point of a world space ray -bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const { - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - - glm::quat inverseOrientation = glm::inverse(myAvatar->getOrientation()); - - glm::vec3 relativePosition = inverseOrientation * (position - myAvatar->getDefaultEyePosition()); - glm::vec3 relativeDirection = glm::normalize(inverseOrientation * direction); - - float t; - if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getScale(), &t)){ - result = position + direction * t; - return true; - } - - return false; -} - -//Renders optional pointers -void ApplicationOverlay::renderPointers() { - //glEnable(GL_TEXTURE_2D); - //glEnable(GL_BLEND); - //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - //glActiveTexture(GL_TEXTURE0); - //bindCursorTexture(); - - if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) { - //If we are in oculus, render reticle later - if (_lastMouseMove == 0) { - _lastMouseMove = usecTimestampNow(); - } - QPoint position = QPoint(qApp->getTrueMouseX(), qApp->getTrueMouseY()); - - static const int MAX_IDLE_TIME = 3; - if (_reticlePosition[MOUSE] != position) { - _lastMouseMove = usecTimestampNow(); - } else if (usecTimestampNow() - _lastMouseMove > MAX_IDLE_TIME * USECS_PER_SECOND) { - //float pitch = 0.0f, yaw = 0.0f, roll = 0.0f; // radians - //OculusManager::getEulerAngles(yaw, pitch, roll); - glm::quat orientation = qApp->getHeadOrientation(); // (glm::vec3(pitch, yaw, roll)); - glm::vec3 result; - - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - if (calculateRayUICollisionPoint(myAvatar->getEyePosition(), - myAvatar->getOrientation() * orientation * IDENTITY_FRONT, - result)) { - glm::vec3 lookAtDirection = glm::inverse(myAvatar->getOrientation()) * (result - myAvatar->getDefaultEyePosition()); - glm::vec2 spericalPos = directionToSpherical(glm::normalize(lookAtDirection)); - glm::vec2 screenPos = sphericalToScreen(spericalPos); - position = QPoint(screenPos.x, screenPos.y); - // FIXME - //glCanvas->cursor().setPos(glCanvas->mapToGlobal(position)); - } else { - qDebug() << "No collision point"; - } - } - - _reticlePosition[MOUSE] = position; - _reticleActive[MOUSE] = true; - _magActive[MOUSE] = _magnifier; - _reticleActive[LEFT_CONTROLLER] = false; - _reticleActive[RIGHT_CONTROLLER] = false; - } else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { - _lastMouseMove = 0; - //only render controller pointer if we aren't already rendering a mouse pointer - _reticleActive[MOUSE] = false; - _magActive[MOUSE] = false; - renderControllerPointers(); - } - //glBindTexture(GL_TEXTURE_2D, 0); - //glDisable(GL_TEXTURE_2D); -} - -void ApplicationOverlay::renderControllerPointers() { - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - - //Static variables used for storing controller state - static quint64 pressedTime[NUMBER_OF_RETICLES] = { 0ULL, 0ULL, 0ULL }; - static bool isPressed[NUMBER_OF_RETICLES] = { false, false, false }; - static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false }; - - const HandData* handData = DependencyManager::get()->getMyAvatar()->getHandData(); - - for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { - const int index = palmIndex - 1; - - const PalmData* palmData = NULL; - - if (palmIndex >= handData->getPalms().size()) { - return; - } - - if (handData->getPalms()[palmIndex].isActive()) { - palmData = &handData->getPalms()[palmIndex]; - } else { - continue; - } - - int controllerButtons = palmData->getControllerButtons(); - - //Check for if we should toggle or drag the magnification window - if (controllerButtons & BUTTON_3) { - if (isPressed[index] == false) { - //We are now dragging the window - isPressed[index] = true; - //set the pressed time in us - pressedTime[index] = usecTimestampNow(); - stateWhenPressed[index] = _magActive[index]; - } - } else if (isPressed[index]) { - isPressed[index] = false; - //If the button was only pressed for < 250 ms - //then disable it. - - const int MAX_BUTTON_PRESS_TIME = 250 * MSECS_TO_USECS; - if (usecTimestampNow() < pressedTime[index] + MAX_BUTTON_PRESS_TIME) { - _magActive[index] = !stateWhenPressed[index]; - } - } - - //if we have the oculus, we should make the cursor smaller since it will be - //magnified - if (qApp->isHMDMode()) { - - QPoint point = getPalmClickLocation(palmData); - - _reticlePosition[index] = point; - - //When button 2 is pressed we drag the mag window - if (isPressed[index]) { - _magActive[index] = true; - } - - // If oculus is enabled, we draw the crosshairs later - continue; - } - - auto canvasSize = qApp->getCanvasSize(); - int mouseX, mouseY; - if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { - QPoint res = getPalmClickLocation(palmData); - mouseX = res.x(); - mouseY = res.y(); - } else { - // Get directon relative to avatar orientation - glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); - - // Get the angles, scaled between (-0.5,0.5) - float xAngle = (atan2(direction.z, direction.x) + M_PI_2); - float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); - - // Get the pixel range over which the xAngle and yAngle are scaled - float cursorRange = canvasSize.x * SixenseManager::getInstance().getCursorPixelRangeMult(); - - mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle); - mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle); - } - - //If the cursor is out of the screen then don't render it - if (mouseX < 0 || mouseX >= (int)canvasSize.x || mouseY < 0 || mouseY >= (int)canvasSize.y) { - _reticleActive[index] = false; - continue; - } - _reticleActive[index] = true; - - - const float reticleSize = 40.0f; - - mouseX -= reticleSize / 2.0f; - mouseY += reticleSize / 2.0f; - - - glm::vec2 topLeft(mouseX, mouseY); - glm::vec2 bottomRight(mouseX + reticleSize, mouseY - reticleSize); - glm::vec2 texCoordTopLeft(0.0f, 0.0f); - glm::vec2 texCoordBottomRight(1.0f, 1.0f); - - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, - glm::vec4(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f)); - - } -} - -//Renders a small magnification of the currently bound texture at the coordinates -void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder) { - if (!_magnifier) { - return; - } - auto canvasSize = qApp->getCanvasSize(); - - const int widgetWidth = canvasSize.x; - const int widgetHeight = canvasSize.y; - - const float halfWidth = (MAGNIFY_WIDTH / _textureAspectRatio) * sizeMult / 2.0f; - const float halfHeight = MAGNIFY_HEIGHT * sizeMult / 2.0f; - // Magnification Texture Coordinates - const float magnifyULeft = (magPos.x - halfWidth) / (float)widgetWidth; - const float magnifyURight = (magPos.x + halfWidth) / (float)widgetWidth; - const float magnifyVTop = 1.0f - (magPos.y - halfHeight) / (float)widgetHeight; - const float magnifyVBottom = 1.0f - (magPos.y + halfHeight) / (float)widgetHeight; - - const float newHalfWidth = halfWidth * MAGNIFY_MULT; - const float newHalfHeight = halfHeight * MAGNIFY_MULT; - //Get yaw / pitch value for the corners - const glm::vec2 topLeftYawPitch = overlayToSpherical(glm::vec2(magPos.x - newHalfWidth, - magPos.y - newHalfHeight)); - const glm::vec2 bottomRightYawPitch = overlayToSpherical(glm::vec2(magPos.x + newHalfWidth, - magPos.y + newHalfHeight)); - - const glm::vec3 bottomLeft = getPoint(topLeftYawPitch.x, bottomRightYawPitch.y); - const glm::vec3 bottomRight = getPoint(bottomRightYawPitch.x, bottomRightYawPitch.y); - const glm::vec3 topLeft = getPoint(topLeftYawPitch.x, topLeftYawPitch.y); - const glm::vec3 topRight = getPoint(bottomRightYawPitch.x, topLeftYawPitch.y); - - auto geometryCache = DependencyManager::get(); - - if (bottomLeft != _previousMagnifierBottomLeft || bottomRight != _previousMagnifierBottomRight - || topLeft != _previousMagnifierTopLeft || topRight != _previousMagnifierTopRight) { - QVector border; - border << topLeft; - border << bottomLeft; - border << bottomRight; - border << topRight; - border << topLeft; - geometryCache->updateVertices(_magnifierBorder, border, glm::vec4(1.0f, 0.0f, 0.0f, _alpha)); - - _previousMagnifierBottomLeft = bottomLeft; - _previousMagnifierBottomRight = bottomRight; - _previousMagnifierTopLeft = topLeft; - _previousMagnifierTopRight = topRight; - } - - glPushMatrix(); { - if (showBorder) { - glDisable(GL_TEXTURE_2D); - glLineWidth(1.0f); - //Outer Line - geometryCache->renderVertices(gpu::LINE_STRIP, _magnifierBorder); - glEnable(GL_TEXTURE_2D); - } - glm::vec4 magnifierColor = { 1.0f, 1.0f, 1.0f, _alpha }; - - DependencyManager::get()->renderQuad(bottomLeft, bottomRight, topRight, topLeft, - glm::vec2(magnifyULeft, magnifyVBottom), - glm::vec2(magnifyURight, magnifyVBottom), - glm::vec2(magnifyURight, magnifyVTop), - glm::vec2(magnifyULeft, magnifyVTop), - magnifierColor, _magnifierQuad); - - } glPopMatrix(); -} - -const int AUDIO_METER_GAP = 5; -const int MUTE_ICON_PADDING = 10; - void ApplicationOverlay::renderCameraToggle() { if (Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)) { return; @@ -915,101 +350,10 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder() { } } - -void ApplicationOverlay::buildHemiVertices( - const float fov, const float aspectRatio, const int slices, const int stacks) { - static float textureFOV = 0.0f, textureAspectRatio = 1.0f; - if (textureFOV == fov && textureAspectRatio == aspectRatio) { - return; - } - - textureFOV = fov; - textureAspectRatio = aspectRatio; - - auto geometryCache = DependencyManager::get(); - - _hemiVertices = gpu::BufferPointer(new gpu::Buffer()); - _hemiIndices = gpu::BufferPointer(new gpu::Buffer()); - - - if (fov >= PI) { - qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues"; - } - - //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm - - vec3 pos; - vec2 uv; - // Compute vertices positions and texture UV coordinate - // Create and write to buffer - for (int i = 0; i < stacks; i++) { - uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f - // abs(theta) <= fov / 2.0f - float pitch = -fov * (uv.y - 0.5f); - for (int j = 0; j < slices; j++) { - uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f - // abs(phi) <= fov * aspectRatio / 2.0f - float yaw = -fov * aspectRatio * (uv.x - 0.5f); - pos = getPoint(yaw, pitch); - static const vec4 color(1); - _hemiVertices->append(sizeof(pos), (gpu::Byte*)&pos); - _hemiVertices->append(sizeof(vec2), (gpu::Byte*)&uv); - _hemiVertices->append(sizeof(vec4), (gpu::Byte*)&color); - } - } - - // Compute number of indices needed - static const int VERTEX_PER_TRANGLE = 3; - static const int TRIANGLE_PER_RECTANGLE = 2; - int numberOfRectangles = (slices - 1) * (stacks - 1); - _hemiIndexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; - - // Compute indices order - std::vector indices; - for (int i = 0; i < stacks - 1; i++) { - for (int j = 0; j < slices - 1; j++) { - GLushort bottomLeftIndex = i * slices + j; - GLushort bottomRightIndex = bottomLeftIndex + 1; - GLushort topLeftIndex = bottomLeftIndex + slices; - GLushort topRightIndex = topLeftIndex + 1; - // FIXME make a z-order curve for better vertex cache locality - indices.push_back(topLeftIndex); - indices.push_back(bottomLeftIndex); - indices.push_back(topRightIndex); - - indices.push_back(topRightIndex); - indices.push_back(bottomLeftIndex); - indices.push_back(bottomRightIndex); - } - } - _hemiIndices->append(sizeof(GLushort) * indices.size(), (gpu::Byte*)&indices[0]); -} - - -void ApplicationOverlay::drawSphereSection(gpu::Batch& batch) { - buildHemiVertices(_textureFov, _textureAspectRatio, 80, 80); - static const int VERTEX_DATA_SLOT = 0; - static const int TEXTURE_DATA_SLOT = 1; - static const int COLOR_DATA_SLOT = 2; - gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 1 for everyone - streamFormat->setAttribute(gpu::Stream::POSITION, VERTEX_DATA_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - streamFormat->setAttribute(gpu::Stream::TEXCOORD, TEXTURE_DATA_SLOT, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_DATA_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA)); - batch.setInputFormat(streamFormat); - - static const int VERTEX_STRIDE = sizeof(vec3) + sizeof(vec2) + sizeof(vec4); - gpu::BufferView posView(_hemiVertices, 0, _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::POSITION)._element); - gpu::BufferView uvView(_hemiVertices, sizeof(vec3), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::TEXCOORD)._element); - gpu::BufferView colView(_hemiVertices, sizeof(vec3) + sizeof(vec2), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element); - batch.setInputBuffer(VERTEX_DATA_SLOT, posView); - batch.setInputBuffer(TEXTURE_DATA_SLOT, uvView); - batch.setInputBuffer(COLOR_DATA_SLOT, colView); - batch.setIndexBuffer(gpu::UINT16, _hemiIndices, 0); - batch.drawIndexed(gpu::TRIANGLES, _hemiIndexCount); -} - - GLuint ApplicationOverlay::getOverlayTexture() { + if (!_framebufferObject) { + return 0; + } return _framebufferObject->texture(); } @@ -1036,71 +380,3 @@ void ApplicationOverlay::buildFramebufferObject() { glBindTexture(GL_TEXTURE_2D, 0); } -glm::vec2 ApplicationOverlay::directionToSpherical(const glm::vec3& direction) { - glm::vec2 result; - // Compute yaw - glm::vec3 normalProjection = glm::normalize(glm::vec3(direction.x, 0.0f, direction.z)); - result.x = glm::acos(glm::dot(IDENTITY_FRONT, normalProjection)); - if (glm::dot(IDENTITY_RIGHT, normalProjection) > 0.0f) { - result.x = -glm::abs(result.x); - } else { - result.x = glm::abs(result.x); - } - // Compute pitch - result.y = angleBetween(IDENTITY_UP, direction) - PI_OVER_TWO; - - return result; -} - -glm::vec3 ApplicationOverlay::sphericalToDirection(const glm::vec2& sphericalPos) { - glm::quat rotation(glm::vec3(sphericalPos.y, sphericalPos.x, 0.0f)); - return rotation * IDENTITY_FRONT; -} - -glm::vec2 ApplicationOverlay::screenToSpherical(const glm::vec2& screenPos) { - auto screenSize = qApp->getCanvasSize(); - glm::vec2 result; - result.x = -(screenPos.x / screenSize.x - 0.5f); - result.y = (screenPos.y / screenSize.y - 0.5f); - result.x *= MOUSE_YAW_RANGE; - result.y *= MOUSE_PITCH_RANGE; - - return result; -} - -glm::vec2 ApplicationOverlay::sphericalToScreen(const glm::vec2& sphericalPos) { - glm::vec2 result = sphericalPos; - result.x *= -1.0; - result /= MOUSE_RANGE; - result += 0.5f; - result *= qApp->getCanvasSize(); - return result; -} - -glm::vec2 ApplicationOverlay::sphericalToOverlay(const glm::vec2& sphericalPos) const { - glm::vec2 result = sphericalPos; - result.x *= -1.0; - result /= _textureFov; - result.x /= _textureAspectRatio; - result += 0.5f; - result *= qApp->getCanvasSize(); - return result; -} - -glm::vec2 ApplicationOverlay::overlayToSpherical(const glm::vec2& overlayPos) const { - glm::vec2 result = overlayPos; - result /= qApp->getCanvasSize(); - result -= 0.5f; - result *= _textureFov; - result.x *= _textureAspectRatio; - result.x *= -1.0f; - return result; -} - -glm::vec2 ApplicationOverlay::screenToOverlay(const glm::vec2& screenPos) const { - return sphericalToOverlay(screenToSpherical(screenPos)); -} - -glm::vec2 ApplicationOverlay::overlayToScreen(const glm::vec2& overlayPos) const { - return sphericalToScreen(overlayToSpherical(overlayPos)); -} diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index e8b5a77b1f..dc21fba194 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -13,16 +13,8 @@ #define hifi_ApplicationOverlay_h #include -class Camera; -class Overlays; class QOpenGLFramebufferObject; -const float MAGNIFY_WIDTH = 220.0f; -const float MAGNIFY_HEIGHT = 100.0f; -const float MAGNIFY_MULT = 2.0f; - -const float DEFAULT_HMD_UI_ANGULAR_SIZE = 72.0f; - // Handles the drawing of the overlays to the screen // TODO, move divide up the rendering, displaying and input handling // facilities of this class @@ -33,83 +25,19 @@ public: ~ApplicationOverlay(); void renderOverlay(RenderArgs* renderArgs); - void displayOverlayTexture(RenderArgs* renderArgs); - void displayOverlayTextureStereo(RenderArgs* renderArgs, Camera& whichCamera, float aspectRatio, float fov); - void displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera); - - QPoint getPalmClickLocation(const PalmData *palm) const; - bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; - - bool hasMagnifier() const { return _magnifier; } - void toggleMagnifier() { _magnifier = !_magnifier; } - - float getHmdUIAngularSize() const { return _hmdUIAngularSize; } - void setHmdUIAngularSize(float hmdUIAngularSize) { _hmdUIAngularSize = hmdUIAngularSize; } - - // Converter from one frame of reference to another. - // Frame of reference: - // Direction: Ray that represents the spherical values - // Screen: Position on the screen (x,y) - // Spherical: Pitch and yaw that gives the position on the sphere we project on (yaw,pitch) - // Overlay: Position on the overlay (x,y) - // (x,y) in Overlay are similar than (x,y) in Screen except they can be outside of the bound of te screen. - // This allows for picking outside of the screen projection in 3D. - glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const; - glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const; - glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const; - glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const; - void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const; GLuint getOverlayTexture(); - static glm::vec2 directionToSpherical(const glm::vec3 & direction); - static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos); - static glm::vec2 screenToSpherical(const glm::vec2 & screenPos); - static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos); - private: - void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks); - void drawSphereSection(gpu::Batch& batch); - float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; - QOpenGLFramebufferObject* _framebufferObject; - void renderPointers(); - void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder); - - void renderControllerPointers(); - void renderPointersOculus(); - void renderAudioMeter(); void renderCameraToggle(); void renderStatsAndLogs(); void renderDomainConnectionStatusBorder(); - void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0); - void buildFramebufferObject(); - - float _textureFov; - float _textureAspectRatio; - int _hemiVerticesID{ GeometryCache::UNKNOWN_ID }; - - - enum Reticles { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_RETICLES }; - bool _reticleActive[NUMBER_OF_RETICLES]; - QPoint _reticlePosition[NUMBER_OF_RETICLES]; - bool _magActive[NUMBER_OF_RETICLES]; - float _magSizeMult[NUMBER_OF_RETICLES]; - quint64 _lastMouseMove; - bool _magnifier; - float _alpha = 1.0f; - float _oculusUIRadius; float _trailingAudioLoudness; - - - QMap _cursors; - - GLuint _newUiTexture{ 0 }; + GLuint _uiTexture{ 0 }; - int _reticleQuad; - int _magnifierQuad; int _audioRedQuad; int _audioGreenQuad; int _audioBlueQuad; @@ -119,15 +47,7 @@ private: int _previousBorderWidth; int _previousBorderHeight; - glm::vec3 _previousMagnifierBottomLeft; - glm::vec3 _previousMagnifierBottomRight; - glm::vec3 _previousMagnifierTopLeft; - glm::vec3 _previousMagnifierTopRight; - - gpu::PipelinePointer _standardDrawPipeline; - - gpu::PipelinePointer getDrawPipeline(); - + QOpenGLFramebufferObject* _framebufferObject; }; #endif // hifi_ApplicationOverlay_h diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index febd6f90bf..eca250a428 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -175,7 +175,7 @@ void PreferencesDialog::loadPreferences() { ui.maxOctreePPSSpin->setValue(qApp->getMaxOctreePacketsPerSecond()); - ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationOverlay().getHmdUIAngularSize()); + ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationCompositor().getHmdUIAngularSize()); SixenseManager& sixense = SixenseManager::getInstance(); ui.sixenseReticleMoveSpeedSpin->setValue(sixense.getReticleMoveSpeed()); @@ -239,7 +239,7 @@ void PreferencesDialog::savePreferences() { qApp->setMaxOctreePacketsPerSecond(ui.maxOctreePPSSpin->value()); - qApp->getApplicationOverlay().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value()); + qApp->getApplicationCompositor().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value()); SixenseManager& sixense = SixenseManager::getInstance(); sixense.setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value()); diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 6e579ed4c4..1bb81319e4 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -258,7 +258,7 @@ void Overlays::deleteOverlay(unsigned int id) { unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { glm::vec2 pointCopy = point; if (qApp->isHMDMode()) { - pointCopy = qApp->getApplicationOverlay().screenToOverlay(point); + pointCopy = qApp->getApplicationCompositor().screenToOverlay(point); } QReadLocker lock(&_lock); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 3066fd4890..98067e1b81 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -28,6 +28,9 @@ #include "RenderUtilsLogging.h" #include "GeometryCache.h" +#include "standardTransformPNTC_vert.h" +#include "standardDrawTexture_frag.h" + //#define WANT_DEBUG const int GeometryCache::UNKNOWN_ID = -1; @@ -1817,6 +1820,23 @@ QSharedPointer GeometryCache::createResource(const QUrl& url, const QS return geometry.staticCast(); } +void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch) { + if (!_standardDrawPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(standardDrawTexture_frag))); + auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + gpu::Shader::makeProgram((*program)); + + auto state = gpu::StatePointer(new gpu::State()); + + // enable decal blend + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); + + _standardDrawPipeline.reset(gpu::Pipeline::create(program, state)); + } + batch.setPipeline(_standardDrawPipeline); +} + const float NetworkGeometry::NO_HYSTERESIS = -1.0f; NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, @@ -2385,3 +2405,4 @@ int NetworkMesh::getTranslucentPartCount(const FBXMesh& fbxMesh) const { } return count; } + diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 76e03f8669..2155586f4b 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -252,6 +252,9 @@ public: /// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested QSharedPointer getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false); + /// Set a batch to the simple pipeline, returning the previous pipeline + void useSimpleDrawPipeline(gpu::Batch& batch); + protected: virtual QSharedPointer createResource(const QUrl& url, @@ -269,6 +272,7 @@ private: int vertexSize; }; + gpu::PipelinePointer _standardDrawPipeline; QHash _cubeVerticies; QHash _cubeColors; gpu::BufferPointer _wireCubeIndexBuffer; @@ -276,7 +280,7 @@ private: QHash _solidCubeVertices; QHash _solidCubeColors; gpu::BufferPointer _solidCubeIndexBuffer; - + class BatchItemDetails { public: static int population; From ba13b7820c751bf9c18b06f8c62974cd47010227 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 16 Jun 2015 09:58:03 -0700 Subject: [PATCH 05/35] Working on overlay refactor --- interface/src/Application.cpp | 249 +++++++++-------- interface/src/Application.h | 37 +-- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/ui/ApplicationCompositor.cpp | 205 ++++++++++++-- interface/src/ui/ApplicationCompositor.h | 30 +- interface/src/ui/ApplicationOverlay.cpp | 288 ++++++++++++++------ interface/src/ui/ApplicationOverlay.h | 26 +- libraries/entities/src/EntityItemID.h | 2 +- libraries/render-utils/src/TextRenderer.cpp | 21 ++ libraries/shared/src/GLMHelpers.h | 15 + 10 files changed, 590 insertions(+), 287 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b540751c8d..6270c8f597 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -321,15 +321,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _viewFrustum(), _lastQueriedViewFrustum(), _lastQueriedTime(usecTimestampNow()), - _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _firstRun("firstRun", true), _previousScriptLocation("LastScriptLocation"), _scriptsLocationHandle("scriptsLocation"), _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), _viewTransform(), - _scaleMirror(1.0f), - _rotateMirror(0.0f), - _raiseMirror(0.0f), _cursorVisible(true), _lastMouseMove(usecTimestampNow()), _lastMouseMoveWasSimulated(false), @@ -905,11 +901,13 @@ void Application::paintGL() { } } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { +#if 0 _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0) + (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); +#endif } // Update camera position @@ -951,14 +949,13 @@ void Application::paintGL() { _compositor.displayOverlayTexture(&renderArgs); glPopMatrix(); - renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; - if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { - _rearMirrorTools->render(&renderArgs, true, _glWidget->mapFromGlobal(QCursor::pos())); - } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { - renderRearViewMirror(&renderArgs, _mirrorViewRect); - } - - renderArgs._renderMode = RenderArgs::NORMAL_RENDER_MODE; + //renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; + //if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { + // _rearMirrorTools->render(&renderArgs, true, _glWidget->mapFromGlobal(QCursor::pos())); + //} else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { + // renderRearViewMirror(&renderArgs, _mirrorViewRect); + //} + //renderArgs._renderMode = RenderArgs::NORMAL_RENDER_MODE; auto finalFbo = DependencyManager::get()->render(&renderArgs); @@ -1050,10 +1047,12 @@ void Application::resizeGL() { offscreenUi->resize(_glWidget->size()); _glWidget->makeCurrent(); +#if 0 // update Stats width // let's set horizontal offset to give stats some margin to mirror int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; Stats::getInstance()->resetWidth(_renderResolution.x, horizontalOffset); +#endif } void Application::updateProjectionMatrix() { @@ -1269,37 +1268,37 @@ void Application::keyPressEvent(QKeyEvent* event) { Menu::getInstance()->triggerOption(MenuOption::Chat); break; - case Qt::Key_Up: - if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - if (!isShifted) { - _scaleMirror *= 0.95f; - } else { - _raiseMirror += 0.05f; - } - } - break; + //case Qt::Key_Up: + // if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + // if (!isShifted) { + // _scaleMirror *= 0.95f; + // } else { + // _raiseMirror += 0.05f; + // } + // } + // break; - case Qt::Key_Down: - if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - if (!isShifted) { - _scaleMirror *= 1.05f; - } else { - _raiseMirror -= 0.05f; - } - } - break; + //case Qt::Key_Down: + // if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + // if (!isShifted) { + // _scaleMirror *= 1.05f; + // } else { + // _raiseMirror -= 0.05f; + // } + // } + // break; - case Qt::Key_Left: - if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - _rotateMirror += PI / 20.0f; - } - break; + //case Qt::Key_Left: + // if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + // _rotateMirror += PI / 20.0f; + // } + // break; - case Qt::Key_Right: - if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - _rotateMirror -= PI / 20.0f; - } - break; + //case Qt::Key_Right: + // if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + // _rotateMirror -= PI / 20.0f; + // } + // break; #if 0 case Qt::Key_I: @@ -1562,11 +1561,12 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { // stop propagation return; } - +#if 0 if (_rearMirrorTools->mousePressEvent(getMouseX(), getMouseY())) { // stop propagation return; } +#endif } // nobody handled this - make it an action event on the _window object @@ -1617,12 +1617,14 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { if (event->button() == Qt::LeftButton) { _mousePressed = false; +#if 0 if (Menu::getInstance()->isOptionChecked(MenuOption::Stats) && mouseOnScreen()) { // let's set horizontal offset to give stats some margin to mirror int horizontalOffset = MIRROR_VIEW_WIDTH; Stats::getInstance()->checkClick(getMouseX(), getMouseY(), getMouseDragStartedX(), getMouseDragStartedY(), horizontalOffset); } +#endif // fire an action end event HFActionEvent actionEvent(HFActionEvent::endType(), @@ -2137,7 +2139,9 @@ void Application::init() { DependencyManager::get()->init(); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); +#if 0 _mirrorCamera.setMode(CAMERA_MODE_MIRROR); +#endif OculusManager::connect(); if (OculusManager::isConnected()) { @@ -2209,12 +2213,13 @@ void Application::init() { _entityClipboardRenderer.setViewFrustum(getViewFrustum()); _entityClipboardRenderer.setTree(&_entityClipboard); +#if 0 _rearMirrorTools = new RearMirrorTools(_mirrorViewRect); - connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView())); connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView())); connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView())); connect(_rearMirrorTools, SIGNAL(resetView()), SLOT(resetSensors())); +#endif // initialize the GlowEffect with our widget bool glow = Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect); @@ -3115,6 +3120,7 @@ PickRay Application::computePickRay(float x, float y) const { return result; } +#if 0 QImage Application::renderAvatarBillboard(RenderArgs* renderArgs) { auto primaryFramebuffer = DependencyManager::get()->getPrimaryFramebuffer(); glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFramebuffer)); @@ -3133,15 +3139,14 @@ QImage Application::renderAvatarBillboard(RenderArgs* renderArgs) { renderRearViewMirror(renderArgs, QRect(0, _glWidget->getDeviceHeight() - BILLBOARD_SIZE, BILLBOARD_SIZE, BILLBOARD_SIZE), true); - QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glBindFramebuffer(GL_FRAMEBUFFER, 0); - return image; } +#endif ViewFrustum* Application::getViewFrustum() { #ifdef DEBUG @@ -3331,9 +3336,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se // flip x if in mirror mode (also requires reversing winding order for backface culling) if (theCamera.getMode() == CAMERA_MODE_MIRROR) { - glScalef(-1.0f, 1.0f, 1.0f); - glFrontFace(GL_CW); - + //glScalef(-1.0f, 1.0f, 1.0f); + //glFrontFace(GL_CW); } else { glFrontFace(GL_CCW); } @@ -3356,7 +3360,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se viewTransform.setTranslation(theCamera.getPosition()); viewTransform.setRotation(rotation); if (theCamera.getMode() == CAMERA_MODE_MIRROR) { - viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f)); + //viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f)); } if (renderArgs->_renderSide != RenderArgs::MONO) { glm::mat4 invView = glm::inverse(_untranslatedViewMatrix); @@ -3623,78 +3627,83 @@ glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { return screenPoint; } -void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool billboard) { - // Grab current viewport to reset it at the end - int viewport[4]; - glGetIntegerv(GL_VIEWPORT, viewport); - float aspect = (float)region.width() / region.height(); - float fov = MIRROR_FIELD_OF_VIEW; - - // bool eyeRelativeCamera = false; - if (billboard) { - fov = BILLBOARD_FIELD_OF_VIEW; // degees - _mirrorCamera.setPosition(_myAvatar->getPosition() + - _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale()); - - } else if (RearMirrorTools::rearViewZoomLevel.get() == BODY) { - _mirrorCamera.setPosition(_myAvatar->getChestPosition() + - _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); - - } else { // HEAD zoom level - // FIXME note that the positioing of the camera relative to the avatar can suffer limited - // precision as the user's position moves further away from the origin. Thus at - // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways - // wildly as you rotate your avatar because the floating point values are becoming - // larger, squeezing out the available digits of precision you have available at the - // human scale for camera positioning. - - // Previously there was a hack to correct this using the mechanism of repositioning - // the avatar at the origin of the world for the purposes of rendering the mirror, - // but it resulted in failing to render the avatar's head model in the mirror view - // when in first person mode. Presumably this was because of some missed culling logic - // that was not accounted for in the hack. - - // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further - // investigated in order to adapt the technique while fixing the head rendering issue, - // but the complexity of the hack suggests that a better approach - _mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() + - _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); - } - _mirrorCamera.setProjection(glm::perspective(glm::radians(fov), aspect, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); - _mirrorCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); - - // set the bounds of rear mirror view - if (billboard) { - QSize size = DependencyManager::get()->getFrameBufferSize(); - glViewport(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); - glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); - } else { - // if not rendering the billboard, the region is in device independent coordinates; must convert to device - QSize size = DependencyManager::get()->getFrameBufferSize(); - float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale(); - int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio; - glViewport(x, size.height() - y - height, width, height); - glScissor(x, size.height() - y - height, width, height); - } - bool updateViewFrustum = false; - updateProjectionMatrix(_mirrorCamera, updateViewFrustum); - glEnable(GL_SCISSOR_TEST); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // render rear mirror view - glPushMatrix(); - displaySide(renderArgs, _mirrorCamera, true, billboard); - glPopMatrix(); - - if (!billboard) { - _rearMirrorTools->render(renderArgs, false, _glWidget->mapFromGlobal(QCursor::pos())); - } - - // reset Viewport and projection matrix - glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); - glDisable(GL_SCISSOR_TEST); - updateProjectionMatrix(_myCamera, updateViewFrustum); -} +//void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool billboard) { +// // Grab current viewport to reset it at the end +// int viewport[4]; +// glGetIntegerv(GL_VIEWPORT, viewport); +// float aspect = (float)region.width() / region.height(); +// float fov = MIRROR_FIELD_OF_VIEW; +// +// // bool eyeRelativeCamera = false; +// if (billboard) { +// fov = BILLBOARD_FIELD_OF_VIEW; // degees +// _mirrorCamera.setPosition(_myAvatar->getPosition() + +// _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale()); +// +// } else if (RearMirrorTools::rearViewZoomLevel.get() == BODY) { +// _mirrorCamera.setPosition(_myAvatar->getChestPosition() + +// _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); +// +// } else { // HEAD zoom level +// // FIXME note that the positioing of the camera relative to the avatar can suffer limited +// // precision as the user's position moves further away from the origin. Thus at +// // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways +// // wildly as you rotate your avatar because the floating point values are becoming +// // larger, squeezing out the available digits of precision you have available at the +// // human scale for camera positioning. +// +// // Previously there was a hack to correct this using the mechanism of repositioning +// // the avatar at the origin of the world for the purposes of rendering the mirror, +// // but it resulted in failing to render the avatar's head model in the mirror view +// // when in first person mode. Presumably this was because of some missed culling logic +// // that was not accounted for in the hack. +// +// // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further +// // investigated in order to adapt the technique while fixing the head rendering issue, +// // but the complexity of the hack suggests that a better approach +// _mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() + +// _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); +// } +// _mirrorCamera.setProjection(glm::perspective(glm::radians(fov), aspect, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); +// _mirrorCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); +// +// // set the bounds of rear mirror view +// if (billboard) { +// QSize size = DependencyManager::get()->getFrameBufferSize(); +// glViewport(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); +// glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); +// } else { +// // if not rendering the billboard, the region is in device independent coordinates; must convert to device +// QSize size = DependencyManager::get()->getFrameBufferSize(); +// float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale(); +// int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio; +// glViewport(x, size.height() - y - height, width, height); +// glScissor(x, size.height() - y - height, width, height); +// } +// bool updateViewFrustum = false; +// updateProjectionMatrix(_mirrorCamera, updateViewFrustum); +// glEnable(GL_SCISSOR_TEST); +// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +// +// // render rear mirror view +// glMatrixMode(GL_MODELVIEW); +// glPushMatrix(); +// glLoadIdentity(); +// glLoadMatrixf(glm::value_ptr(glm::mat4_cast(_mirrorCamera.getOrientation()) * glm::translate(glm::mat4(), _mirrorCamera.getPosition()))); +// renderArgs->_context->syncCache(); +// displaySide(renderArgs, _mirrorCamera, true, billboard); +// glMatrixMode(GL_MODELVIEW); +// glPopMatrix(); +// +// if (!billboard) { +// _rearMirrorTools->render(renderArgs, false, _glWidget->mapFromGlobal(QCursor::pos())); +// } +// +// // reset Viewport and projection matrix +// glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); +// glDisable(GL_SCISSOR_TEST); +// updateProjectionMatrix(_myCamera, updateViewFrustum); +//} void Application::resetSensors() { DependencyManager::get()->reset(); diff --git a/interface/src/Application.h b/interface/src/Application.h index bb5c50687a..6f30bbf560 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -109,15 +109,6 @@ static const QString FST_EXTENSION = ".fst"; static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; // degrees static const float BILLBOARD_DISTANCE = 5.56f; // meters -static const int MIRROR_VIEW_TOP_PADDING = 5; -static const int MIRROR_VIEW_LEFT_PADDING = 10; -static const int MIRROR_VIEW_WIDTH = 265; -static const int MIRROR_VIEW_HEIGHT = 215; -static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; -static const float MIRROR_REARVIEW_DISTANCE = 0.722f; -static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f; -static const float MIRROR_FIELD_OF_VIEW = 30.0f; - static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; static const QString INFO_HELP_PATH = "html/interface-welcome.html"; @@ -482,6 +473,7 @@ private slots: void faceTrackerMuteToggled(); void setCursorVisible(bool visible); + //void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool billboard = false); private: void resetCamerasOnResizeGL(Camera& camera, const glm::uvec2& size); @@ -516,7 +508,6 @@ private: glm::vec3 getSunDirection(); void updateShadowMap(RenderArgs* renderArgs); - void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool billboard = false); void setMenuShortcutsEnabled(bool enabled); static void attachNewHeadToNode(Node *newNode); @@ -564,31 +555,21 @@ private: OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers - KeyboardMouseDevice _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad - - UserInputMapper _userInputMapper; // User input mapper allowing to mapp different real devices to the action channels that the application has to offer - - MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be) - - Camera _myCamera; // My view onto the world - Camera _mirrorCamera; // Cammera for mirror view - QRect _mirrorViewRect; - RearMirrorTools* _rearMirrorTools; + KeyboardMouseDevice _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad + UserInputMapper _userInputMapper; // User input mapper allowing to mapp different real devices to the action channels that the application has to offer + MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be) + Camera _myCamera; // My view onto the world - Setting::Handle _firstRun; - Setting::Handle _previousScriptLocation; - Setting::Handle _scriptsLocationHandle; - Setting::Handle _fieldOfView; + Setting::Handle _firstRun; + Setting::Handle _previousScriptLocation; + Setting::Handle _scriptsLocationHandle; + Setting::Handle _fieldOfView; Transform _viewTransform; glm::mat4 _untranslatedViewMatrix; glm::vec3 _viewMatrixTranslation; glm::mat4 _projectionMatrix; - float _scaleMirror; - float _rotateMirror; - float _raiseMirror; - static const int CASCADED_SHADOW_MATRIX_COUNT = 4; glm::mat4 _shadowMatrices[CASCADED_SHADOW_MATRIX_COUNT]; glm::vec3 _shadowDistances; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2b3997077b..e86fd26705 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1458,9 +1458,10 @@ void MyAvatar::maybeUpdateBillboard() { return; } } + /* gpu::Context context(new gpu::GLBackend()); RenderArgs renderArgs(&context); - QImage image = Application::getInstance()->renderAvatarBillboard(&renderArgs); + QImage image = qApp->renderAvatarBillboard(&renderArgs); _billboard.clear(); QBuffer buffer(&_billboard); buffer.open(QIODevice::WriteOnly); @@ -1468,6 +1469,7 @@ void MyAvatar::maybeUpdateBillboard() { _billboardValid = true; sendBillboardPacket(); + */ } void MyAvatar::increaseSize() { diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index f6a79f2bc2..ab5014dfaa 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -41,6 +41,7 @@ static const float MAG_SPEED = 0.08f; static const quint64 MSECS_TO_USECS = 1000ULL; +static const quint64 TOOLTIP_DELAY = 2000000ULL; static const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; static const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; @@ -57,6 +58,8 @@ static const glm::vec2 MOUSE_RANGE(MOUSE_YAW_RANGE, MOUSE_PITCH_RANGE); static gpu::BufferPointer _hemiVertices; static gpu::BufferPointer _hemiIndices; static int _hemiIndexCount{ 0 }; +EntityItemID ApplicationCompositor::_noItemId; + // Return a point's cartesian coordinates on a sphere from pitch and yaw glm::vec3 getPoint(float yaw, float pitch) { @@ -132,11 +135,32 @@ ApplicationCompositor::ApplicationCompositor() { _reticleQuad = geometryCache->allocateID(); _magnifierQuad = geometryCache->allocateID(); - _audioRedQuad = geometryCache->allocateID(); - _audioGreenQuad = geometryCache->allocateID(); - _audioBlueQuad = geometryCache->allocateID(); - _domainStatusBorder = geometryCache->allocateID(); _magnifierBorder = geometryCache->allocateID(); + + auto entityScriptingInterface = DependencyManager::get(); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) { + if (_hoverItemId != entityItemID) { + _hoverItemId = entityItemID; + _hoverItemEnterUsecs = usecTimestampNow(); + auto properties = entityScriptingInterface->getEntityProperties(_hoverItemId); + _hoverItemHref = properties.getHref(); + auto cursor = Cursor::Manager::instance().getCursor(); + if (!_hoverItemHref.isEmpty()) { + cursor->setIcon(Cursor::Icon::LINK); + } else { + cursor->setIcon(Cursor::Icon::DEFAULT); + } + } + }); + + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) { + if (_hoverItemId == entityItemID) { + _hoverItemId = _noItemId; + _hoverItemHref.clear(); + auto cursor = Cursor::Manager::instance().getCursor(); + cursor->setIcon(Cursor::Icon::DEFAULT); + } + }); } ApplicationCompositor::~ApplicationCompositor() { @@ -154,13 +178,26 @@ void ApplicationCompositor::bindCursorTexture(gpu::Batch& batch, uint8_t cursorI batch.setUniformTexture(0, _cursors[iconId]); } - // Draws the FBO texture for the screen void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) { if (_alpha == 0.0f) { return; } + vec2 canvasSize = qApp->getCanvasSize(); + + _textureAspectRatio = aspect(canvasSize); + + if (_hoverItemId != _noItemId) { + quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs; + if (!_hoverItemHref.isEmpty() && hoverDuration > TOOLTIP_DELAY) { + // TODO Enable and position the tooltip + } + } + + //Handle fading and deactivation/activation of UI + gpu::Batch batch; + GLuint texture = qApp->getApplicationOverlay().getOverlayTexture(); if (!texture) { return; @@ -168,7 +205,6 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) { renderArgs->_context->syncCache(); auto geometryCache = DependencyManager::get(); - gpu::Batch batch; geometryCache->useSimpleDrawPipeline(batch); batch.setModelTransform(Transform()); batch.setViewTransform(Transform()); @@ -179,17 +215,12 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) { geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha)); // Doesn't actually render - renderPointers(); - + renderPointers(batch); //draw the mouse pointer - vec2 canvasSize = qApp->getCanvasSize(); - // Get the mouse coordinates and convert to NDC [-1, 1] - vec2 mousePosition = vec2(qApp->getMouse()); - mousePosition /= canvasSize; - mousePosition *= 2.0f; - mousePosition -= 1.0f; + vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize); + // Invert the Y axis mousePosition.y *= -1.0f; Transform model; @@ -250,7 +281,7 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int drawSphereSection(batch); // Doesn't actually render - renderPointers(); + renderPointers(batch); vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize); bindCursorTexture(batch); @@ -360,7 +391,7 @@ bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& positi } //Renders optional pointers -void ApplicationCompositor::renderPointers() { +void ApplicationCompositor::renderPointers(gpu::Batch& batch) { //glEnable(GL_TEXTURE_2D); //glEnable(GL_BLEND); //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -374,7 +405,7 @@ void ApplicationCompositor::renderPointers() { _lastMouseMove = usecTimestampNow(); } QPoint position = QPoint(qApp->getTrueMouseX(), qApp->getTrueMouseY()); - + static const int MAX_IDLE_TIME = 3; if (_reticlePosition[MOUSE] != position) { _lastMouseMove = usecTimestampNow(); @@ -383,11 +414,11 @@ void ApplicationCompositor::renderPointers() { //OculusManager::getEulerAngles(yaw, pitch, roll); glm::quat orientation = qApp->getHeadOrientation(); // (glm::vec3(pitch, yaw, roll)); glm::vec3 result; - + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); if (calculateRayUICollisionPoint(myAvatar->getEyePosition(), - myAvatar->getOrientation() * orientation * IDENTITY_FRONT, - result)) { + myAvatar->getOrientation() * orientation * IDENTITY_FRONT, + result)) { glm::vec3 lookAtDirection = glm::inverse(myAvatar->getOrientation()) * (result - myAvatar->getDefaultEyePosition()); glm::vec2 spericalPos = directionToSpherical(glm::normalize(lookAtDirection)); glm::vec2 screenPos = sphericalToScreen(spericalPos); @@ -398,7 +429,7 @@ void ApplicationCompositor::renderPointers() { qDebug() << "No collision point"; } } - + _reticlePosition[MOUSE] = position; _reticleActive[MOUSE] = true; _magActive[MOUSE] = _magnifier; @@ -409,13 +440,14 @@ void ApplicationCompositor::renderPointers() { //only render controller pointer if we aren't already rendering a mouse pointer _reticleActive[MOUSE] = false; _magActive[MOUSE] = false; - renderControllerPointers(); + renderControllerPointers(batch); } //glBindTexture(GL_TEXTURE_2D, 0); //glDisable(GL_TEXTURE_2D); } + -void ApplicationCompositor::renderControllerPointers() { +void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); //Static variables used for storing controller state @@ -526,7 +558,7 @@ void ApplicationCompositor::renderControllerPointers() { } //Renders a small magnification of the currently bound texture at the coordinates -void ApplicationCompositor::renderMagnifier(const glm::vec2& magPos, float sizeMult, bool showBorder) { +void ApplicationCompositor::renderMagnifier(gpu::Batch& batch, const glm::vec2& magPos, float sizeMult, bool showBorder) { if (!_magnifier) { return; } @@ -755,3 +787,128 @@ glm::vec2 ApplicationCompositor::screenToOverlay(const glm::vec2& screenPos) con glm::vec2 ApplicationCompositor::overlayToScreen(const glm::vec2& overlayPos) const { return sphericalToScreen(overlayToSpherical(overlayPos)); } + +#if 0 + +gpu::PipelinePointer ApplicationOverlay::getDrawPipeline() { + if (!_standardDrawPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(standardDrawTexture_frag))); + auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + gpu::Shader::makeProgram((*program)); + + auto state = gpu::StatePointer(new gpu::State()); + + // enable decal blend + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); + + _standardDrawPipeline.reset(gpu::Pipeline::create(program, state)); + } + + return _standardDrawPipeline; +} + + +// Draws the FBO texture for the screen +void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { + if (_alpha == 0.0f) { + return; + } + + renderArgs->_context->syncCache(); + + gpu::Batch batch; + Transform model; + //DependencyManager::get()->bindSimpleProgram(batch, true); + batch.setPipeline(getDrawPipeline()); + batch.setModelTransform(Transform()); + batch.setProjectionTransform(mat4()); + batch.setViewTransform(model); + batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + DependencyManager::get()->renderUnitQuad(batch, vec4(vec3(1), _alpha)); + + //draw the mouse pointer + glm::vec2 canvasSize = qApp->getCanvasSize(); + + // Get the mouse coordinates and convert to NDC [-1, 1] + vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY()); + mousePosition /= canvasSize; + mousePosition *= 2.0f; + mousePosition -= 1.0f; + mousePosition.y *= -1.0f; + model.setTranslation(vec3(mousePosition, 0)); + glm::vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize; + model.setScale(vec3(mouseSize, 1.0f)); + batch.setModelTransform(model); + bindCursorTexture(batch); + glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; + DependencyManager::get()->renderUnitQuad(batch, vec4(1)); + renderArgs->_context->render(batch); +} + + + +// Draws the FBO texture for Oculus rift. +void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera) { + if (_alpha == 0.0f) { + return; + } + + renderArgs->_context->syncCache(); + + gpu::Batch batch; + batch.setPipeline(getDrawPipeline()); + batch._glDisable(GL_DEPTH_TEST); + batch._glDisable(GL_CULL_FACE); + batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + batch.setProjectionTransform(whichCamera.getProjection()); + batch.setViewTransform(Transform()); + + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + const quat& avatarOrientation = myAvatar->getOrientation(); + quat hmdOrientation = qApp->getCamera()->getHmdRotation(); + vec3 hmdPosition = glm::inverse(avatarOrientation) * qApp->getCamera()->getHmdPosition(); + mat4 overlayXfm = glm::mat4_cast(glm::inverse(hmdOrientation)) * glm::translate(mat4(), -hmdPosition); + batch.setModelTransform(Transform(overlayXfm)); + drawSphereSection(batch); + + + bindCursorTexture(batch); + auto geometryCache = DependencyManager::get(); + vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize); + //Controller Pointers + for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { + PalmData& palm = myAvatar->getHand()->getPalms()[i]; + if (palm.isActive()) { + glm::vec2 polar = getPolarCoordinates(palm); + // Convert to quaternion + mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); + mat4 reticleXfm = overlayXfm * pointerXfm; + reticleXfm = glm::scale(reticleXfm, reticleScale); + batch.setModelTransform(reticleXfm); + // Render reticle at location + geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); + } + } + + //Mouse Pointer + if (_reticleActive[MOUSE]) { + glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(), + _reticlePosition[MOUSE].y())); + mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); + mat4 reticleXfm = overlayXfm * pointerXfm; + reticleXfm = glm::scale(reticleXfm, reticleScale); + batch.setModelTransform(reticleXfm); + geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); + } + + renderArgs->_context->render(batch); +} + + + +#endif diff --git a/interface/src/ui/ApplicationCompositor.h b/interface/src/ui/ApplicationCompositor.h index 61bbafaba7..5145e14006 100644 --- a/interface/src/ui/ApplicationCompositor.h +++ b/interface/src/ui/ApplicationCompositor.h @@ -6,12 +6,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_ApplicationOverlayCompositor_h -#define hifi_ApplicationOverlayCompositor_h +#ifndef hifi_ApplicationCompositor_h +#define hifi_ApplicationCompositor_h #include #include +#include #include #include #include @@ -61,7 +62,7 @@ public: glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const; glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const; void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const; - GLuint getOverlayTexture(); + GLuint getOverlayTexture() const; static glm::vec2 directionToSpherical(const glm::vec3 & direction); static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos); @@ -74,12 +75,17 @@ private: void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks); void drawSphereSection(gpu::Batch& batch); - void renderPointers(); - void renderMagnifier(const glm::vec2& magPos, float sizeMult, bool showBorder); - - void renderControllerPointers(); - void renderPointersOculus(); - + void renderPointers(gpu::Batch& batch); + void renderMagnifier(gpu::Batch& batch, const glm::vec2& magPos, float sizeMult, bool showBorder); + void renderControllerPointers(gpu::Batch& batch); + void renderPointersOculus(gpu::Batch& batch); + + // Support for hovering and tooltips + static EntityItemID _noItemId; + EntityItemID _hoverItemId{ _noItemId }; + QString _hoverItemHref; + quint64 _hoverItemEnterUsecs{ 0 }; + float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; float _textureFov{ glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE) }; float _textureAspectRatio{ 1.0f }; @@ -100,10 +106,6 @@ private: int _reticleQuad; int _magnifierQuad; - int _audioRedQuad; - int _audioGreenQuad; - int _audioBlueQuad; - int _domainStatusBorder; int _magnifierBorder; int _previousBorderWidth{ -1 }; @@ -115,4 +117,4 @@ private: glm::vec3 _previousMagnifierTopRight; }; -#endif // hifi_ApplicationOverlayCompositor_h +#endif // hifi_ApplicationCompositor_h diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index ea56eed73d..989cec4345 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -39,15 +39,21 @@ const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; const int AUDIO_METER_GAP = 5; const int MUTE_ICON_PADDING = 10; -const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f }; +const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f }; const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; +static const int MIRROR_VIEW_TOP_PADDING = 5; +static const int MIRROR_VIEW_LEFT_PADDING = 10; +static const int MIRROR_VIEW_WIDTH = 265; +static const int MIRROR_VIEW_HEIGHT = 215; +static const int STATS_HORIZONTAL_OFFSET = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; +static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; +static const float MIRROR_REARVIEW_DISTANCE = 0.722f; +static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f; +static const float MIRROR_FIELD_OF_VIEW = 30.0f; + ApplicationOverlay::ApplicationOverlay() : - _alpha(1.0f), - _trailingAudioLoudness(0.0f), - _previousBorderWidth(-1), - _previousBorderHeight(-1), - _framebufferObject(nullptr) + _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)) { auto geometryCache = DependencyManager::get(); _audioRedQuad = geometryCache->allocateID(); @@ -82,67 +88,70 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { Overlays& overlays = qApp->getOverlays(); glm::vec2 size = qApp->getCanvasSize(); // TODO Handle fading and deactivation/activation of UI - - // Render 2D overlay + + + // TODO First render the mirror to the mirror FBO + + + // Now render the overlay components together into a single texture + gpu::Batch batch; + auto geometryCache = DependencyManager::get(); + geometryCache->useSimpleDrawPipeline(batch); + static const float NEAR_CLIP = -10000; + static const float FAR_CLIP = 10000; + batch._glDisable(GL_DEPTH); + batch._glDisable(GL_LIGHTING); + batch._glEnable(GL_BLEND); + renderAudioMeter(batch); + renderCameraToggle(batch); + renderStatsAndLogs(batch); + renderDomainConnectionStatusBorder(batch); + renderQmlUi(batch); + + // Execute the batch into our framebuffer + buildFramebufferObject(); + _overlayFramebuffer->bind(); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glViewport(0, 0, size.x, size.y); + mat4 legacyProjection = glm::ortho(0, size.x, size.y, 0, NEAR_CLIP, FAR_CLIP); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(glm::value_ptr(legacyProjection)); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - buildFramebufferObject(); - - _framebufferObject->bind(); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glViewport(0, 0, size.x, size.y); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); { - const float NEAR_CLIP = -10000; - const float FAR_CLIP = 10000; - glLoadIdentity(); - glOrtho(0, size.x, size.y, 0, NEAR_CLIP, FAR_CLIP); - - glMatrixMode(GL_MODELVIEW); - - renderAudioMeter(); - renderCameraToggle(); - - renderStatsAndLogs(); - - // give external parties a change to hook in - emit qApp->renderingOverlay(); - - overlays.renderHUD(renderArgs); - - renderDomainConnectionStatusBorder(); - if (_uiTexture) { - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBindTexture(GL_TEXTURE_2D, _uiTexture); - DependencyManager::get()->renderUnitQuad(); - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - } - glLoadIdentity(); - - - glMatrixMode(GL_PROJECTION); - } glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glEnable(GL_DEPTH_TEST); - glEnable(GL_LIGHTING); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + glPushMatrix(); + glLoadIdentity(); - _framebufferObject->release(); + // give external parties a change to hook in + //emit qApp->renderingOverlay(); + overlays.renderHUD(renderArgs); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + renderArgs->_context->syncCache(); + renderArgs->_context->render(batch); + _overlayFramebuffer->release(); } -void ApplicationOverlay::renderCameraToggle() { +void ApplicationOverlay::renderQmlUi(gpu::Batch& batch) { + if (_uiTexture) { + batch.setProjectionTransform(mat4()); + batch.setModelTransform(mat4()); + batch._glBindTexture(GL_TEXTURE_2D, _uiTexture); + auto geometryCache = DependencyManager::get(); + geometryCache->renderUnitQuad(batch, glm::vec4(1)); + } +} + +void ApplicationOverlay::renderCameraToggle(gpu::Batch& batch) { + /* if (Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)) { return; } @@ -158,9 +167,11 @@ void ApplicationOverlay::renderCameraToggle() { } DependencyManager::get()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, boxed); + */ } -void ApplicationOverlay::renderAudioMeter() { +void ApplicationOverlay::renderAudioMeter(gpu::Batch& batch) { + /* auto audio = DependencyManager::get(); // Audio VU Meter and Mute Icon @@ -274,9 +285,90 @@ void ApplicationOverlay::renderAudioMeter() { audioLevel, AUDIO_METER_HEIGHT, quadColor, _audioBlueQuad); } + */ } -void ApplicationOverlay::renderStatsAndLogs() { +void ApplicationOverlay::renderRearView(gpu::Batch& batch) { + // // Grab current viewport to reset it at the end + // int viewport[4]; + // glGetIntegerv(GL_VIEWPORT, viewport); + // float aspect = (float)region.width() / region.height(); + // float fov = MIRROR_FIELD_OF_VIEW; + + // // bool eyeRelativeCamera = false; + // if (billboard) { + // fov = BILLBOARD_FIELD_OF_VIEW; // degees + // _mirrorCamera.setPosition(_myAvatar->getPosition() + + // _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale()); + + // } else if (RearMirrorTools::rearViewZoomLevel.get() == BODY) { + // _mirrorCamera.setPosition(_myAvatar->getChestPosition() + + // _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); + + // } else { // HEAD zoom level + // // FIXME note that the positioing of the camera relative to the avatar can suffer limited + // // precision as the user's position moves further away from the origin. Thus at + // // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways + // // wildly as you rotate your avatar because the floating point values are becoming + // // larger, squeezing out the available digits of precision you have available at the + // // human scale for camera positioning. + + // // Previously there was a hack to correct this using the mechanism of repositioning + // // the avatar at the origin of the world for the purposes of rendering the mirror, + // // but it resulted in failing to render the avatar's head model in the mirror view + // // when in first person mode. Presumably this was because of some missed culling logic + // // that was not accounted for in the hack. + + // // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further + // // investigated in order to adapt the technique while fixing the head rendering issue, + // // but the complexity of the hack suggests that a better approach + // _mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() + + // _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); + // } + // _mirrorCamera.setProjection(glm::perspective(glm::radians(fov), aspect, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); + // _mirrorCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); + + // // set the bounds of rear mirror view + // if (billboard) { + // QSize size = DependencyManager::get()->getFrameBufferSize(); + // glViewport(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); + // glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); + // } else { + // // if not rendering the billboard, the region is in device independent coordinates; must convert to device + // QSize size = DependencyManager::get()->getFrameBufferSize(); + // float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale(); + // int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio; + // glViewport(x, size.height() - y - height, width, height); + // glScissor(x, size.height() - y - height, width, height); + // } + // bool updateViewFrustum = false; + // updateProjectionMatrix(_mirrorCamera, updateViewFrustum); + // glEnable(GL_SCISSOR_TEST); + // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // // render rear mirror view + // glMatrixMode(GL_MODELVIEW); + // glPushMatrix(); + // glLoadIdentity(); + // glLoadMatrixf(glm::value_ptr(glm::mat4_cast(_mirrorCamera.getOrientation()) * glm::translate(glm::mat4(), _mirrorCamera.getPosition()))); + // renderArgs->_context->syncCache(); + // displaySide(renderArgs, _mirrorCamera, true, billboard); + // glMatrixMode(GL_MODELVIEW); + // glPopMatrix(); + + // if (!billboard) { + // _rearMirrorTools->render(renderArgs, false, _glWidget->mapFromGlobal(QCursor::pos())); + // } + + // // reset Viewport and projection matrix + // glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + // glDisable(GL_SCISSOR_TEST); + // updateProjectionMatrix(_myCamera, updateViewFrustum); + //} +} + +void ApplicationOverlay::renderStatsAndLogs(gpu::Batch& batch) { + /* Application* application = Application::getInstance(); QSharedPointer bandwidthRecorder = DependencyManager::get(); @@ -284,7 +376,7 @@ void ApplicationOverlay::renderStatsAndLogs() { NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay(); // Display stats and log text onscreen - glLineWidth(1.0f); + batch._glLineWidth(1.0f); glPointSize(1.0f); // Determine whether to compute timing details @@ -297,10 +389,9 @@ void ApplicationOverlay::renderStatsAndLogs() { if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { // let's set horizontal offset to give stats some margin to mirror - int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; int voxelPacketsToProcess = octreePacketProcessor.packetsToProcessCount(); // Onscreen text about position, servers, etc - Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, application->getFps(), + Stats::getInstance()->display(WHITE_TEXT, STATS_HORIZONTAL_OFFSET, application->getFps(), bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond(), bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond(), bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond(), @@ -320,56 +411,73 @@ void ApplicationOverlay::renderStatsAndLogs() { 0.30f, 0.0f, 0, frameTimer.toUtf8().constData(), WHITE_TEXT); } nodeBoundsDisplay.drawOverlay(); + */ } -void ApplicationOverlay::renderDomainConnectionStatusBorder() { +void ApplicationOverlay::renderDomainConnectionStatusBorder(gpu::Batch& batch) { + auto geometryCache = DependencyManager::get(); + std::once_flag once; + std::call_once(once, [&] { + QVector points; + static const float B = 0.99; + points.push_back(vec2(-B)); + points.push_back(vec2(B, -B)); + points.push_back(vec2(B)); + points.push_back(vec2(-B, B)); + points.push_back(vec2(-B)); + geometryCache->updateVertices(_domainStatusBorder, points, CONNECTION_STATUS_BORDER_COLOR); + }); auto nodeList = DependencyManager::get(); + if (nodeList && !nodeList->getDomainHandler().isConnected()) { - auto geometryCache = DependencyManager::get(); - auto canvasSize = qApp->getCanvasSize(); - if ((int)canvasSize.x != _previousBorderWidth || (int)canvasSize.y != _previousBorderHeight) { - glm::vec4 color(CONNECTION_STATUS_BORDER_COLOR[0], - CONNECTION_STATUS_BORDER_COLOR[1], - CONNECTION_STATUS_BORDER_COLOR[2], 1.0f); + batch.setProjectionTransform(mat4()); + batch.setModelTransform(mat4()); + batch.setUniformTexture(0, DependencyManager::get()->getWhiteTexture()); + batch._glLineWidth(CONNECTION_STATUS_BORDER_LINE_WIDTH); - QVector border; - border << glm::vec2(0, 0); - border << glm::vec2(0, canvasSize.y); - border << glm::vec2(canvasSize.x, canvasSize.y); - border << glm::vec2(canvasSize.x, 0); - border << glm::vec2(0, 0); - geometryCache->updateVertices(_domainStatusBorder, border, color); - _previousBorderWidth = canvasSize.x; - _previousBorderHeight = canvasSize.y; - } + // TODO animate the disconnect border for some excitement while not connected? + //double usecs = usecTimestampNow(); + //double secs = usecs / 1000000.0; + //float scaleAmount = 1.0f + (0.01f * sin(secs * 5.0f)); + //batch.setModelTransform(glm::scale(mat4(), vec3(scaleAmount))); - glLineWidth(CONNECTION_STATUS_BORDER_LINE_WIDTH); - - geometryCache->renderVertices(gpu::LINE_STRIP, _domainStatusBorder); + geometryCache->renderVertices(batch, gpu::LINE_STRIP, _domainStatusBorder); } } GLuint ApplicationOverlay::getOverlayTexture() { - if (!_framebufferObject) { + if (!_overlayFramebuffer) { return 0; } - return _framebufferObject->texture(); + return _overlayFramebuffer->texture(); } void ApplicationOverlay::buildFramebufferObject() { + if (!_mirrorFramebuffer) { + _mirrorFramebuffer = new QOpenGLFramebufferObject(QSize(MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT), QOpenGLFramebufferObject::Depth); + glBindTexture(GL_TEXTURE_2D, _mirrorFramebuffer->texture()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + GLfloat borderColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); + glBindTexture(GL_TEXTURE_2D, 0); + } + auto canvasSize = qApp->getCanvasSize(); QSize fboSize = QSize(canvasSize.x, canvasSize.y); - if (_framebufferObject != NULL && fboSize == _framebufferObject->size()) { + if (_overlayFramebuffer && fboSize == _overlayFramebuffer->size()) { // Already built return; } - if (_framebufferObject != NULL) { - delete _framebufferObject; + if (_overlayFramebuffer) { + delete _overlayFramebuffer; } - _framebufferObject = new QOpenGLFramebufferObject(fboSize, QOpenGLFramebufferObject::Depth); + _overlayFramebuffer = new QOpenGLFramebufferObject(fboSize, QOpenGLFramebufferObject::Depth); glBindTexture(GL_TEXTURE_2D, getOverlayTexture()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index dc21fba194..9dd846d9bf 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -29,13 +29,16 @@ public: private: - void renderAudioMeter(); - void renderCameraToggle(); - void renderStatsAndLogs(); - void renderDomainConnectionStatusBorder(); + void renderAudioMeter(gpu::Batch& batch); + void renderCameraToggle(gpu::Batch& batch); + void renderStatsAndLogs(gpu::Batch& batch); + void renderDomainConnectionStatusBorder(gpu::Batch& batch); + void renderRearView(gpu::Batch& batch); + void renderQmlUi(gpu::Batch& batch); void buildFramebufferObject(); - float _alpha = 1.0f; - float _trailingAudioLoudness; + + float _alpha{ 1.0f }; + float _trailingAudioLoudness{ 0.0f }; GLuint _uiTexture{ 0 }; int _audioRedQuad; @@ -44,10 +47,15 @@ private: int _domainStatusBorder; int _magnifierBorder; - int _previousBorderWidth; - int _previousBorderHeight; + float _scaleMirror{ 1.0f }; + float _rotateMirror{ 0.0f }; + float _raiseMirror{ 0.0f }; - QOpenGLFramebufferObject* _framebufferObject; + + ivec2 _previousBorderSize{ -1 }; + QRect _mirrorViewRect; + QOpenGLFramebufferObject* _overlayFramebuffer{ nullptr }; + QOpenGLFramebufferObject* _mirrorFramebuffer{ nullptr }; }; #endif // hifi_ApplicationOverlay_h diff --git a/libraries/entities/src/EntityItemID.h b/libraries/entities/src/EntityItemID.h index 547907625d..7f55005a17 100644 --- a/libraries/entities/src/EntityItemID.h +++ b/libraries/entities/src/EntityItemID.h @@ -14,7 +14,7 @@ #include - +#include #include #include #include diff --git a/libraries/render-utils/src/TextRenderer.cpp b/libraries/render-utils/src/TextRenderer.cpp index 87cf9b2728..c815c5f179 100644 --- a/libraries/render-utils/src/TextRenderer.cpp +++ b/libraries/render-utils/src/TextRenderer.cpp @@ -434,6 +434,8 @@ glm::vec2 Font::drawString(float x, float y, const QString & str, fromGlm(MatrixStack::projection().top())); if (effectType == TextRenderer::OUTLINE_EFFECT) { _program->setUniformValue("Outline", true); + } else { + _program->setUniformValue("Outline", false); } // Needed? glEnable(GL_TEXTURE_2D); @@ -539,6 +541,25 @@ glm::vec2 TextRenderer::computeExtent(const QString & str) const { return glm::vec2(0.1f,0.1f); } +static gpu::PipelinePointer _textDrawPipeline; + +void TextRenderer::useTextPipeline(gpu::Batch& batch) { + if (!_textDrawPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(sdf_text_vert))); + auto fs = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(sdf_text_frag))); + auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, fs)); + gpu::Shader::makeProgram((*program)); + + auto state = gpu::StatePointer(new gpu::State()); + // enable decal blend + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); + _textDrawPipeline.reset(gpu::Pipeline::create(program, state)); + } + batch.setPipeline(_textDrawPipeline); + batch._glBindTexture(GL_TEXTURE_2D, this->_font->_texture->textureId()); +} + + float TextRenderer::draw(float x, float y, const QString & str, const glm::vec4& color, const glm::vec2 & bounds) { glm::vec4 actualColor(color); diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 9b7cca70ee..54fe6492cf 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -122,6 +122,21 @@ float aspect(const T& t) { return (float)t.x / (float)t.y; } +// Take values in an arbitrary range [0, size] and convert them to the range [0, 1] +template +T toUnitScale(const T& value, const T& size) { + return value / size; +} + +// Take values in an arbitrary range [0, size] and convert them to the range [0, 1] +template +T toNormalizedDeviceScale(const T& value, const T& size) { + vec2 result = toUnitScale(value, size); + result *= 2.0f; + result -= 1.0f; + return result; +} + #define YAW(euler) euler.y #define PITCH(euler) euler.x #define ROLL(euler) euler.z From 02d33f6086f98092c5168401597740f2ba549e77 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 16 Jun 2015 12:14:42 -0700 Subject: [PATCH 06/35] Working on overlay rendering --- interface/src/ui/ApplicationOverlay.cpp | 74 +++++++++++---------- interface/src/ui/ApplicationOverlay.h | 1 + libraries/render-utils/src/TextRenderer.cpp | 21 ------ 3 files changed, 39 insertions(+), 57 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 989cec4345..6471fcfb16 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -85,55 +85,33 @@ ApplicationOverlay::~ApplicationOverlay() { void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); - Overlays& overlays = qApp->getOverlays(); glm::vec2 size = qApp->getCanvasSize(); // TODO Handle fading and deactivation/activation of UI - // TODO First render the mirror to the mirror FBO - - // Now render the overlay components together into a single texture - gpu::Batch batch; - auto geometryCache = DependencyManager::get(); - geometryCache->useSimpleDrawPipeline(batch); - static const float NEAR_CLIP = -10000; - static const float FAR_CLIP = 10000; - batch._glDisable(GL_DEPTH); - batch._glDisable(GL_LIGHTING); - batch._glEnable(GL_BLEND); - renderAudioMeter(batch); - renderCameraToggle(batch); - renderStatsAndLogs(batch); - renderDomainConnectionStatusBorder(batch); - renderQmlUi(batch); - // Execute the batch into our framebuffer buildFramebufferObject(); _overlayFramebuffer->bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, size.x, size.y); - mat4 legacyProjection = glm::ortho(0, size.x, size.y, 0, NEAR_CLIP, FAR_CLIP); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadMatrixf(glm::value_ptr(legacyProjection)); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); + // Now render the overlay components together into a single texture + gpu::Batch batch; + auto geometryCache = DependencyManager::get(); + geometryCache->useSimpleDrawPipeline(batch); + batch._glDisable(GL_DEPTH); + batch._glDisable(GL_LIGHTING); + batch._glEnable(GL_BLEND); - // give external parties a change to hook in - //emit qApp->renderingOverlay(); - overlays.renderHUD(renderArgs); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); + renderOverlays(batch, renderArgs); + + renderAudioMeter(batch); + renderCameraToggle(batch); + renderStatsAndLogs(batch); + renderDomainConnectionStatusBorder(batch); + renderQmlUi(batch); renderArgs->_context->syncCache(); renderArgs->_context->render(batch); @@ -150,6 +128,30 @@ void ApplicationOverlay::renderQmlUi(gpu::Batch& batch) { } } +void ApplicationOverlay::renderOverlays(gpu::Batch& batch, RenderArgs* renderArgs) { + static const float NEAR_CLIP = -10000; + static const float FAR_CLIP = 10000; + glm::vec2 size = qApp->getCanvasSize(); + mat4 legacyProjection = glm::ortho(0, size.x, size.y, 0, NEAR_CLIP, FAR_CLIP); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(glm::value_ptr(legacyProjection)); + glMatrixMode(GL_MODELVIEW); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // give external parties a change to hook in + emit qApp->renderingOverlay(); + qApp->getOverlays().renderHUD(renderArgs); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); +} + void ApplicationOverlay::renderCameraToggle(gpu::Batch& batch) { /* if (Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)) { diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 9dd846d9bf..9076d61b18 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -35,6 +35,7 @@ private: void renderDomainConnectionStatusBorder(gpu::Batch& batch); void renderRearView(gpu::Batch& batch); void renderQmlUi(gpu::Batch& batch); + void renderOverlays(gpu::Batch& batch, RenderArgs* renderArgs); void buildFramebufferObject(); float _alpha{ 1.0f }; diff --git a/libraries/render-utils/src/TextRenderer.cpp b/libraries/render-utils/src/TextRenderer.cpp index c815c5f179..87cf9b2728 100644 --- a/libraries/render-utils/src/TextRenderer.cpp +++ b/libraries/render-utils/src/TextRenderer.cpp @@ -434,8 +434,6 @@ glm::vec2 Font::drawString(float x, float y, const QString & str, fromGlm(MatrixStack::projection().top())); if (effectType == TextRenderer::OUTLINE_EFFECT) { _program->setUniformValue("Outline", true); - } else { - _program->setUniformValue("Outline", false); } // Needed? glEnable(GL_TEXTURE_2D); @@ -541,25 +539,6 @@ glm::vec2 TextRenderer::computeExtent(const QString & str) const { return glm::vec2(0.1f,0.1f); } -static gpu::PipelinePointer _textDrawPipeline; - -void TextRenderer::useTextPipeline(gpu::Batch& batch) { - if (!_textDrawPipeline) { - auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(sdf_text_vert))); - auto fs = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(sdf_text_frag))); - auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, fs)); - gpu::Shader::makeProgram((*program)); - - auto state = gpu::StatePointer(new gpu::State()); - // enable decal blend - state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); - _textDrawPipeline.reset(gpu::Pipeline::create(program, state)); - } - batch.setPipeline(_textDrawPipeline); - batch._glBindTexture(GL_TEXTURE_2D, this->_font->_texture->textureId()); -} - - float TextRenderer::draw(float x, float y, const QString & str, const glm::vec4& color, const glm::vec2 & bounds) { glm::vec4 actualColor(color); From 659fa2387c0b76dd2e9561fbcccb7867715dc90f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 16 Jun 2015 21:00:13 -0700 Subject: [PATCH 07/35] Working on overlays / stats --- interface/resources/qml/Stats.qml | 147 +++++ interface/resources/qml/Tooltip.qml | 29 + interface/src/Application.cpp | 2 + interface/src/ui/ApplicationCompositor.cpp | 165 +---- interface/src/ui/ApplicationCompositor.h | 1 + interface/src/ui/ApplicationOverlay.cpp | 103 ++- interface/src/ui/ApplicationOverlay.h | 14 +- interface/src/ui/Stats.cpp | 620 +++++++----------- interface/src/ui/Stats.h | 128 +++- .../render-utils/src/OffscreenQmlSurface.cpp | 4 +- libraries/ui/src/Tooltip.cpp | 51 ++ libraries/ui/src/Tooltip.h | 45 ++ 12 files changed, 671 insertions(+), 638 deletions(-) create mode 100644 interface/resources/qml/Stats.qml create mode 100644 interface/resources/qml/Tooltip.qml create mode 100644 libraries/ui/src/Tooltip.cpp create mode 100644 libraries/ui/src/Tooltip.h diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml new file mode 100644 index 0000000000..6173b8517d --- /dev/null +++ b/interface/resources/qml/Stats.qml @@ -0,0 +1,147 @@ +import Hifi 1.0 as Hifi +import QtQuick 2.3 +import QtQuick.Controls 1.2 + +Hifi.Stats { + id: root + objectName: "Stats" + implicitHeight: row.height + implicitWidth: row.width + readonly property int sTATS_GENERAL_MIN_WIDTH: 165 + readonly property int sTATS_PING_MIN_WIDTH: 190 + readonly property int sTATS_GEO_MIN_WIDTH: 240 + readonly property int sTATS_OCTREE_MIN_WIDTH: 410 + + onParentChanged: { + root.x = parent.width - root.width; + } + + Row { + z: 100 + id: row + spacing: 8 + Rectangle { + width: generalCol.width + 8; + height: generalCol.height + 8; + color: "#99333333"; + + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + } + + Column { + id: generalCol + spacing: 4; x: 4; y: 4; + width: sTATS_GENERAL_MIN_WIDTH + Text { color: "white"; text: "Servers: " + root.serverCount } + Text { color: "white"; text: "Avatars: " + root.avatarCount } + Text { color: "white"; text: "Framerate: " + root.framerate } + Text { color: "white"; text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount } + Text { color: "white"; text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2) } + } + } + + Rectangle { + width: pingCol.width + 8 + height: pingCol.height + 8 + color: "#99333333" + visible: root.audioPing != -2 + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + } + Column { + id: pingCol + spacing: 4; x: 4; y: 4; + width: sTATS_PING_MIN_WIDTH + Text { color: "white"; text: "Audio ping: " + root.audioPing } + Text { color: "white"; text: "Avatar ping: " + root.avatarPing } + Text { color: "white"; text: "Entities avg ping: " + root.entitiesPing } + Text { color: "white"; text: "Voxel max ping: " + 0; visible: root.expanded; } + } + } + Rectangle { + width: geoCol.width + height: geoCol.height + color: "#99333333" + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + } + Column { + spacing: 4 + id: geoCol + width: sTATS_GEO_MIN_WIDTH + Text { + color: "white"; + text: "Position: " + root.position.x.toFixed(1) + ", " + + root.position.y.toFixed(1) + ", " + root.position.z.toFixed(1) + } + Text { + color: "white"; + text: "Velocity: " + root.velocity.toFixed(1) + } + Text { + color: "white"; + text: "Yaw: " + root.yaw.toFixed(1) + } + Text { + color: "white"; + visible: root.expanded; + text: "Avatar Mixer: " + root.avatarMixerKbps + " kbps, " + + root.avatarMixerPps + "pps"; + } + Text { + color: "white"; + visible: root.expanded; + text: "Downloads: "; + } + } + } + Rectangle { + width: octreeCol.width + 8 + height: octreeCol.height + 8 + color: "#99333333" + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + } + Column { + id: octreeCol + spacing: 4; x: 4; y: 4; + width: sTATS_OCTREE_MIN_WIDTH + Text { + color: "white"; + text: "Triangles: " + root.triangles + + " / Quads: " + root.quads + " / Material Switches: " + root.materialSwitches + } + Text { + color: "white"; + visible: root.expanded; + text: "\tMesh Parts Rendered Opaque: " + root.meshOpaque + + " / Translucent: " + root.meshTranslucent; + } + Text { + color: "white"; + visible: root.expanded; + text: "\tOpaque considered: " + root.opaqueConsidered + + " / Out of view: " + root.opaqueOutOfView + " / Too small: " + root.opaqueTooSmall; + } + Text { + color: "white"; + visible: !root.expanded + text: "Octree Elements Server: "; + } + } + } + } + + Connections { + target: root.parent + onWidthChanged: { + root.x = root.parent.width - root.width; + } + } +} + diff --git a/interface/resources/qml/Tooltip.qml b/interface/resources/qml/Tooltip.qml new file mode 100644 index 0000000000..f1504317e2 --- /dev/null +++ b/interface/resources/qml/Tooltip.qml @@ -0,0 +1,29 @@ +import Hifi 1.0 as Hifi +import QtQuick 2.3 as Original +import "controls" +import "styles" + +Hifi.Tooltip { + id: root + HifiConstants { id: hifi } + x: lastMousePosition.x + y: lastMousePosition.y + implicitWidth: border.implicitWidth + implicitHeight: border.implicitHeight + + Border { + id: border + anchors.fill: parent + implicitWidth: text.implicitWidth + implicitHeight: Math.max(text.implicitHeight, 64) + + Text { + id: text + anchors.fill: parent + anchors.margins: 16 + font.pixelSize: hifi.fonts.pixelSize / 2 + text: root.text + wrapMode: Original.Text.WordWrap + } + } +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6270c8f597..2aa718a27c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -88,6 +88,7 @@ #include #include #include +#include #include #include #include @@ -822,6 +823,7 @@ void Application::initializeUi() { LoginDialog::registerType(); MessageDialog::registerType(); VrMenu::registerType(); + Tooltip::registerType(); auto offscreenUi = DependencyManager::get(); offscreenUi->create(_glWidget->context()->contextHandle()); diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index ab5014dfaa..b0c21cf77d 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "AudioClient.h" @@ -41,7 +42,7 @@ static const float MAG_SPEED = 0.08f; static const quint64 MSECS_TO_USECS = 1000ULL; -static const quint64 TOOLTIP_DELAY = 2000000ULL; +static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS; static const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; static const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; @@ -59,7 +60,7 @@ static gpu::BufferPointer _hemiVertices; static gpu::BufferPointer _hemiIndices; static int _hemiIndexCount{ 0 }; EntityItemID ApplicationCompositor::_noItemId; - +static QString _tooltipId; // Return a point's cartesian coordinates on a sphere from pitch and yaw glm::vec3 getPoint(float yaw, float pitch) { @@ -159,6 +160,11 @@ ApplicationCompositor::ApplicationCompositor() { _hoverItemHref.clear(); auto cursor = Cursor::Manager::instance().getCursor(); cursor->setIcon(Cursor::Icon::DEFAULT); + if (!_tooltipId.isEmpty()) { + qDebug() << "Closing tooltip " << _tooltipId; + Tooltip::closeTip(_tooltipId); + _tooltipId.clear(); + } } }); } @@ -166,6 +172,7 @@ ApplicationCompositor::ApplicationCompositor() { ApplicationCompositor::~ApplicationCompositor() { } + void ApplicationCompositor::bindCursorTexture(gpu::Batch& batch, uint8_t cursorIndex) { auto& cursorManager = Cursor::Manager::instance(); auto cursor = cursorManager.getCursor(cursorIndex); @@ -184,17 +191,10 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) { return; } + updateTooltips(); + vec2 canvasSize = qApp->getCanvasSize(); - _textureAspectRatio = aspect(canvasSize); - - if (_hoverItemId != _noItemId) { - quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs; - if (!_hoverItemHref.isEmpty() && hoverDuration > TOOLTIP_DELAY) { - // TODO Enable and position the tooltip - } - } - //Handle fading and deactivation/activation of UI gpu::Batch batch; @@ -262,6 +262,11 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int return; } + vec2 canvasSize = qApp->getCanvasSize(); + _textureAspectRatio = aspect(canvasSize); + + updateTooltips(); + renderArgs->_context->syncCache(); auto geometryCache = DependencyManager::get(); @@ -392,13 +397,6 @@ bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& positi //Renders optional pointers void ApplicationCompositor::renderPointers(gpu::Batch& batch) { - //glEnable(GL_TEXTURE_2D); - //glEnable(GL_BLEND); - //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - //glActiveTexture(GL_TEXTURE0); - //bindCursorTexture(); - if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) { //If we are in oculus, render reticle later if (_lastMouseMove == 0) { @@ -442,8 +440,6 @@ void ApplicationCompositor::renderPointers(gpu::Batch& batch) { _magActive[MOUSE] = false; renderControllerPointers(batch); } - //glBindTexture(GL_TEXTURE_2D, 0); - //glDisable(GL_TEXTURE_2D); } @@ -718,7 +714,6 @@ void ApplicationCompositor::drawSphereSection(gpu::Batch& batch) { batch.drawIndexed(gpu::TRIANGLES, _hemiIndexCount); } - glm::vec2 ApplicationCompositor::directionToSpherical(const glm::vec3& direction) { glm::vec2 result; // Compute yaw @@ -788,127 +783,13 @@ glm::vec2 ApplicationCompositor::overlayToScreen(const glm::vec2& overlayPos) co return sphericalToScreen(overlayToSpherical(overlayPos)); } -#if 0 - -gpu::PipelinePointer ApplicationOverlay::getDrawPipeline() { - if (!_standardDrawPipeline) { - auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert))); - auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(standardDrawTexture_frag))); - auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); - gpu::Shader::makeProgram((*program)); - - auto state = gpu::StatePointer(new gpu::State()); - - // enable decal blend - state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); - - _standardDrawPipeline.reset(gpu::Pipeline::create(program, state)); - } - - return _standardDrawPipeline; -} - - -// Draws the FBO texture for the screen -void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { - if (_alpha == 0.0f) { - return; - } - - renderArgs->_context->syncCache(); - - gpu::Batch batch; - Transform model; - //DependencyManager::get()->bindSimpleProgram(batch, true); - batch.setPipeline(getDrawPipeline()); - batch.setModelTransform(Transform()); - batch.setProjectionTransform(mat4()); - batch.setViewTransform(model); - batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); - batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - DependencyManager::get()->renderUnitQuad(batch, vec4(vec3(1), _alpha)); - - //draw the mouse pointer - glm::vec2 canvasSize = qApp->getCanvasSize(); - - // Get the mouse coordinates and convert to NDC [-1, 1] - vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY()); - mousePosition /= canvasSize; - mousePosition *= 2.0f; - mousePosition -= 1.0f; - mousePosition.y *= -1.0f; - model.setTranslation(vec3(mousePosition, 0)); - glm::vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize; - model.setScale(vec3(mouseSize, 1.0f)); - batch.setModelTransform(model); - bindCursorTexture(batch); - glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; - DependencyManager::get()->renderUnitQuad(batch, vec4(1)); - renderArgs->_context->render(batch); -} - - - -// Draws the FBO texture for Oculus rift. -void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera) { - if (_alpha == 0.0f) { - return; - } - - renderArgs->_context->syncCache(); - - gpu::Batch batch; - batch.setPipeline(getDrawPipeline()); - batch._glDisable(GL_DEPTH_TEST); - batch._glDisable(GL_CULL_FACE); - batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); - batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - batch.setProjectionTransform(whichCamera.getProjection()); - batch.setViewTransform(Transform()); - - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - const quat& avatarOrientation = myAvatar->getOrientation(); - quat hmdOrientation = qApp->getCamera()->getHmdRotation(); - vec3 hmdPosition = glm::inverse(avatarOrientation) * qApp->getCamera()->getHmdPosition(); - mat4 overlayXfm = glm::mat4_cast(glm::inverse(hmdOrientation)) * glm::translate(mat4(), -hmdPosition); - batch.setModelTransform(Transform(overlayXfm)); - drawSphereSection(batch); - - - bindCursorTexture(batch); - auto geometryCache = DependencyManager::get(); - vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize); - //Controller Pointers - for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { - PalmData& palm = myAvatar->getHand()->getPalms()[i]; - if (palm.isActive()) { - glm::vec2 polar = getPolarCoordinates(palm); - // Convert to quaternion - mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); - mat4 reticleXfm = overlayXfm * pointerXfm; - reticleXfm = glm::scale(reticleXfm, reticleScale); - batch.setModelTransform(reticleXfm); - // Render reticle at location - geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); +void ApplicationCompositor::updateTooltips() { + if (_hoverItemId != _noItemId) { + quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs; + if (_hoverItemEnterUsecs != UINT64_MAX && !_hoverItemHref.isEmpty() && hoverDuration > TOOLTIP_DELAY) { + // TODO Enable and position the tooltip + _hoverItemEnterUsecs = UINT64_MAX; + _tooltipId = Tooltip::showTip("URL: " + _hoverItemHref); } } - - //Mouse Pointer - if (_reticleActive[MOUSE]) { - glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(), - _reticlePosition[MOUSE].y())); - mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); - mat4 reticleXfm = overlayXfm * pointerXfm; - reticleXfm = glm::scale(reticleXfm, reticleScale); - batch.setModelTransform(reticleXfm); - geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); - } - - renderArgs->_context->render(batch); } - - - -#endif diff --git a/interface/src/ui/ApplicationCompositor.h b/interface/src/ui/ApplicationCompositor.h index 5145e14006..0063319796 100644 --- a/interface/src/ui/ApplicationCompositor.h +++ b/interface/src/ui/ApplicationCompositor.h @@ -74,6 +74,7 @@ private: void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0); void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks); void drawSphereSection(gpu::Batch& batch); + void updateTooltips(); void renderPointers(gpu::Batch& batch); void renderMagnifier(gpu::Batch& batch, const glm::vec2& magPos, float sizeMult, bool showBorder); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 6471fcfb16..86f06ba7fa 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -50,6 +50,8 @@ static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; static const float MIRROR_REARVIEW_DISTANCE = 0.722f; static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f; static const float MIRROR_FIELD_OF_VIEW = 30.0f; +static const float ORTHO_NEAR_CLIP = -10000; +static const float ORTHO_FAR_CLIP = 10000; ApplicationOverlay::ApplicationOverlay() : @@ -85,6 +87,7 @@ ApplicationOverlay::~ApplicationOverlay() { void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); + Stats::getInstance()->updateStats(); glm::vec2 size = qApp->getCanvasSize(); // TODO Handle fading and deactivation/activation of UI @@ -98,41 +101,38 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { glViewport(0, 0, size.x, size.y); // Now render the overlay components together into a single texture - gpu::Batch batch; - auto geometryCache = DependencyManager::get(); - geometryCache->useSimpleDrawPipeline(batch); - batch._glDisable(GL_DEPTH); - batch._glDisable(GL_LIGHTING); - batch._glEnable(GL_BLEND); + //renderOverlays(renderArgs); + //renderAudioMeter(renderArgs); + //renderCameraToggle(renderArgs); + //renderStatsAndLogs(renderArgs); - renderOverlays(batch, renderArgs); - renderAudioMeter(batch); - renderCameraToggle(batch); - renderStatsAndLogs(batch); - renderDomainConnectionStatusBorder(batch); - renderQmlUi(batch); + renderDomainConnectionStatusBorder(renderArgs); + renderQmlUi(renderArgs); - renderArgs->_context->syncCache(); - renderArgs->_context->render(batch); _overlayFramebuffer->release(); } -void ApplicationOverlay::renderQmlUi(gpu::Batch& batch) { +void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { if (_uiTexture) { + gpu::Batch batch; + auto geometryCache = DependencyManager::get(); + geometryCache->useSimpleDrawPipeline(batch); + batch._glDisable(GL_DEPTH_TEST); + batch._glDisable(GL_LIGHTING); + batch._glEnable(GL_BLEND); batch.setProjectionTransform(mat4()); batch.setModelTransform(mat4()); batch._glBindTexture(GL_TEXTURE_2D, _uiTexture); - auto geometryCache = DependencyManager::get(); geometryCache->renderUnitQuad(batch, glm::vec4(1)); + renderArgs->_context->syncCache(); + renderArgs->_context->render(batch); } } -void ApplicationOverlay::renderOverlays(gpu::Batch& batch, RenderArgs* renderArgs) { - static const float NEAR_CLIP = -10000; - static const float FAR_CLIP = 10000; +void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { glm::vec2 size = qApp->getCanvasSize(); - mat4 legacyProjection = glm::ortho(0, size.x, size.y, 0, NEAR_CLIP, FAR_CLIP); + mat4 legacyProjection = glm::ortho(0, size.x, size.y, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadMatrixf(glm::value_ptr(legacyProjection)); @@ -152,8 +152,7 @@ void ApplicationOverlay::renderOverlays(gpu::Batch& batch, RenderArgs* renderArg glMatrixMode(GL_MODELVIEW); } -void ApplicationOverlay::renderCameraToggle(gpu::Batch& batch) { - /* +void ApplicationOverlay::renderCameraToggle(RenderArgs* renderArgs) { if (Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)) { return; } @@ -169,11 +168,9 @@ void ApplicationOverlay::renderCameraToggle(gpu::Batch& batch) { } DependencyManager::get()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, boxed); - */ } -void ApplicationOverlay::renderAudioMeter(gpu::Batch& batch) { - /* +void ApplicationOverlay::renderAudioMeter(RenderArgs* renderArgs) { auto audio = DependencyManager::get(); // Audio VU Meter and Mute Icon @@ -287,10 +284,9 @@ void ApplicationOverlay::renderAudioMeter(gpu::Batch& batch) { audioLevel, AUDIO_METER_HEIGHT, quadColor, _audioBlueQuad); } - */ } -void ApplicationOverlay::renderRearView(gpu::Batch& batch) { +void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) { // // Grab current viewport to reset it at the end // int viewport[4]; // glGetIntegerv(GL_VIEWPORT, viewport); @@ -369,37 +365,18 @@ void ApplicationOverlay::renderRearView(gpu::Batch& batch) { //} } -void ApplicationOverlay::renderStatsAndLogs(gpu::Batch& batch) { - /* - Application* application = Application::getInstance(); - QSharedPointer bandwidthRecorder = DependencyManager::get(); - - const OctreePacketProcessor& octreePacketProcessor = application->getOctreePacketProcessor(); - NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay(); +void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) { // Display stats and log text onscreen - batch._glLineWidth(1.0f); - glPointSize(1.0f); // Determine whether to compute timing details bool shouldDisplayTimingDetail = Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && - Menu::getInstance()->isOptionChecked(MenuOption::Stats) && - Stats::getInstance()->isExpanded(); + Menu::getInstance()->isOptionChecked(MenuOption::Stats); //&& + // Stats::getInstance()->isExpanded(); if (shouldDisplayTimingDetail != PerformanceTimer::isActive()) { PerformanceTimer::setActive(shouldDisplayTimingDetail); } - - if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { - // let's set horizontal offset to give stats some margin to mirror - int voxelPacketsToProcess = octreePacketProcessor.packetsToProcessCount(); - // Onscreen text about position, servers, etc - Stats::getInstance()->display(WHITE_TEXT, STATS_HORIZONTAL_OFFSET, application->getFps(), - bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond(), - bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond(), - bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond(), - bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond(), - voxelPacketsToProcess); - } + Stats::getInstance()->updateStats(); // Show on-screen msec timer if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) { @@ -412,16 +389,26 @@ void ApplicationOverlay::renderStatsAndLogs(gpu::Batch& batch) { drawText(canvasSize.x - 100, canvasSize.y - timerBottom, 0.30f, 0.0f, 0, frameTimer.toUtf8().constData(), WHITE_TEXT); } + + glPointSize(1.0f); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + NodeBounds& nodeBoundsDisplay = qApp->getNodeBoundsDisplay(); nodeBoundsDisplay.drawOverlay(); - */ + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); } -void ApplicationOverlay::renderDomainConnectionStatusBorder(gpu::Batch& batch) { +void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderArgs) { auto geometryCache = DependencyManager::get(); std::once_flag once; std::call_once(once, [&] { QVector points; - static const float B = 0.99; + static const float B = 0.99f; points.push_back(vec2(-B)); points.push_back(vec2(B, -B)); points.push_back(vec2(B)); @@ -430,9 +417,13 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(gpu::Batch& batch) { geometryCache->updateVertices(_domainStatusBorder, points, CONNECTION_STATUS_BORDER_COLOR); }); auto nodeList = DependencyManager::get(); - - if (nodeList && !nodeList->getDomainHandler().isConnected()) { + gpu::Batch batch; + auto geometryCache = DependencyManager::get(); + geometryCache->useSimpleDrawPipeline(batch); + batch._glDisable(GL_DEPTH); + batch._glDisable(GL_LIGHTING); + batch._glEnable(GL_BLEND); batch.setProjectionTransform(mat4()); batch.setModelTransform(mat4()); batch.setUniformTexture(0, DependencyManager::get()->getWhiteTexture()); @@ -445,6 +436,8 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(gpu::Batch& batch) { //batch.setModelTransform(glm::scale(mat4(), vec3(scaleAmount))); geometryCache->renderVertices(batch, gpu::LINE_STRIP, _domainStatusBorder); + renderArgs->_context->syncCache(); + renderArgs->_context->render(batch); } } diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 9076d61b18..efdec01fb5 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -29,13 +29,13 @@ public: private: - void renderAudioMeter(gpu::Batch& batch); - void renderCameraToggle(gpu::Batch& batch); - void renderStatsAndLogs(gpu::Batch& batch); - void renderDomainConnectionStatusBorder(gpu::Batch& batch); - void renderRearView(gpu::Batch& batch); - void renderQmlUi(gpu::Batch& batch); - void renderOverlays(gpu::Batch& batch, RenderArgs* renderArgs); + void renderAudioMeter(RenderArgs* renderArgs); + void renderCameraToggle(RenderArgs* renderArgs); + void renderStatsAndLogs(RenderArgs* renderArgs); + void renderDomainConnectionStatusBorder(RenderArgs* renderArgs); + void renderRearView(RenderArgs* renderArgs); + void renderQmlUi(RenderArgs* renderArgs); + void renderOverlays(RenderArgs* renderArgs); void buildFramebufferObject(); float _alpha{ 1.0f }; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 8359480b03..fc9ceb8a67 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -9,9 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include -#include +#include "Stats.h" #include #include @@ -25,147 +25,30 @@ #include #include -#include "Stats.h" #include "BandwidthRecorder.h" #include "InterfaceConfig.h" #include "Menu.h" #include "Util.h" #include "SequenceNumberStats.h" +HIFI_QML_DEF(Stats) + using namespace std; -const int STATS_PELS_PER_LINE = 16; -const int STATS_PELS_INITIALOFFSET = 12; - -const int STATS_GENERAL_MIN_WIDTH = 165; -const int STATS_PING_MIN_WIDTH = 190; -const int STATS_GEO_MIN_WIDTH = 240; -const int STATS_OCTREE_MIN_WIDTH = 410; +static Stats* INSTANCE{ nullptr }; Stats* Stats::getInstance() { - static Stats stats; - return &stats; + if (!INSTANCE) { + Stats::registerType(); + Stats::show(); + //Stats::toggle(); + Q_ASSERT(INSTANCE); + } + return INSTANCE; } -Stats::Stats(): - _expanded(false), - _recentMaxPackets(0), - _resetRecentMaxPacketsSoon(true), - _generalStatsWidth(STATS_GENERAL_MIN_WIDTH), - _pingStatsWidth(STATS_PING_MIN_WIDTH), - _geoStatsWidth(STATS_GEO_MIN_WIDTH), - _octreeStatsWidth(STATS_OCTREE_MIN_WIDTH), - _lastHorizontalOffset(0) -{ - auto canvasSize = Application::getInstance()->getCanvasSize(); - resetWidth(canvasSize.x, 0); -} - -void Stats::toggleExpanded() { - _expanded = !_expanded; -} - -// called on mouse click release -// check for clicks over stats in order to expand or contract them -void Stats::checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset) { - auto canvasSize = Application::getInstance()->getCanvasSize(); - - if (0 != glm::compMax(glm::abs(glm::ivec2(mouseX - mouseDragStartedX, mouseY - mouseDragStartedY)))) { - // not worried about dragging on stats - return; - } - - int statsHeight = 0, - statsWidth = 0, - statsX = 0, - statsY = 0, - lines = 0; - - statsX = horizontalOffset; - - // top-left stats click - lines = _expanded ? 5 : 3; - statsHeight = lines * STATS_PELS_PER_LINE + 10; - if (mouseX > statsX && mouseX < statsX + _generalStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { - toggleExpanded(); - return; - } - statsX += _generalStatsWidth; - - // ping stats click - if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { - lines = _expanded ? 4 : 3; - statsHeight = lines * STATS_PELS_PER_LINE + 10; - if (mouseX > statsX && mouseX < statsX + _pingStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { - toggleExpanded(); - return; - } - statsX += _pingStatsWidth; - } - - // geo stats panel click - lines = _expanded ? 4 : 3; - statsHeight = lines * STATS_PELS_PER_LINE + 10; - if (mouseX > statsX && mouseX < statsX + _geoStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { - toggleExpanded(); - return; - } - statsX += _geoStatsWidth; - - // top-right stats click - lines = _expanded ? 11 : 3; - statsHeight = lines * STATS_PELS_PER_LINE + 10; - statsWidth = canvasSize.x - statsX; - if (mouseX > statsX && mouseX < statsX + statsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { - toggleExpanded(); - return; - } -} - -void Stats::resetWidth(int width, int horizontalOffset) { - auto canvasSize = Application::getInstance()->getCanvasSize(); - int extraSpace = canvasSize.x - horizontalOffset - 2 - - STATS_GENERAL_MIN_WIDTH - - (Menu::getInstance()->isOptionChecked(MenuOption::TestPing) ? STATS_PING_MIN_WIDTH -1 : 0) - - STATS_GEO_MIN_WIDTH - - STATS_OCTREE_MIN_WIDTH; - - int panels = 4; - - _generalStatsWidth = STATS_GENERAL_MIN_WIDTH; - if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { - _pingStatsWidth = STATS_PING_MIN_WIDTH; - } else { - _pingStatsWidth = 0; - panels = 3; - } - _geoStatsWidth = STATS_GEO_MIN_WIDTH; - _octreeStatsWidth = STATS_OCTREE_MIN_WIDTH; - - if (extraSpace > panels) { - _generalStatsWidth += (int) extraSpace / panels; - if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { - _pingStatsWidth += (int) extraSpace / panels; - } - _geoStatsWidth += (int) extraSpace / panels; - _octreeStatsWidth += canvasSize.x - - (_generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3); - } -} - - -// translucent background box that makes stats more readable -void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int height) { - glm::vec4 color(((rgba >> 24) & 0xff) / 255.0f, - ((rgba >> 16) & 0xff) / 255.0f, - ((rgba >> 8) & 0xff) / 255.0f, - (rgba & 0xff) / 255.0f); - - // FIX ME: is this correct? It seems to work to fix textures bleeding into us... - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - - DependencyManager::get()->renderQuad(x, y, width, height, color); +Stats::Stats(QQuickItem* parent) : QQuickItem(parent) { + INSTANCE = this; } bool Stats::includeTimingRecord(const QString& name) { @@ -190,161 +73,63 @@ bool Stats::includeTimingRecord(const QString& name) { return false; } -// display expanded or contracted stats -void Stats::display( - const float* color, - int horizontalOffset, - float fps, - int inPacketsPerSecond, - int outPacketsPerSecond, - int inKbitsPerSecond, - int outKbitsPerSecond, - int voxelPacketsToProcess) -{ - auto canvasSize = Application::getInstance()->getCanvasSize(); - - unsigned int backgroundColor = 0x33333399; - int verticalOffset = 0, lines = 0; - float scale = 0.10f; - float rotation = 0.0f; - int font = 2; - - QLocale locale(QLocale::English); - std::stringstream octreeStats; - - QSharedPointer bandwidthRecorder = DependencyManager::get(); - - if (_lastHorizontalOffset != horizontalOffset) { - resetWidth(canvasSize.x, horizontalOffset); - _lastHorizontalOffset = horizontalOffset; +#define STAT_UPDATE(name, src) \ + { \ + auto val = src; \ + if (_##name != val) { \ + _##name = val; \ + emit name##Changed(); \ + } \ } - glPointSize(1.0f); +#define STAT_UPDATE_FLOAT(name, src, epsilon) \ + { \ + float val = src; \ + if (abs(_##name - val) >= epsilon) { \ + _##name = val; \ + emit name##Changed(); \ + } \ + } + +void Stats::updateStats() { + if (!Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { + if (isVisible()) { + setVisible(false); + } + return; + } else { + if (!isVisible()) { + setVisible(true); + } + } + + auto nodeList = DependencyManager::get(); + auto avatarManager = DependencyManager::get(); // we need to take one avatar out so we don't include ourselves - int totalAvatars = DependencyManager::get()->size() - 1; - int totalServers = DependencyManager::get()->size(); + STAT_UPDATE(avatarCount, avatarManager->size() - 1); + STAT_UPDATE(serverCount, nodeList->size()); + STAT_UPDATE(framerate, (int)qApp->getFps()); - lines = 5; - int columnOneWidth = _generalStatsWidth; - - bool performanceTimerIsActive = PerformanceTimer::isActive(); - bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails); - if (displayPerf && performanceTimerIsActive) { - PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up - - columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // 3 columns wide... - // we will also include room for 1 line per timing record and a header of 4 lines - lines += 4; - - const QMap& allRecords = PerformanceTimer::getAllTimerRecords(); - QMapIterator i(allRecords); - bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen); - int statsLines = 0; - while (i.hasNext()) { - i.next(); - if (includeTimingRecord(i.key())) { - lines++; - statsLines++; - if (onlyDisplayTopTen && statsLines == 10) { - break; - } - } - } - } - - drawBackground(backgroundColor, horizontalOffset, 0, columnOneWidth, (lines + 1) * STATS_PELS_PER_LINE); - horizontalOffset += 5; - - int columnOneHorizontalOffset = horizontalOffset; - - QString serverNodes = QString("Servers: %1").arg(totalServers); - QString avatarNodes = QString("Avatars: %1").arg(totalAvatars); - QString framesPerSecond = QString("Framerate: %1 FPS").arg(fps, 3, 'f', 0); - - verticalOffset = STATS_PELS_INITIALOFFSET; // first one is offset by less than a line - drawText(horizontalOffset, verticalOffset, scale, rotation, font, serverNodes.toUtf8().constData(), color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarNodes.toUtf8().constData(), color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, framesPerSecond.toUtf8().constData(), color); - - QString packetsPerSecondString = QString("Packets In/Out: %1/%2").arg(inPacketsPerSecond).arg(outPacketsPerSecond); - QString averageMegabitsPerSecond = QString("Mbps In/Out: %1/%2"). - arg((float)inKbitsPerSecond * 1.0f / 1000.0f). - arg((float)outKbitsPerSecond * 1.0f / 1000.0f); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, packetsPerSecondString.toUtf8().constData(), color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, averageMegabitsPerSecond.toUtf8().constData(), color); - - - // TODO: the display of these timing details should all be moved to JavaScript - if (displayPerf && performanceTimerIsActive) { - bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen); - // Timing details... - verticalOffset += STATS_PELS_PER_LINE * 4; // skip 3 lines to be under the other columns - drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, - "-------------------------------------------------------- Function " - "------------------------------------------------------- --msecs- -calls--", color); - - // First iterate all the records, and for the ones that should be included, insert them into - // a new Map sorted by average time... - QMap sortedRecords; - const QMap& allRecords = PerformanceTimer::getAllTimerRecords(); - QMapIterator i(allRecords); - - while (i.hasNext()) { - i.next(); - if (includeTimingRecord(i.key())) { - float averageTime = (float)i.value().getMovingAverage() / (float)USECS_PER_MSEC; - sortedRecords.insertMulti(averageTime, i.key()); - } - } - - int linesDisplayed = 0; - QMapIterator j(sortedRecords); - j.toBack(); - while (j.hasPrevious()) { - j.previous(); - QChar noBreakingSpace = QChar::Nbsp; - QString functionName = j.value(); - const PerformanceTimerRecord& record = allRecords.value(functionName); - - QString perfLine = QString("%1: %2 [%3]"). - arg(QString(qPrintable(functionName)), 120, noBreakingSpace). - arg((float)record.getMovingAverage() / (float)USECS_PER_MSEC, 8, 'f', 3, noBreakingSpace). - arg((int)record.getCount(), 6, 10, noBreakingSpace); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, perfLine.toUtf8().constData(), color); - linesDisplayed++; - if (onlyDisplayTopTen && linesDisplayed == 10) { - break; - } - } - } - - - verticalOffset = STATS_PELS_INITIALOFFSET; - horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + 1; + auto bandwidthRecorder = DependencyManager::get(); + STAT_UPDATE(packetInCount, bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond()); + STAT_UPDATE(packetOutCount, bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond()); + STAT_UPDATE_FLOAT(mbpsIn, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f); + STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond() / 1000.0f, 0.01f); + // Second column: ping if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { - int pingAudio = -1, pingAvatar = -1, pingVoxel = -1, pingOctreeMax = -1; - - auto nodeList = DependencyManager::get(); SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); + STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1); + STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1); - pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : -1; - pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : -1; - - // Now handle voxel servers, since there could be more than one, we average their ping times + //// Now handle voxel servers, since there could be more than one, we average their ping times unsigned long totalPingOctree = 0; int octreeServerCount = 0; - - nodeList->eachNode([&totalPingOctree, &pingOctreeMax, &octreeServerCount](const SharedNodePointer& node){ + int pingOctreeMax = 0; + int pingVoxel; + nodeList->eachNode([&](const SharedNodePointer& node) { // TODO: this should also support entities if (node->getType() == NodeType::EntityServer) { totalPingOctree += node->getPingMs(); @@ -359,146 +144,101 @@ void Stats::display( pingVoxel = totalPingOctree / octreeServerCount; } - lines = _expanded ? 4 : 3; - - // only draw our background if column one didn't draw a wide background - if (columnOneWidth == _generalStatsWidth) { - drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, (lines + 1) * STATS_PELS_PER_LINE); - } - horizontalOffset += 5; - - - QString audioPing; - if (pingAudio >= 0) { - audioPing = QString("Audio ping: %1").arg(pingAudio); - } else { - audioPing = QString("Audio ping: --"); - } - - QString avatarPing; - if (pingAvatar >= 0) { - avatarPing = QString("Avatar ping: %1").arg(pingAvatar); - } else { - avatarPing = QString("Avatar ping: --"); - } - - QString voxelAvgPing; - if (pingVoxel >= 0) { - voxelAvgPing = QString("Entities avg ping: %1").arg(pingVoxel); - } else { - voxelAvgPing = QString("Entities avg ping: --"); - } - - drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioPing.toUtf8().constData(), color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPing.toUtf8().constData(), color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelAvgPing.toUtf8().constData(), color); - - if (_expanded) { - QString voxelMaxPing; - if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid. - voxelMaxPing = QString("Voxel max ping: %1").arg(pingOctreeMax); - } else { - voxelMaxPing = QString("Voxel max ping: --"); - } - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelMaxPing.toUtf8().constData(), color); - } - - verticalOffset = STATS_PELS_INITIALOFFSET; - horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + 2; + STAT_UPDATE(entitiesPing, pingVoxel); + //if (_expanded) { + // QString voxelMaxPing; + // if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid. + // voxelMaxPing = QString("Voxel max ping: %1").arg(pingOctreeMax); + // } else { + // voxelMaxPing = QString("Voxel max ping: --"); + // } + } else { + // -2 causes the QML to hide the ping column + STAT_UPDATE(audioPing, -2); } - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + + // Third column, avatar stats + MyAvatar* myAvatar = avatarManager->getMyAvatar(); glm::vec3 avatarPos = myAvatar->getPosition(); - - lines = _expanded ? 7 : 3; - - if (columnOneWidth == _generalStatsWidth) { - drawBackground(backgroundColor, horizontalOffset, 0, _geoStatsWidth, (lines + 1) * STATS_PELS_PER_LINE); - } - horizontalOffset += 5; - - QString avatarPosition = QString("Position: %1, %2, %3"). - arg(avatarPos.x, -1, 'f', 1). - arg(avatarPos.y, -1, 'f', 1). - arg(avatarPos.z, -1, 'f', 1); - QString avatarVelocity = QString("Velocity: %1").arg(glm::length(myAvatar->getVelocity()), -1, 'f', 1); - QString avatarBodyYaw = QString("Yaw: %1").arg(myAvatar->getBodyYaw(), -1, 'f', 1); - QString avatarMixerStats; - - drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPosition.toUtf8().constData(), color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarVelocity.toUtf8().constData(), color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarBodyYaw.toUtf8().constData(), color); - + STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z)); + STAT_UPDATE_FLOAT(velocity, glm::length(myAvatar->getVelocity()), 0.1f); + STAT_UPDATE_FLOAT(yaw, myAvatar->getBodyYaw(), 0.1f); if (_expanded) { - SharedNodePointer avatarMixer = DependencyManager::get()->soloNodeOfType(NodeType::AvatarMixer); + SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer); if (avatarMixer) { - avatarMixerStats = QString("Avatar Mixer: %1 kbps, %2 pps"). - arg(roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) + - bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))). - arg(roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) + - bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer))); + STAT_UPDATE(avatarMixerKbps, roundf( + bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AvatarMixer) + + bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AvatarMixer))); + STAT_UPDATE(avatarMixerPps, roundf( + bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AvatarMixer) + + bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AvatarMixer))); } else { - avatarMixerStats = QString("No Avatar Mixer"); + STAT_UPDATE(avatarMixerKbps, -1); + STAT_UPDATE(avatarMixerPps, -1); + } + SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); + if (audioMixerNode) { + STAT_UPDATE(audioMixerKbps, roundf( + bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) + + bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))); + STAT_UPDATE(audioMixerPps, roundf( + bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) + + bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer))); + } else { + STAT_UPDATE(audioMixerKbps, -1); + STAT_UPDATE(audioMixerPps, -1); } - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarMixerStats.toUtf8().constData(), color); - - stringstream downloads; - downloads << "Downloads: "; - foreach (Resource* resource, ResourceCache::getLoadingRequests()) { - downloads << (int)(resource->getProgress() * 100.0f) << "% "; - } - downloads << "(" << ResourceCache::getPendingRequestCount() << " pending)"; - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, downloads.str().c_str(), color); - } + STAT_UPDATE(downloads, ResourceCache::getLoadingRequests().size()); + STAT_UPDATE(downloadsPending, ResourceCache::getPendingRequestCount()); + // TODO fix to match original behavior + //stringstream downloads; + //downloads << "Downloads: "; + //foreach(Resource* resource, ) { + // downloads << (int)(resource->getProgress() * 100.0f) << "% "; + //} + //downloads << "(" << << " pending)"; + } // expanded avatar column - verticalOffset = STATS_PELS_INITIALOFFSET; - horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3; - - lines = _expanded ? 10 : 3; - - drawBackground(backgroundColor, horizontalOffset, 0, canvasSize.x - horizontalOffset, - (lines + 1) * STATS_PELS_PER_LINE); - horizontalOffset += 5; - - // Model/Entity render details - octreeStats.str(""); - octreeStats << "Triangles: " << _renderDetails._trianglesRendered - << " / Quads:" << _renderDetails._quadsRendered - << " / Material Switches:" << _renderDetails._materialSwitches; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); + // Fourth column, octree stats +} +void Stats::setRenderDetails(const RenderDetails& details) { + //STATS_PROPERTY(int, triangles, 0) + //STATS_PROPERTY(int, quads, 0) + //STATS_PROPERTY(int, materialSwitches, 0) + //STATS_PROPERTY(int, meshOpaque, 0) + //STATS_PROPERTY(int, meshTranslucent, 0) + //STATS_PROPERTY(int, opaqueConsidered, 0) + //STATS_PROPERTY(int, opaqueOutOfView, 0) + //STATS_PROPERTY(int, opaqueTooSmall, 0) + //STATS_PROPERTY(int, translucentConsidered, 0) + //STATS_PROPERTY(int, translucentOutOfView, 0) + //STATS_PROPERTY(int, translucentTooSmall, 0) + //STATS_PROPERTY(int, octreeElementsServer, 0) + //STATS_PROPERTY(int, octreeElementsLocal, 0) + STAT_UPDATE(triangles, details._trianglesRendered); + STAT_UPDATE(quads, details._quadsRendered); + STAT_UPDATE(materialSwitches, details._materialSwitches); if (_expanded) { - octreeStats.str(""); - octreeStats << " Mesh Parts Rendered Opaque: " << _renderDetails._opaque._rendered - << " / Translucent:" << _renderDetails._translucent._rendered; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - - octreeStats.str(""); - octreeStats << " Opaque considered: " << _renderDetails._opaque._considered - << " / Out of view:" << _renderDetails._opaque._outOfView - << " / Too small:" << _renderDetails._opaque._tooSmall; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - - octreeStats.str(""); - octreeStats << " Translucent considered: " << _renderDetails._translucent._considered - << " / Out of view:" << _renderDetails._translucent._outOfView - << " / Too small:" << _renderDetails._translucent._tooSmall; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); + STAT_UPDATE(meshOpaque, details._opaque._rendered); + STAT_UPDATE(meshTranslucent, details._opaque._rendered); + STAT_UPDATE(opaqueConsidered, details._opaque._considered); + STAT_UPDATE(opaqueOutOfView, details._opaque._outOfView); + STAT_UPDATE(opaqueTooSmall, details._opaque._tooSmall); + STAT_UPDATE(translucentConsidered, details._translucent._considered); + STAT_UPDATE(translucentOutOfView, details._translucent._outOfView); + STAT_UPDATE(translucentTooSmall, details._translucent._tooSmall); } +} + +/* +// display expanded or contracted stats +void Stats::display( + int voxelPacketsToProcess) +{ // iterate all the current voxel stats, and list their sending modes, and total voxel counts std::stringstream sendingMode(""); sendingMode << "Octree Sending Mode: ["; @@ -619,3 +359,81 @@ void Stats::display( drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); } + +//// Performance timer + + + bool performanceTimerIsActive = PerformanceTimer::isActive(); + bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails); + if (displayPerf && performanceTimerIsActive) { + PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up + columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // 3 columns wide... + // we will also include room for 1 line per timing record and a header of 4 lines + lines += 4; + + const QMap& allRecords = PerformanceTimer::getAllTimerRecords(); + QMapIterator i(allRecords); + bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen); + int statsLines = 0; + while (i.hasNext()) { + i.next(); + if (includeTimingRecord(i.key())) { + lines++; + statsLines++; + if (onlyDisplayTopTen && statsLines == 10) { + break; + } + } + } + } + + + // TODO: the display of these timing details should all be moved to JavaScript + if (displayPerf && performanceTimerIsActive) { + bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen); + // Timing details... + verticalOffset += STATS_PELS_PER_LINE * 4; // skip 3 lines to be under the other columns + drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, + "-------------------------------------------------------- Function " + "------------------------------------------------------- --msecs- -calls--", color); + + // First iterate all the records, and for the ones that should be included, insert them into + // a new Map sorted by average time... + QMap sortedRecords; + const QMap& allRecords = PerformanceTimer::getAllTimerRecords(); + QMapIterator i(allRecords); + + while (i.hasNext()) { + i.next(); + if (includeTimingRecord(i.key())) { + float averageTime = (float)i.value().getMovingAverage() / (float)USECS_PER_MSEC; + sortedRecords.insertMulti(averageTime, i.key()); + } + } + + int linesDisplayed = 0; + QMapIterator j(sortedRecords); + j.toBack(); + while (j.hasPrevious()) { + j.previous(); + QChar noBreakingSpace = QChar::Nbsp; + QString functionName = j.value(); + const PerformanceTimerRecord& record = allRecords.value(functionName); + + QString perfLine = QString("%1: %2 [%3]"). + arg(QString(qPrintable(functionName)), 120, noBreakingSpace). + arg((float)record.getMovingAverage() / (float)USECS_PER_MSEC, 8, 'f', 3, noBreakingSpace). + arg((int)record.getCount(), 6, 10, noBreakingSpace); + + verticalOffset += STATS_PELS_PER_LINE; + drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, perfLine.toUtf8().constData(), color); + linesDisplayed++; + if (onlyDisplayTopTen && linesDisplayed == 10) { + break; + } + } + } + + + +*/ \ No newline at end of file diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 4c6d6ede4e..553b54d04b 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -13,47 +13,113 @@ #define hifi_Stats_h #include +#include +#include +#include #include -class Stats: public QObject { +#define STATS_PROPERTY(type, name, initialValue) \ + Q_PROPERTY(type name READ name NOTIFY name##Changed) \ +public: \ + type name() { return _##name; }; \ +private: \ + type _##name{ initialValue }; + + +class Stats : public QQuickItem { Q_OBJECT + HIFI_QML_DECL + Q_PROPERTY(bool expanded READ isExpanded NOTIFY expandedChanged) + STATS_PROPERTY(int, serverCount, 0) + STATS_PROPERTY(int, framerate, 0) + STATS_PROPERTY(int, avatarCount, 0) + STATS_PROPERTY(int, packetInCount, 0) + STATS_PROPERTY(int, packetOutCount, 0) + STATS_PROPERTY(float, mbpsIn, 0) + STATS_PROPERTY(float, mbpsOut, 0) + STATS_PROPERTY(int, audioPing, 0) + STATS_PROPERTY(int, avatarPing, 0) + STATS_PROPERTY(int, entitiesPing, 0) + STATS_PROPERTY(QVector3D, position, QVector3D(0, 0, 0) ) + STATS_PROPERTY(float, velocity, 0) + STATS_PROPERTY(float, yaw, 0) + STATS_PROPERTY(int, avatarMixerKbps, 0) + STATS_PROPERTY(int, avatarMixerPps, 0) + STATS_PROPERTY(int, audioMixerKbps, 0) + STATS_PROPERTY(int, audioMixerPps, 0) + STATS_PROPERTY(int, downloads, 0) + STATS_PROPERTY(int, downloadsPending, 0) + STATS_PROPERTY(int, triangles, 0) + STATS_PROPERTY(int, quads, 0) + STATS_PROPERTY(int, materialSwitches, 0) + STATS_PROPERTY(int, meshOpaque, 0) + STATS_PROPERTY(int, meshTranslucent, 0) + STATS_PROPERTY(int, opaqueConsidered, 0) + STATS_PROPERTY(int, opaqueOutOfView, 0) + STATS_PROPERTY(int, opaqueTooSmall, 0) + STATS_PROPERTY(int, translucentConsidered, 0) + STATS_PROPERTY(int, translucentOutOfView, 0) + STATS_PROPERTY(int, translucentTooSmall, 0) + STATS_PROPERTY(int, octreeElementsServer, 0) + STATS_PROPERTY(int, octreeElementsLocal, 0) public: static Stats* getInstance(); - Stats(); - - static void drawBackground(unsigned int rgba, int x, int y, int width, int height); - - void toggleExpanded(); - bool isExpanded() { return _expanded; } - - void checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset); - void resetWidth(int width, int horizontalOffset); - void display(const float* color, int horizontalOffset, float fps, int inPacketsPerSecond, int outPacketsPerSecond, - int inKbitsPerSecond, int outKbitsPerSecond, int voxelPacketsToProcess); + Stats(QQuickItem* parent = nullptr); bool includeTimingRecord(const QString& name); - - void setRenderDetails(const RenderDetails& details) { _renderDetails = details; } - + void setRenderDetails(const RenderDetails& details); + + void updateStats(); + + bool isExpanded() { return _expanded; } + + void setExpanded(bool expanded) { + if (expanded != _expanded) { + _expanded = expanded; + } + } + +signals: + void expandedChanged(); + void serverCountChanged(); + void framerateChanged(); + void avatarCountChanged(); + void packetInCountChanged(); + void packetOutCountChanged(); + void mbpsInChanged(); + void mbpsOutChanged(); + void audioPingChanged(); + void avatarPingChanged(); + void entitiesPingChanged(); + void positionChanged(); + void velocityChanged(); + void yawChanged(); + void avatarMixerKbpsChanged(); + void avatarMixerPpsChanged(); + void audioMixerKbpsChanged(); + void audioMixerPpsChanged(); + void downloadsChanged(); + void downloadsPendingChanged(); + void trianglesChanged(); + void quadsChanged(); + void materialSwitchesChanged(); + void meshOpaqueChanged(); + void meshTranslucentChanged(); + void opaqueConsideredChanged(); + void opaqueOutOfViewChanged(); + void opaqueTooSmallChanged(); + void translucentConsideredChanged(); + void translucentOutOfViewChanged(); + void translucentTooSmallChanged(); + void octreeElementsServerChanged(); + void octreeElementsLocalChanged(); + private: - static Stats* _sharedInstance; - - bool _expanded; - - int _recentMaxPackets; // recent max incoming voxel packets to process - bool _resetRecentMaxPacketsSoon; - - int _generalStatsWidth; - int _bandwidthStatsWidth; - int _pingStatsWidth; - int _geoStatsWidth; - int _octreeStatsWidth; - - int _lastHorizontalOffset; - - RenderDetails _renderDetails; + int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process + bool _resetRecentMaxPacketsSoon{ true }; + bool _expanded{ false }; }; #endif // hifi_Stats_h diff --git a/libraries/render-utils/src/OffscreenQmlSurface.cpp b/libraries/render-utils/src/OffscreenQmlSurface.cpp index 9b497964e7..07a887be2c 100644 --- a/libraries/render-utils/src/OffscreenQmlSurface.cpp +++ b/libraries/render-utils/src/OffscreenQmlSurface.cpp @@ -91,6 +91,7 @@ void OffscreenQmlSurface::resize(const QSize& newSize) { // Qt bug in 5.4 forces this check of pixel ratio, // even though we're rendering offscreen. qreal pixelRatio = 1.0; + _qmlEngine->rootContext()->setContextProperty("surfaceSize", newSize); if (_renderControl && _renderControl->_renderWindow) { pixelRatio = _renderControl->_renderWindow->devicePixelRatio(); } else { @@ -111,7 +112,6 @@ void OffscreenQmlSurface::resize(const QSize& newSize) { _quickWindow->contentItem()->setSize(newSize); } - // Update our members if (_rootItem) { _rootItem->setSize(newSize); @@ -376,4 +376,4 @@ void OffscreenQmlSurface::setProxyWindow(QWindow* window) { QQuickWindow* OffscreenQmlSurface::getWindow() { return _quickWindow; -} \ No newline at end of file +} diff --git a/libraries/ui/src/Tooltip.cpp b/libraries/ui/src/Tooltip.cpp new file mode 100644 index 0000000000..35b17b74a2 --- /dev/null +++ b/libraries/ui/src/Tooltip.cpp @@ -0,0 +1,51 @@ +// +// Tooltip.cpp +// +// Created by Bradley Austin Davis on 2015/04/14 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "Tooltip.h" +#include + +HIFI_QML_DEF(Tooltip) + +Tooltip::Tooltip(QQuickItem* parent) : QQuickItem(parent) { +} + +Tooltip::~Tooltip() { +} + +QString Tooltip::text() const { + return _text; +} + +void Tooltip::setText(const QString& arg) { + if (arg != _text) { + _text = arg; + emit textChanged(); + } +} + +void Tooltip::setVisible(bool visible) { + QQuickItem::setVisible(visible); +} + +QString Tooltip::showTip(const QString& text) { + const QString newTipId = QUuid().createUuid().toString(); + Tooltip::show([&](QQmlContext*, QObject* object) { + object->setObjectName(newTipId); + object->setProperty("text", text); + }); + return newTipId; +} + +void Tooltip::closeTip(const QString& tipId) { + auto rootItem = DependencyManager::get()->getRootItem(); + QQuickItem* that = rootItem->findChild(tipId); + if (that) { + that->deleteLater(); + } +} \ No newline at end of file diff --git a/libraries/ui/src/Tooltip.h b/libraries/ui/src/Tooltip.h new file mode 100644 index 0000000000..7292fe9399 --- /dev/null +++ b/libraries/ui/src/Tooltip.h @@ -0,0 +1,45 @@ +// +// Tooltip.h +// +// Created by Bradley Austin Davis on 2015/04/14 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_Tooltip_h +#define hifi_Tooltip_h + +#include "OffscreenQmlDialog.h" + +class Tooltip : public QQuickItem +{ + Q_OBJECT + HIFI_QML_DECL + +private: + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + +public: + Tooltip(QQuickItem* parent = 0); + virtual ~Tooltip(); + + QString text() const; + + static QString showTip(const QString& text); + static void closeTip(const QString& tipId); + +public slots: + virtual void setVisible(bool v); + void setText(const QString& arg); + +signals: + void textChanged(); + +private: + QString _text; +}; + +#endif // hifi_Tooltip_h From 2bf53b625e4b2cc7f6e210aae456cac633fa0a77 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 17 Jun 2015 02:00:13 -0700 Subject: [PATCH 08/35] Working on overlays and stats --- interface/resources/qml/Stats.qml | 164 +++++++-- interface/src/Application.cpp | 3 +- interface/src/ui/ApplicationOverlay.cpp | 12 +- interface/src/ui/Stats.cpp | 345 +++++++----------- interface/src/ui/Stats.h | 29 +- .../render-utils/src/OffscreenQmlSurface.cpp | 4 + 6 files changed, 306 insertions(+), 251 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 6173b8517d..89961aeb9c 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -11,19 +11,22 @@ Hifi.Stats { readonly property int sTATS_PING_MIN_WIDTH: 190 readonly property int sTATS_GEO_MIN_WIDTH: 240 readonly property int sTATS_OCTREE_MIN_WIDTH: 410 + readonly property int fontSize: 12 + readonly property string fontColor: "white" + readonly property string bgColor: "#99333333" onParentChanged: { root.x = parent.width - root.width; } + Row { - z: 100 id: row spacing: 8 Rectangle { width: generalCol.width + 8; height: generalCol.height + 8; - color: "#99333333"; + color: root.bgColor; MouseArea { anchors.fill: parent @@ -34,18 +37,38 @@ Hifi.Stats { id: generalCol spacing: 4; x: 4; y: 4; width: sTATS_GENERAL_MIN_WIDTH - Text { color: "white"; text: "Servers: " + root.serverCount } - Text { color: "white"; text: "Avatars: " + root.avatarCount } - Text { color: "white"; text: "Framerate: " + root.framerate } - Text { color: "white"; text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount } - Text { color: "white"; text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2) } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Servers: " + root.serverCount + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Avatars: " + root.avatarCount + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Framerate: " + root.framerate + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2) + } } } Rectangle { width: pingCol.width + 8 height: pingCol.height + 8 - color: "#99333333" + color: root.bgColor; visible: root.audioPing != -2 MouseArea { anchors.fill: parent @@ -55,45 +78,68 @@ Hifi.Stats { id: pingCol spacing: 4; x: 4; y: 4; width: sTATS_PING_MIN_WIDTH - Text { color: "white"; text: "Audio ping: " + root.audioPing } - Text { color: "white"; text: "Avatar ping: " + root.avatarPing } - Text { color: "white"; text: "Entities avg ping: " + root.entitiesPing } - Text { color: "white"; text: "Voxel max ping: " + 0; visible: root.expanded; } + Text { + color: root.fontColor + font.pixelSize: root.fontSize + text: "Audio ping: " + root.audioPing + } + Text { + color: root.fontColor + font.pixelSize: root.fontSize + text: "Avatar ping: " + root.avatarPing + } + Text { + color: root.fontColor + font.pixelSize: root.fontSize + text: "Entities avg ping: " + root.entitiesPing + } + Text { + color: root.fontColor + font.pixelSize: root.fontSize + visible: root.expanded; + text: "Voxel max ping: " + 0 + } } } + Rectangle { - width: geoCol.width - height: geoCol.height - color: "#99333333" + width: geoCol.width + 8 + height: geoCol.height + 8 + color: root.bgColor; MouseArea { anchors.fill: parent onClicked: { root.expanded = !root.expanded; } } Column { - spacing: 4 id: geoCol + spacing: 4; x: 4; y: 4; width: sTATS_GEO_MIN_WIDTH Text { - color: "white"; + color: root.fontColor; + font.pixelSize: root.fontSize text: "Position: " + root.position.x.toFixed(1) + ", " + root.position.y.toFixed(1) + ", " + root.position.z.toFixed(1) } Text { - color: "white"; + color: root.fontColor; + font.pixelSize: root.fontSize text: "Velocity: " + root.velocity.toFixed(1) } Text { - color: "white"; + color: root.fontColor; + font.pixelSize: root.fontSize text: "Yaw: " + root.yaw.toFixed(1) } Text { - color: "white"; + color: root.fontColor; + font.pixelSize: root.fontSize visible: root.expanded; text: "Avatar Mixer: " + root.avatarMixerKbps + " kbps, " + root.avatarMixerPps + "pps"; } Text { - color: "white"; + color: root.fontColor; + font.pixelSize: root.fontSize visible: root.expanded; text: "Downloads: "; } @@ -102,7 +148,7 @@ Hifi.Stats { Rectangle { width: octreeCol.width + 8 height: octreeCol.height + 8 - color: "#99333333" + color: root.bgColor; MouseArea { anchors.fill: parent onClicked: { root.expanded = !root.expanded; } @@ -112,31 +158,93 @@ Hifi.Stats { spacing: 4; x: 4; y: 4; width: sTATS_OCTREE_MIN_WIDTH Text { - color: "white"; + color: root.fontColor; + font.pixelSize: root.fontSize text: "Triangles: " + root.triangles + " / Quads: " + root.quads + " / Material Switches: " + root.materialSwitches } Text { - color: "white"; + color: root.fontColor; + font.pixelSize: root.fontSize visible: root.expanded; text: "\tMesh Parts Rendered Opaque: " + root.meshOpaque + " / Translucent: " + root.meshTranslucent; } Text { - color: "white"; - visible: root.expanded; + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded; text: "\tOpaque considered: " + root.opaqueConsidered + " / Out of view: " + root.opaqueOutOfView + " / Too small: " + root.opaqueTooSmall; } Text { - color: "white"; + color: root.fontColor; + font.pixelSize: root.fontSize visible: !root.expanded - text: "Octree Elements Server: "; + text: "Octree Elements Server: " + root.serverElements + + " Local: " + root.localElements; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "Octree Sending Mode: " + root.sendingMode; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "Octree Packets to Process: " + root.packetStats; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "Octree Elements - "; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "\tServer: " + root.serverElements + + " Internal: " + root.serverInternal + + " Leaves: " + root.serverLeaves; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "\tLocal: " + root.localElements + + " Internal: " + root.localInternal + + " Leaves: " + root.localLeaves; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "LOD: " + root.lodStatus; } } } } + Rectangle { + y: 250 + width: perfText.width + 8 + height: perfText.height + 8 + color: root.bgColor; + anchors.top: row.bottom + 16 + Text { + x: 4; y: 4 + id: perfText + color: root.fontColor + font.family: "Lucida Console" + text: "-------------------------------------------------------- Function " + + "------------------------------------------------------- --msecs- -calls--\n" + + root.timingStats; + } + } + Connections { target: root.parent onWidthChanged: { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 52aa9e97b3..d7e72e6541 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1817,6 +1817,7 @@ void Application::idle() { targetFramePeriod = 1000.0 / targetFramerate; } double timeSinceLastUpdate = (double)_lastTimeUpdated.nsecsElapsed() / 1000000.0; + //if (true) { if (timeSinceLastUpdate > targetFramePeriod) { _lastTimeUpdated.start(); { @@ -1843,7 +1844,7 @@ void Application::idle() { } // After finishing all of the above work, restart the idle timer, allowing 2ms to process events. - idleTimer->start(2); + idleTimer->start(0); } } diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 86f06ba7fa..ba0cdf3be0 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -87,7 +87,6 @@ ApplicationOverlay::~ApplicationOverlay() { void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); - Stats::getInstance()->updateStats(); glm::vec2 size = qApp->getCanvasSize(); // TODO Handle fading and deactivation/activation of UI @@ -104,8 +103,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { //renderOverlays(renderArgs); //renderAudioMeter(renderArgs); //renderCameraToggle(renderArgs); - //renderStatsAndLogs(renderArgs); - + renderStatsAndLogs(renderArgs); renderDomainConnectionStatusBorder(renderArgs); renderQmlUi(renderArgs); @@ -370,14 +368,9 @@ void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) { // Display stats and log text onscreen // Determine whether to compute timing details - bool shouldDisplayTimingDetail = Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && - Menu::getInstance()->isOptionChecked(MenuOption::Stats); //&& - // Stats::getInstance()->isExpanded(); - if (shouldDisplayTimingDetail != PerformanceTimer::isActive()) { - PerformanceTimer::setActive(shouldDisplayTimingDetail); - } Stats::getInstance()->updateStats(); + /* // Show on-screen msec timer if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) { auto canvasSize = qApp->getCanvasSize(); @@ -401,6 +394,7 @@ void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) { glEnable(GL_LIGHTING); glEnable(GL_BLEND); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + */ } void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderArgs) { diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index fc9ceb8a67..ca91e3a919 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -13,6 +13,8 @@ #include "Stats.h" +#include + #include #include #include @@ -41,7 +43,6 @@ Stats* Stats::getInstance() { if (!INSTANCE) { Stats::registerType(); Stats::show(); - //Stats::toggle(); Q_ASSERT(INSTANCE); } return INSTANCE; @@ -104,6 +105,13 @@ void Stats::updateStats() { } } + bool shouldDisplayTimingDetail = Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && + Menu::getInstance()->isOptionChecked(MenuOption::Stats) && isExpanded(); + if (shouldDisplayTimingDetail != PerformanceTimer::isActive()) { + PerformanceTimer::setActive(shouldDisplayTimingDetail); + } + + auto nodeList = DependencyManager::get(); auto avatarManager = DependencyManager::get(); // we need to take one avatar out so we don't include ourselves @@ -144,7 +152,7 @@ void Stats::updateStats() { pingVoxel = totalPingOctree / octreeServerCount; } - STAT_UPDATE(entitiesPing, pingVoxel); + //STAT_UPDATE(entitiesPing, pingVoxel); //if (_expanded) { // QString voxelMaxPing; // if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid. @@ -202,22 +210,137 @@ void Stats::updateStats() { } // expanded avatar column // Fourth column, octree stats + int serverCount = 0; + int movingServerCount = 0; + unsigned long totalNodes = 0; + unsigned long totalInternal = 0; + unsigned long totalLeaves = 0; + std::stringstream sendingModeStream(""); + sendingModeStream << "["; + NodeToOctreeSceneStats* octreeServerSceneStats = Application::getInstance()->getOcteeSceneStats(); + for (NodeToOctreeSceneStatsIterator i = octreeServerSceneStats->begin(); i != octreeServerSceneStats->end(); i++) { + //const QUuid& uuid = i->first; + OctreeSceneStats& stats = i->second; + serverCount++; + if (_expanded) { + if (serverCount > 1) { + sendingModeStream << ","; + } + if (stats.isMoving()) { + sendingModeStream << "M"; + movingServerCount++; + } else { + sendingModeStream << "S"; + } + } + + // calculate server node totals + totalNodes += stats.getTotalElements(); + if (_expanded) { + totalInternal += stats.getTotalInternal(); + totalLeaves += stats.getTotalLeaves(); + } + } + if (_expanded) { + if (serverCount == 0) { + sendingModeStream << "---"; + } + sendingModeStream << "] " << serverCount << " servers"; + if (movingServerCount > 0) { + sendingModeStream << " "; + } else { + sendingModeStream << " "; + } + QString sendingModeResult = sendingModeStream.str().c_str(); + STAT_UPDATE(sendingMode, sendingModeResult); + } + + // Incoming packets + QLocale locale(QLocale::English); + auto voxelPacketsToProcess = qApp->getOctreePacketProcessor().packetsToProcessCount(); + if (_expanded) { + std::stringstream octreeStats; + QString packetsString = locale.toString((int)voxelPacketsToProcess); + QString maxString = locale.toString((int)_recentMaxPackets); + octreeStats << "Octree Packets to Process: " << qPrintable(packetsString) + << " [Recent Max: " << qPrintable(maxString) << "]"; + QString str = octreeStats.str().c_str(); + STAT_UPDATE(packetStats, str); + // drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); + } + + if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) { + _recentMaxPackets = 0; + _resetRecentMaxPacketsSoon = false; + } + if (voxelPacketsToProcess == 0) { + _resetRecentMaxPacketsSoon = true; + } else if (voxelPacketsToProcess > _recentMaxPackets) { + _recentMaxPackets = voxelPacketsToProcess; + } + + // Server Octree Elements + STAT_UPDATE(serverElements, totalNodes); + STAT_UPDATE(localElements, OctreeElement::getNodeCount()); + + if (_expanded) { + STAT_UPDATE(serverInternal, totalInternal); + STAT_UPDATE(serverLeaves, totalLeaves); + // Local Voxels + STAT_UPDATE(localInternal, OctreeElement::getInternalNodeCount()); + STAT_UPDATE(localLeaves, OctreeElement::getLeafNodeCount()); + // LOD Details + STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get()->getLODFeedbackText()); + } + + bool performanceTimerIsActive = PerformanceTimer::isActive(); + bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails); + if (displayPerf && performanceTimerIsActive) { + PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up + + // we will also include room for 1 line per timing record and a header of 4 lines + // Timing details... + + // First iterate all the records, and for the ones that should be included, insert them into + // a new Map sorted by average time... + bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen); + QMap sortedRecords; + const QMap& allRecords = PerformanceTimer::getAllTimerRecords(); + QMapIterator i(allRecords); + + while (i.hasNext()) { + i.next(); + if (includeTimingRecord(i.key())) { + float averageTime = (float)i.value().getMovingAverage() / (float)USECS_PER_MSEC; + sortedRecords.insertMulti(averageTime, i.key()); + } + } + + int linesDisplayed = 0; + QMapIterator j(sortedRecords); + j.toBack(); + QString perfLines; + while (j.hasPrevious()) { + j.previous(); + static const QChar noBreakingSpace = QChar::Nbsp; + QString functionName = j.value(); + const PerformanceTimerRecord& record = allRecords.value(functionName); + perfLines += QString("%1: %2 [%3]\n"). + arg(QString(qPrintable(functionName)), 120, noBreakingSpace). + arg((float)record.getMovingAverage() / (float)USECS_PER_MSEC, 8, 'f', 3, noBreakingSpace). + arg((int)record.getCount(), 6, 10, noBreakingSpace); + linesDisplayed++; + if (onlyDisplayTopTen && linesDisplayed == 10) { + break; + } + } + _timingStats = perfLines; + emit timingStatsChanged(); + } + } void Stats::setRenderDetails(const RenderDetails& details) { - //STATS_PROPERTY(int, triangles, 0) - //STATS_PROPERTY(int, quads, 0) - //STATS_PROPERTY(int, materialSwitches, 0) - //STATS_PROPERTY(int, meshOpaque, 0) - //STATS_PROPERTY(int, meshTranslucent, 0) - //STATS_PROPERTY(int, opaqueConsidered, 0) - //STATS_PROPERTY(int, opaqueOutOfView, 0) - //STATS_PROPERTY(int, opaqueTooSmall, 0) - //STATS_PROPERTY(int, translucentConsidered, 0) - //STATS_PROPERTY(int, translucentOutOfView, 0) - //STATS_PROPERTY(int, translucentTooSmall, 0) - //STATS_PROPERTY(int, octreeElementsServer, 0) - //STATS_PROPERTY(int, octreeElementsLocal, 0) STAT_UPDATE(triangles, details._trianglesRendered); STAT_UPDATE(quads, details._quadsRendered); STAT_UPDATE(materialSwitches, details._materialSwitches); @@ -240,200 +363,8 @@ void Stats::display( int voxelPacketsToProcess) { // iterate all the current voxel stats, and list their sending modes, and total voxel counts - std::stringstream sendingMode(""); - sendingMode << "Octree Sending Mode: ["; - int serverCount = 0; - int movingServerCount = 0; - unsigned long totalNodes = 0; - unsigned long totalInternal = 0; - unsigned long totalLeaves = 0; - NodeToOctreeSceneStats* octreeServerSceneStats = Application::getInstance()->getOcteeSceneStats(); - for(NodeToOctreeSceneStatsIterator i = octreeServerSceneStats->begin(); i != octreeServerSceneStats->end(); i++) { - //const QUuid& uuid = i->first; - OctreeSceneStats& stats = i->second; - serverCount++; - if (_expanded) { - if (serverCount > 1) { - sendingMode << ","; - } - if (stats.isMoving()) { - sendingMode << "M"; - movingServerCount++; - } else { - sendingMode << "S"; - } - } - // calculate server node totals - totalNodes += stats.getTotalElements(); - if (_expanded) { - totalInternal += stats.getTotalInternal(); - totalLeaves += stats.getTotalLeaves(); - } - } - if (_expanded) { - if (serverCount == 0) { - sendingMode << "---"; - } - sendingMode << "] " << serverCount << " servers"; - if (movingServerCount > 0) { - sendingMode << " "; - } else { - sendingMode << " "; - } - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)sendingMode.str().c_str(), color); - } - - // Incoming packets - if (_expanded) { - octreeStats.str(""); - QString packetsString = locale.toString((int)voxelPacketsToProcess); - QString maxString = locale.toString((int)_recentMaxPackets); - octreeStats << "Octree Packets to Process: " << qPrintable(packetsString) - << " [Recent Max: " << qPrintable(maxString) << "]"; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - } - - if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) { - _recentMaxPackets = 0; - _resetRecentMaxPacketsSoon = false; - } - if (voxelPacketsToProcess == 0) { - _resetRecentMaxPacketsSoon = true; - } else { - if (voxelPacketsToProcess > _recentMaxPackets) { - _recentMaxPackets = voxelPacketsToProcess; - } - } - - QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' '); - unsigned long localTotal = OctreeElement::getNodeCount(); - QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' '); - - // Server Octree Elements - if (!_expanded) { - octreeStats.str(""); - octreeStats << "Octree Elements Server: " << qPrintable(serversTotalString) - << " Local:" << qPrintable(localTotalString); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - } - - if (_expanded) { - octreeStats.str(""); - octreeStats << "Octree Elements -"; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - - QString serversInternalString = locale.toString((uint)totalInternal); - QString serversLeavesString = locale.toString((uint)totalLeaves); - - octreeStats.str(""); - octreeStats << " Server: " << qPrintable(serversTotalString) << - " Internal: " << qPrintable(serversInternalString) << - " Leaves: " << qPrintable(serversLeavesString); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - - // Local Voxels - unsigned long localInternal = OctreeElement::getInternalNodeCount(); - unsigned long localLeaves = OctreeElement::getLeafNodeCount(); - QString localInternalString = locale.toString((uint)localInternal); - QString localLeavesString = locale.toString((uint)localLeaves); - - octreeStats.str(""); - octreeStats << " Local: " << qPrintable(serversTotalString) << - " Internal: " << qPrintable(localInternalString) << - " Leaves: " << qPrintable(localLeavesString) << ""; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - } - - // LOD Details - octreeStats.str(""); - QString displayLODDetails = DependencyManager::get()->getLODFeedbackText(); - octreeStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed()); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); } -//// Performance timer - - - bool performanceTimerIsActive = PerformanceTimer::isActive(); - bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails); - if (displayPerf && performanceTimerIsActive) { - PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up - columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // 3 columns wide... - // we will also include room for 1 line per timing record and a header of 4 lines - lines += 4; - - const QMap& allRecords = PerformanceTimer::getAllTimerRecords(); - QMapIterator i(allRecords); - bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen); - int statsLines = 0; - while (i.hasNext()) { - i.next(); - if (includeTimingRecord(i.key())) { - lines++; - statsLines++; - if (onlyDisplayTopTen && statsLines == 10) { - break; - } - } - } - } - - - // TODO: the display of these timing details should all be moved to JavaScript - if (displayPerf && performanceTimerIsActive) { - bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen); - // Timing details... - verticalOffset += STATS_PELS_PER_LINE * 4; // skip 3 lines to be under the other columns - drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, - "-------------------------------------------------------- Function " - "------------------------------------------------------- --msecs- -calls--", color); - - // First iterate all the records, and for the ones that should be included, insert them into - // a new Map sorted by average time... - QMap sortedRecords; - const QMap& allRecords = PerformanceTimer::getAllTimerRecords(); - QMapIterator i(allRecords); - - while (i.hasNext()) { - i.next(); - if (includeTimingRecord(i.key())) { - float averageTime = (float)i.value().getMovingAverage() / (float)USECS_PER_MSEC; - sortedRecords.insertMulti(averageTime, i.key()); - } - } - - int linesDisplayed = 0; - QMapIterator j(sortedRecords); - j.toBack(); - while (j.hasPrevious()) { - j.previous(); - QChar noBreakingSpace = QChar::Nbsp; - QString functionName = j.value(); - const PerformanceTimerRecord& record = allRecords.value(functionName); - - QString perfLine = QString("%1: %2 [%3]"). - arg(QString(qPrintable(functionName)), 120, noBreakingSpace). - arg((float)record.getMovingAverage() / (float)USECS_PER_MSEC, 8, 'f', 3, noBreakingSpace). - arg((int)record.getCount(), 6, 10, noBreakingSpace); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, perfLine.toUtf8().constData(), color); - linesDisplayed++; - if (onlyDisplayTopTen && linesDisplayed == 10) { - break; - } - } - } - - - */ \ No newline at end of file diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 553b54d04b..01179ad353 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -30,7 +30,7 @@ private: \ class Stats : public QQuickItem { Q_OBJECT HIFI_QML_DECL - Q_PROPERTY(bool expanded READ isExpanded NOTIFY expandedChanged) + Q_PROPERTY(bool expanded READ isExpanded WRITE setExpanded NOTIFY expandedChanged) STATS_PROPERTY(int, serverCount, 0) STATS_PROPERTY(int, framerate, 0) STATS_PROPERTY(int, avatarCount, 0) @@ -61,8 +61,16 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, translucentConsidered, 0) STATS_PROPERTY(int, translucentOutOfView, 0) STATS_PROPERTY(int, translucentTooSmall, 0) - STATS_PROPERTY(int, octreeElementsServer, 0) - STATS_PROPERTY(int, octreeElementsLocal, 0) + STATS_PROPERTY(QString, sendingMode, QString()) + STATS_PROPERTY(QString, packetStats, QString()) + STATS_PROPERTY(QString, lodStatus, QString()) + STATS_PROPERTY(QString, timingStats, QString()) + STATS_PROPERTY(int, serverElements, 0) + STATS_PROPERTY(int, serverInternal, 0) + STATS_PROPERTY(int, serverLeaves, 0) + STATS_PROPERTY(int, localElements, 0) + STATS_PROPERTY(int, localInternal, 0) + STATS_PROPERTY(int, localLeaves, 0) public: static Stats* getInstance(); @@ -76,8 +84,9 @@ public: bool isExpanded() { return _expanded; } void setExpanded(bool expanded) { - if (expanded != _expanded) { + if (_expanded != expanded) { _expanded = expanded; + emit expandedChanged(); } } @@ -113,8 +122,16 @@ signals: void translucentConsideredChanged(); void translucentOutOfViewChanged(); void translucentTooSmallChanged(); - void octreeElementsServerChanged(); - void octreeElementsLocalChanged(); + void sendingModeChanged(); + void packetStatsChanged(); + void lodStatusChanged(); + void serverElementsChanged(); + void serverInternalChanged(); + void serverLeavesChanged(); + void localElementsChanged(); + void localInternalChanged(); + void localLeavesChanged(); + void timingStatsChanged(); private: int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process diff --git a/libraries/render-utils/src/OffscreenQmlSurface.cpp b/libraries/render-utils/src/OffscreenQmlSurface.cpp index 07a887be2c..b6bc6b721e 100644 --- a/libraries/render-utils/src/OffscreenQmlSurface.cpp +++ b/libraries/render-utils/src/OffscreenQmlSurface.cpp @@ -11,6 +11,9 @@ #include #include #include + +#include + #include "AbstractViewStateInterface.h" Q_DECLARE_LOGGING_CATEGORY(offscreenFocus) @@ -212,6 +215,7 @@ QObject* OffscreenQmlSurface::finishQmlLoad(std::function Date: Wed, 17 Jun 2015 11:35:30 -0700 Subject: [PATCH 09/35] Working on overlays still, mirror rendering and overlay order --- interface/resources/qml/Stats.qml | 1 + interface/src/Application.cpp | 9 +- interface/src/ui/ApplicationOverlay.cpp | 157 ++++++++++++++---------- interface/src/ui/ApplicationOverlay.h | 3 +- interface/src/ui/Stats.cpp | 8 +- interface/src/ui/Stats.h | 4 + 6 files changed, 112 insertions(+), 70 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 89961aeb9c..c77ab8ec27 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -230,6 +230,7 @@ Hifi.Stats { Rectangle { y: 250 + visible: root.timingExpanded width: perfText.width + 8 height: perfText.height + 8 color: root.bgColor; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8e3c4e416d..058f809249 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -874,6 +874,11 @@ void Application::paintGL() { OculusManager::beginFrameTiming(); } + { + PerformanceTimer perfTimer("renderOverlay"); + _applicationOverlay.renderOverlay(&renderArgs); + } + PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::paintGL()"); @@ -974,10 +979,6 @@ void Application::paintGL() { glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); } - { - PerformanceTimer perfTimer("renderOverlay"); - _applicationOverlay.renderOverlay(&renderArgs); - } if (!OculusManager::isConnected() || OculusManager::allowSwap()) { diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index ba0cdf3be0..d3c433339b 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -87,16 +87,16 @@ ApplicationOverlay::~ApplicationOverlay() { void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); - glm::vec2 size = qApp->getCanvasSize(); - // TODO Handle fading and deactivation/activation of UI + buildFramebufferObject(); - // TODO First render the mirror to the mirror FBO + // First render the mirror to the mirror FBO + renderRearViewToFbo(renderArgs); // Execute the batch into our framebuffer - buildFramebufferObject(); _overlayFramebuffer->bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glm::vec2 size = qApp->getCanvasSize(); glViewport(0, 0, size.x, size.y); // Now render the overlay components together into a single texture @@ -104,6 +104,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { //renderAudioMeter(renderArgs); //renderCameraToggle(renderArgs); renderStatsAndLogs(renderArgs); + renderRearView(renderArgs); renderDomainConnectionStatusBorder(renderArgs); renderQmlUi(renderArgs); @@ -284,73 +285,77 @@ void ApplicationOverlay::renderAudioMeter(RenderArgs* renderArgs) { } } -void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) { - // // Grab current viewport to reset it at the end - // int viewport[4]; - // glGetIntegerv(GL_VIEWPORT, viewport); - // float aspect = (float)region.width() / region.height(); - // float fov = MIRROR_FIELD_OF_VIEW; +void fboViewport(QOpenGLFramebufferObject* fbo) { + auto size = fbo->size(); + glViewport(0, 0, size.width(), size.height()); +} - // // bool eyeRelativeCamera = false; - // if (billboard) { - // fov = BILLBOARD_FIELD_OF_VIEW; // degees - // _mirrorCamera.setPosition(_myAvatar->getPosition() + - // _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale()); +void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) { + // Grab current viewport to reset it at the end + auto mirrorSize = _mirrorFramebuffer->size(); + float aspect = (float)mirrorSize.width() / mirrorSize.height(); + float fov = MIRROR_FIELD_OF_VIEW; + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + // bool eyeRelativeCamera = false; + bool billboard = false; + if (billboard) { + fov = BILLBOARD_FIELD_OF_VIEW; // degees + _mirrorCamera.setPosition(myAvatar->getPosition() + + myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * myAvatar->getScale()); - // } else if (RearMirrorTools::rearViewZoomLevel.get() == BODY) { - // _mirrorCamera.setPosition(_myAvatar->getChestPosition() + - // _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); + } else if (RearMirrorTools::rearViewZoomLevel.get() == BODY) { + _mirrorCamera.setPosition(myAvatar->getChestPosition() + + myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * myAvatar->getScale()); - // } else { // HEAD zoom level - // // FIXME note that the positioing of the camera relative to the avatar can suffer limited - // // precision as the user's position moves further away from the origin. Thus at - // // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways - // // wildly as you rotate your avatar because the floating point values are becoming - // // larger, squeezing out the available digits of precision you have available at the - // // human scale for camera positioning. + } else { // HEAD zoom level + // FIXME note that the positioing of the camera relative to the avatar can suffer limited + // precision as the user's position moves further away from the origin. Thus at + // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways + // wildly as you rotate your avatar because the floating point values are becoming + // larger, squeezing out the available digits of precision you have available at the + // human scale for camera positioning. - // // Previously there was a hack to correct this using the mechanism of repositioning - // // the avatar at the origin of the world for the purposes of rendering the mirror, - // // but it resulted in failing to render the avatar's head model in the mirror view - // // when in first person mode. Presumably this was because of some missed culling logic - // // that was not accounted for in the hack. + // Previously there was a hack to correct this using the mechanism of repositioning + // the avatar at the origin of the world for the purposes of rendering the mirror, + // but it resulted in failing to render the avatar's head model in the mirror view + // when in first person mode. Presumably this was because of some missed culling logic + // that was not accounted for in the hack. - // // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further - // // investigated in order to adapt the technique while fixing the head rendering issue, - // // but the complexity of the hack suggests that a better approach - // _mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() + - // _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); - // } - // _mirrorCamera.setProjection(glm::perspective(glm::radians(fov), aspect, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); - // _mirrorCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); + // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further + // investigated in order to adapt the technique while fixing the head rendering issue, + // but the complexity of the hack suggests that a better approach + _mirrorCamera.setPosition(myAvatar->getHead()->getEyePosition() + + myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * myAvatar->getScale()); + } + _mirrorCamera.setProjection(glm::perspective(glm::radians(fov), aspect, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); + _mirrorCamera.setRotation(myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); - // // set the bounds of rear mirror view - // if (billboard) { - // QSize size = DependencyManager::get()->getFrameBufferSize(); - // glViewport(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); - // glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); - // } else { - // // if not rendering the billboard, the region is in device independent coordinates; must convert to device - // QSize size = DependencyManager::get()->getFrameBufferSize(); - // float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale(); - // int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio; - // glViewport(x, size.height() - y - height, width, height); - // glScissor(x, size.height() - y - height, width, height); - // } - // bool updateViewFrustum = false; - // updateProjectionMatrix(_mirrorCamera, updateViewFrustum); - // glEnable(GL_SCISSOR_TEST); - // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // set the bounds of rear mirror view + if (billboard) { + //QSize size = DependencyManager::get()->getFrameBufferSize(); + //glViewport(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); + //glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); + } else { + auto mirrorSize = _mirrorFramebuffer->size(); + fboViewport(_mirrorFramebuffer); + } - // // render rear mirror view - // glMatrixMode(GL_MODELVIEW); - // glPushMatrix(); - // glLoadIdentity(); - // glLoadMatrixf(glm::value_ptr(glm::mat4_cast(_mirrorCamera.getOrientation()) * glm::translate(glm::mat4(), _mirrorCamera.getPosition()))); - // renderArgs->_context->syncCache(); - // displaySide(renderArgs, _mirrorCamera, true, billboard); - // glMatrixMode(GL_MODELVIEW); - // glPopMatrix(); + _mirrorFramebuffer->bind(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + { + auto projection = _mirrorCamera.getProjection(); + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(glm::value_ptr(projection)); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glLoadMatrixf(glm::value_ptr(glm::mat4_cast(_mirrorCamera.getOrientation()) * glm::translate(glm::mat4(), _mirrorCamera.getPosition()))); + renderArgs->_context->syncCache(); + qApp->displaySide(renderArgs, _mirrorCamera, true, billboard); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + } + _mirrorFramebuffer->release(); // if (!billboard) { // _rearMirrorTools->render(renderArgs, false, _glWidget->mapFromGlobal(QCursor::pos())); @@ -363,6 +368,30 @@ void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) { //} } +void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) { + if (_mirrorFramebuffer && _mirrorFramebuffer->texture()) { + gpu::Batch batch; + auto geometryCache = DependencyManager::get(); + geometryCache->useSimpleDrawPipeline(batch); + batch._glDisable(GL_DEPTH_TEST); + batch._glDisable(GL_LIGHTING); + batch._glEnable(GL_BLEND); + batch.setProjectionTransform(mat4()); + batch.setModelTransform(mat4()); + auto textureCache = DependencyManager::get(); + batch.setUniformTexture(0, textureCache->getBlueTexture()); + geometryCache->renderUnitQuad(batch, glm::vec4(1)); + batch._glBindTexture(GL_TEXTURE_2D, _mirrorFramebuffer->texture()); + geometryCache->renderUnitQuad(batch, glm::vec4(1)); + renderArgs->_context->syncCache(); + auto overlaySize = _overlayFramebuffer->size(); + glViewport(MIRROR_VIEW_LEFT_PADDING, overlaySize.height() - MIRROR_VIEW_TOP_PADDING - MIRROR_VIEW_HEIGHT, + MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT); + renderArgs->_context->render(batch); + fboViewport(_overlayFramebuffer); + } +} + void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) { // Display stats and log text onscreen diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index efdec01fb5..ab242e592b 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -33,6 +33,7 @@ private: void renderCameraToggle(RenderArgs* renderArgs); void renderStatsAndLogs(RenderArgs* renderArgs); void renderDomainConnectionStatusBorder(RenderArgs* renderArgs); + void renderRearViewToFbo(RenderArgs* renderArgs); void renderRearView(RenderArgs* renderArgs); void renderQmlUi(RenderArgs* renderArgs); void renderOverlays(RenderArgs* renderArgs); @@ -52,7 +53,7 @@ private: float _rotateMirror{ 0.0f }; float _raiseMirror{ 0.0f }; - + Camera _mirrorCamera; ivec2 _previousBorderSize{ -1 }; QRect _mirrorViewRect; QOpenGLFramebufferObject* _overlayFramebuffer{ nullptr }; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index ca91e3a919..f541357486 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -296,6 +296,10 @@ void Stats::updateStats() { bool performanceTimerIsActive = PerformanceTimer::isActive(); bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails); if (displayPerf && performanceTimerIsActive) { + if (!_timingExpanded) { + _timingExpanded = true; + emit timingExpandedChanged(); + } PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up // we will also include room for 1 line per timing record and a header of 4 lines @@ -336,8 +340,10 @@ void Stats::updateStats() { } _timingStats = perfLines; emit timingStatsChanged(); + } else if (_timingExpanded) { + _timingExpanded = false; + emit timingExpandedChanged(); } - } void Stats::setRenderDetails(const RenderDetails& details) { diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 01179ad353..efe340ed18 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -31,6 +31,7 @@ class Stats : public QQuickItem { Q_OBJECT HIFI_QML_DECL Q_PROPERTY(bool expanded READ isExpanded WRITE setExpanded NOTIFY expandedChanged) + Q_PROPERTY(bool timingExpanded READ isTimingExpanded NOTIFY timingExpandedChanged) STATS_PROPERTY(int, serverCount, 0) STATS_PROPERTY(int, framerate, 0) STATS_PROPERTY(int, avatarCount, 0) @@ -82,6 +83,7 @@ public: void updateStats(); bool isExpanded() { return _expanded; } + bool isTimingExpanded() { return _timingExpanded; } void setExpanded(bool expanded) { if (_expanded != expanded) { @@ -92,6 +94,7 @@ public: signals: void expandedChanged(); + void timingExpandedChanged(); void serverCountChanged(); void framerateChanged(); void avatarCountChanged(); @@ -137,6 +140,7 @@ private: int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process bool _resetRecentMaxPacketsSoon{ true }; bool _expanded{ false }; + bool _timingExpanded{ false }; }; #endif // hifi_Stats_h From 4282c044ef4d4e0b82b84a33c0391003214f214a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 17 Jun 2015 12:01:39 -0700 Subject: [PATCH 10/35] Working on mirror and overlay --- interface/src/Application.cpp | 79 +------------------------ interface/src/ui/ApplicationOverlay.cpp | 37 ++++++------ libraries/gpu/src/gpu/Context.cpp | 4 ++ libraries/gpu/src/gpu/Context.h | 4 ++ libraries/gpu/src/gpu/GLBackend.cpp | 4 +- libraries/gpu/src/gpu/GLBackend.h | 2 + 6 files changed, 34 insertions(+), 96 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 058f809249..994a09d65f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -877,6 +877,7 @@ void Application::paintGL() { { PerformanceTimer perfTimer("renderOverlay"); _applicationOverlay.renderOverlay(&renderArgs); + renderArgs._context->resetState(); } PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); @@ -3640,84 +3641,6 @@ glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { return screenPoint; } -//void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool billboard) { -// // Grab current viewport to reset it at the end -// int viewport[4]; -// glGetIntegerv(GL_VIEWPORT, viewport); -// float aspect = (float)region.width() / region.height(); -// float fov = MIRROR_FIELD_OF_VIEW; -// -// // bool eyeRelativeCamera = false; -// if (billboard) { -// fov = BILLBOARD_FIELD_OF_VIEW; // degees -// _mirrorCamera.setPosition(_myAvatar->getPosition() + -// _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale()); -// -// } else if (RearMirrorTools::rearViewZoomLevel.get() == BODY) { -// _mirrorCamera.setPosition(_myAvatar->getChestPosition() + -// _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); -// -// } else { // HEAD zoom level -// // FIXME note that the positioing of the camera relative to the avatar can suffer limited -// // precision as the user's position moves further away from the origin. Thus at -// // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways -// // wildly as you rotate your avatar because the floating point values are becoming -// // larger, squeezing out the available digits of precision you have available at the -// // human scale for camera positioning. -// -// // Previously there was a hack to correct this using the mechanism of repositioning -// // the avatar at the origin of the world for the purposes of rendering the mirror, -// // but it resulted in failing to render the avatar's head model in the mirror view -// // when in first person mode. Presumably this was because of some missed culling logic -// // that was not accounted for in the hack. -// -// // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further -// // investigated in order to adapt the technique while fixing the head rendering issue, -// // but the complexity of the hack suggests that a better approach -// _mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() + -// _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); -// } -// _mirrorCamera.setProjection(glm::perspective(glm::radians(fov), aspect, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); -// _mirrorCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); -// -// // set the bounds of rear mirror view -// if (billboard) { -// QSize size = DependencyManager::get()->getFrameBufferSize(); -// glViewport(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); -// glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); -// } else { -// // if not rendering the billboard, the region is in device independent coordinates; must convert to device -// QSize size = DependencyManager::get()->getFrameBufferSize(); -// float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale(); -// int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio; -// glViewport(x, size.height() - y - height, width, height); -// glScissor(x, size.height() - y - height, width, height); -// } -// bool updateViewFrustum = false; -// updateProjectionMatrix(_mirrorCamera, updateViewFrustum); -// glEnable(GL_SCISSOR_TEST); -// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -// -// // render rear mirror view -// glMatrixMode(GL_MODELVIEW); -// glPushMatrix(); -// glLoadIdentity(); -// glLoadMatrixf(glm::value_ptr(glm::mat4_cast(_mirrorCamera.getOrientation()) * glm::translate(glm::mat4(), _mirrorCamera.getPosition()))); -// renderArgs->_context->syncCache(); -// displaySide(renderArgs, _mirrorCamera, true, billboard); -// glMatrixMode(GL_MODELVIEW); -// glPopMatrix(); -// -// if (!billboard) { -// _rearMirrorTools->render(renderArgs, false, _glWidget->mapFromGlobal(QCursor::pos())); -// } -// -// // reset Viewport and projection matrix -// glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); -// glDisable(GL_SCISSOR_TEST); -// updateProjectionMatrix(_myCamera, updateViewFrustum); -//} - void Application::resetSensors() { DependencyManager::get()->reset(); DependencyManager::get()->reset(); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index d3c433339b..a35cdce58b 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -53,6 +53,13 @@ static const float MIRROR_FIELD_OF_VIEW = 30.0f; static const float ORTHO_NEAR_CLIP = -10000; static const float ORTHO_FAR_CLIP = 10000; +// TODO move somewhere useful +static void fboViewport(QOpenGLFramebufferObject* fbo) { + auto size = fbo->size(); + glViewport(0, 0, size.width(), size.height()); +} + + ApplicationOverlay::ApplicationOverlay() : _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)) @@ -94,10 +101,8 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { // Execute the batch into our framebuffer _overlayFramebuffer->bind(); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glm::vec2 size = qApp->getCanvasSize(); - glViewport(0, 0, size.x, size.y); + fboViewport(_overlayFramebuffer); // Now render the overlay components together into a single texture //renderOverlays(renderArgs); @@ -285,11 +290,6 @@ void ApplicationOverlay::renderAudioMeter(RenderArgs* renderArgs) { } } -void fboViewport(QOpenGLFramebufferObject* fbo) { - auto size = fbo->size(); - glViewport(0, 0, size.width(), size.height()); -} - void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) { // Grab current viewport to reset it at the end auto mirrorSize = _mirrorFramebuffer->size(); @@ -342,19 +342,22 @@ void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) { _mirrorFramebuffer->bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glLoadMatrixf(glm::value_ptr(_mirrorCamera.getProjection())); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glLoadMatrixf(glm::value_ptr(glm::mat4_cast(_mirrorCamera.getOrientation()) * glm::translate(glm::mat4(), _mirrorCamera.getPosition()))); { - auto projection = _mirrorCamera.getProjection(); - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(glm::value_ptr(projection)); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glLoadMatrixf(glm::value_ptr(glm::mat4_cast(_mirrorCamera.getOrientation()) * glm::translate(glm::mat4(), _mirrorCamera.getPosition()))); renderArgs->_context->syncCache(); qApp->displaySide(renderArgs, _mirrorCamera, true, billboard); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); } + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); _mirrorFramebuffer->release(); // if (!billboard) { diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 9cc6bb3cd7..48bb94a89c 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -38,4 +38,8 @@ void Context::render(Batch& batch) { void Context::syncCache() { _backend->syncCache(); +} + +void Context::resetState() { + _backend->resetState(); } \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 98ddc7fb64..db88040aa9 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -28,6 +28,7 @@ public: virtual~ Backend() {}; virtual void render(Batch& batch) = 0; virtual void syncCache() = 0; + virtual void resetState() = 0; class TransformObject { public: @@ -112,6 +113,9 @@ public: void syncCache(); + // TODO remove, make all apps set all the state they need + void resetState(); + protected: Context(const Context& context); diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 20a6b60901..b935796ec6 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -656,4 +656,6 @@ void GLBackend::fetchMatrix(GLenum target, glm::mat4 & m) { glGetFloatv(target, glm::value_ptr(m)); } - +void GLBackend::resetState() { + resetPipelineState(0); +} diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index c87394869c..10fffdf97b 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -36,6 +36,8 @@ public: // Let's try to avoid to do that as much as possible! virtual void syncCache(); + virtual void resetState(); + // Render Batch create a local Context and execute the batch with it // WARNING: // if syncCache is true, then the gpu::GLBackend will synchornize From 11fff7c6e503866d803e78c917ffb53afe1351f2 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Wed, 17 Jun 2015 13:49:42 -0700 Subject: [PATCH 11/35] debugging stack on OSX --- interface/src/Application.cpp | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 994a09d65f..f1ba413fed 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -821,6 +821,23 @@ void Application::initializeGL() { InfoView::show(INFO_HELP_PATH, true); } +template +void with_stable_stack_check(F f) { + + GLint mvDepth, prDepth; + glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &mvDepth); + glGetIntegerv(GL_PROJECTION_STACK_DEPTH, &prDepth); + + f(); + + GLint mvDepthFinal, prDepthFinal; + glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &mvDepthFinal); + glGetIntegerv(GL_PROJECTION_STACK_DEPTH, &prDepthFinal); + Q_ASSERT(mvDepth == mvDepthFinal); + Q_ASSERT(prDepth == prDepthFinal); + +} + void Application::initializeUi() { AddressBarDialog::registerType(); ErrorDialog::registerType(); @@ -955,12 +972,14 @@ void Application::paintGL() { QSize size = DependencyManager::get()->getFrameBufferSize(); glViewport(0, 0, size.width(), size.height()); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - displaySide(&renderArgs, _myCamera); - _compositor.displayOverlayTexture(&renderArgs); - glPopMatrix(); + with_stable_stack_check([&]{ + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + displaySide(&renderArgs, _myCamera); + _compositor.displayOverlayTexture(&renderArgs); + glPopMatrix(); + }); //renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; //if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { From 8a1b51907138d8c09cac0f0182c20dc4c81b692a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 17 Jun 2015 16:06:06 -0700 Subject: [PATCH 12/35] Working on overlays --- interface/src/Application.cpp | 7 +++-- interface/src/devices/OculusManager.cpp | 2 +- interface/src/ui/ApplicationCompositor.cpp | 31 +++++++++++++++------- interface/src/ui/ApplicationOverlay.cpp | 1 - 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ed417ca458..b0fd376586 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -151,6 +151,7 @@ #include "ui/AddressBarDialog.h" #include "ui/UpdateDialog.h" +static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU #if defined(Q_OS_WIN) @@ -932,13 +933,15 @@ void Application::paintGL() { } } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { -#if 0 + // TODO put the mirror modifiers somewhere both the app and the overlay can access it + float _rotateMirror = 0.0f; + float _raiseMirror = 0.0f; + float _scaleMirror = 1.0f; _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0) + (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); -#endif } // Update camera position diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 98597c4a05..53bacfacbf 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -610,7 +610,7 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h); renderArgs->_renderSide = RenderArgs::MONO; - qApp->displaySide(renderArgs, *_camera, false); + qApp->displaySide(renderArgs, *_camera); //qApp->getApplicationCompositor().displayOverlayTexture(renderArgs); qApp->getApplicationCompositor().displayOverlayTextureHmd(renderArgs, eye); }); diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index b0c21cf77d..d78d79b1ab 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -191,6 +191,11 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) { return; } + GLuint texture = qApp->getApplicationOverlay().getOverlayTexture(); + if (!texture) { + return; + } + updateTooltips(); vec2 canvasSize = qApp->getCanvasSize(); @@ -198,10 +203,6 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) { //Handle fading and deactivation/activation of UI gpu::Batch batch; - GLuint texture = qApp->getApplicationOverlay().getOverlayTexture(); - if (!texture) { - return; - } renderArgs->_context->syncCache(); auto geometryCache = DependencyManager::get(); @@ -262,28 +263,38 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int return; } + updateTooltips(); + vec2 canvasSize = qApp->getCanvasSize(); _textureAspectRatio = aspect(canvasSize); - updateTooltips(); - renderArgs->_context->syncCache(); auto geometryCache = DependencyManager::get(); gpu::Batch batch; - DependencyManager::get()->useSimpleDrawPipeline(batch); + geometryCache->useSimpleDrawPipeline(batch); batch._glDisable(GL_DEPTH_TEST); batch._glDisable(GL_CULL_FACE); batch._glBindTexture(GL_TEXTURE_2D, texture); batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - batch.setViewTransform(Transform()); batch.setProjectionTransform(qApp->getEyeProjection(eye)); + mat4 eyePose = qApp->getEyePose(eye); glm::mat4 overlayXfm = glm::inverse(eyePose); - batch.setModelTransform(overlayXfm); - drawSphereSection(batch); + +#ifdef DEBUG_OVERLAY + { + batch.setModelTransform(glm::translate(mat4(), vec3(0, 0, -2))); + geometryCache->renderUnitQuad(batch, glm::vec4(1)); + } +#else + { + batch.setModelTransform(overlayXfm); + drawSphereSection(batch); + } +#endif // Doesn't actually render renderPointers(batch); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index a35cdce58b..46264730c2 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -46,7 +46,6 @@ static const int MIRROR_VIEW_LEFT_PADDING = 10; static const int MIRROR_VIEW_WIDTH = 265; static const int MIRROR_VIEW_HEIGHT = 215; static const int STATS_HORIZONTAL_OFFSET = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; -static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; static const float MIRROR_REARVIEW_DISTANCE = 0.722f; static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f; static const float MIRROR_FIELD_OF_VIEW = 30.0f; From 1705eb8f80bfdc5902d4c353552307ecd44b9a49 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Wed, 17 Jun 2015 14:49:44 -0700 Subject: [PATCH 13/35] Reverting some changes, putting overlay rendering in its own state manager --- interface/src/Application.cpp | 24 +++++++++----------- libraries/gpu/src/gpu/Context.cpp | 4 ---- libraries/gpu/src/gpu/Context.h | 4 ---- libraries/gpu/src/gpu/GLBackend.cpp | 4 +--- libraries/gpu/src/gpu/GLBackend.h | 2 -- libraries/render-utils/src/GeometryCache.cpp | 1 - 6 files changed, 12 insertions(+), 27 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b0fd376586..f6d4c4afd7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -880,6 +880,16 @@ void Application::paintGL() { _glWidget->makeCurrent(); auto lodManager = DependencyManager::get(); + + { + PerformanceTimer perfTimer("renderOverlay"); + gpu::Context context(new gpu::GLBackend()); + RenderArgs renderArgs(&context, nullptr, getViewFrustum(), lodManager->getOctreeSizeScale(), + lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, + RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); + _applicationOverlay.renderOverlay(&renderArgs); + } + gpu::Context context(new gpu::GLBackend()); RenderArgs renderArgs(&context, nullptr, getViewFrustum(), lodManager->getOctreeSizeScale(), lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, @@ -891,12 +901,7 @@ void Application::paintGL() { OculusManager::beginFrameTiming(); } - { - PerformanceTimer perfTimer("renderOverlay"); - _applicationOverlay.renderOverlay(&renderArgs); - renderArgs._context->resetState(); - } - + PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::paintGL()"); @@ -1004,7 +1009,6 @@ void Application::paintGL() { } - if (!OculusManager::isConnected() || OculusManager::allowSwap()) { _glWidget->swapBuffers(); } @@ -1079,12 +1083,6 @@ void Application::resizeGL() { offscreenUi->resize(_glWidget->size()); _glWidget->makeCurrent(); -#if 0 - // update Stats width - // let's set horizontal offset to give stats some margin to mirror - int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; - Stats::getInstance()->resetWidth(_renderResolution.x, horizontalOffset); -#endif } void Application::updateProjectionMatrix() { diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 48bb94a89c..9cc6bb3cd7 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -38,8 +38,4 @@ void Context::render(Batch& batch) { void Context::syncCache() { _backend->syncCache(); -} - -void Context::resetState() { - _backend->resetState(); } \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index db88040aa9..98ddc7fb64 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -28,7 +28,6 @@ public: virtual~ Backend() {}; virtual void render(Batch& batch) = 0; virtual void syncCache() = 0; - virtual void resetState() = 0; class TransformObject { public: @@ -113,9 +112,6 @@ public: void syncCache(); - // TODO remove, make all apps set all the state they need - void resetState(); - protected: Context(const Context& context); diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index b935796ec6..20a6b60901 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -656,6 +656,4 @@ void GLBackend::fetchMatrix(GLenum target, glm::mat4 & m) { glGetFloatv(target, glm::value_ptr(m)); } -void GLBackend::resetState() { - resetPipelineState(0); -} + diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 10fffdf97b..c87394869c 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -36,8 +36,6 @@ public: // Let's try to avoid to do that as much as possible! virtual void syncCache(); - virtual void resetState(); - // Render Batch create a local Context and execute the batch with it // WARNING: // if syncCache is true, then the gpu::GLBackend will synchornize diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 98067e1b81..7a2bbc81f4 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -2405,4 +2405,3 @@ int NetworkMesh::getTranslucentPartCount(const FBXMesh& fbxMesh) const { } return count; } - From a8d3dc721880f9d58f5620174b4a666b8096e438 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 17 Jun 2015 23:50:38 -0700 Subject: [PATCH 14/35] Updating oglplus version --- cmake/externals/oglplus/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/externals/oglplus/CMakeLists.txt b/cmake/externals/oglplus/CMakeLists.txt index 23ba0d515e..6556f5a61b 100644 --- a/cmake/externals/oglplus/CMakeLists.txt +++ b/cmake/externals/oglplus/CMakeLists.txt @@ -4,8 +4,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - GIT_REPOSITORY https://github.com/jherico/oglplus.git - GIT_TAG 470d8e56fd6bf3913ceec03d82f42d3bafab2cbe + GIT_REPOSITORY https://github.com/matus-chochlik/oglplus.git + GIT_TAG a2681383928b1166f176512cbe0f95e96fe68d08 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" From 84c3dab3d8919135b69e8cbf0f63228aec7cc3c9 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 18 Jun 2015 00:23:40 -0700 Subject: [PATCH 15/35] Fixing error in stats qml parsing --- interface/resources/qml/Stats.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index c77ab8ec27..0a05bc18a1 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -234,7 +234,6 @@ Hifi.Stats { width: perfText.width + 8 height: perfText.height + 8 color: root.bgColor; - anchors.top: row.bottom + 16 Text { x: 4; y: 4 id: perfText From acdf7c48d9b588771163b7971ceb2e6f2f63c31f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 18 Jun 2015 00:53:19 -0700 Subject: [PATCH 16/35] Adding more explicit error checking --- interface/src/ui/ApplicationOverlay.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 46264730c2..5ca03d8f0a 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -89,8 +90,10 @@ ApplicationOverlay::ApplicationOverlay() : ApplicationOverlay::~ApplicationOverlay() { } + // Renders the overlays either to a texture or to the screen void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { + CHECK_GL_ERROR(); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); buildFramebufferObject(); @@ -102,18 +105,24 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { _overlayFramebuffer->bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); fboViewport(_overlayFramebuffer); + CHECK_GL_ERROR(); // Now render the overlay components together into a single texture //renderOverlays(renderArgs); //renderAudioMeter(renderArgs); //renderCameraToggle(renderArgs); renderStatsAndLogs(renderArgs); + CHECK_GL_ERROR(); renderRearView(renderArgs); + CHECK_GL_ERROR(); renderDomainConnectionStatusBorder(renderArgs); + CHECK_GL_ERROR(); renderQmlUi(renderArgs); + CHECK_GL_ERROR(); _overlayFramebuffer->release(); + CHECK_GL_ERROR(); } void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { @@ -446,7 +455,7 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderAr gpu::Batch batch; auto geometryCache = DependencyManager::get(); geometryCache->useSimpleDrawPipeline(batch); - batch._glDisable(GL_DEPTH); + batch._glDisable(GL_DEPTH_TEST); batch._glDisable(GL_LIGHTING); batch._glEnable(GL_BLEND); batch.setProjectionTransform(mat4()); From dfea571e629dac633c10d7031fccb7dca008ea18 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 18 Jun 2015 00:54:35 -0700 Subject: [PATCH 17/35] Removing a 'using namespace' from a header and tweaking error checking code --- libraries/gpu/src/gpu/GLBackend.cpp | 10 ++++++++++ libraries/gpu/src/gpu/GLBackend.h | 3 +++ libraries/gpu/src/gpu/GLBackendOutput.cpp | 1 + libraries/gpu/src/gpu/GLBackendShared.h | 14 +++++--------- libraries/gpu/src/gpu/GLBackendTexture.cpp | 1 + 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 20a6b60901..1e61c0e6aa 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -12,6 +12,8 @@ #include "GLBackendShared.h" #include +using namespace gpu; + GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = { (&::gpu::GLBackend::do_draw), @@ -141,6 +143,14 @@ bool GLBackend::checkGLError(const char* name) { } } +bool GLBackend::checkGLErrorDebug(const char* name) { +#ifdef DEBUG + checkGLError(name); +#else + Q_UNUSED(name); + return false; +#endif +} void GLBackend::syncCache() { syncTransformStateCache(); diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index c87394869c..394c394a3a 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -47,6 +47,9 @@ public: static bool checkGLError(const char* name = nullptr); + // Only checks in debug builds + static bool checkGLErrorDebug(const char* name = nullptr); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp index b0dee550d9..903c97f45b 100755 --- a/libraries/gpu/src/gpu/GLBackendOutput.cpp +++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp @@ -11,6 +11,7 @@ #include "GPULogging.h" #include "GLBackendShared.h" +using namespace gpu; GLBackend::GLFramebuffer::GLFramebuffer() {} diff --git a/libraries/gpu/src/gpu/GLBackendShared.h b/libraries/gpu/src/gpu/GLBackendShared.h index 92e06e1d11..9f5c93278b 100644 --- a/libraries/gpu/src/gpu/GLBackendShared.h +++ b/libraries/gpu/src/gpu/GLBackendShared.h @@ -17,9 +17,7 @@ #include "Batch.h" -using namespace gpu; - -static const GLenum _primitiveToGLmode[NUM_PRIMITIVES] = { +static const GLenum _primitiveToGLmode[gpu::NUM_PRIMITIVES] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, @@ -30,7 +28,7 @@ static const GLenum _primitiveToGLmode[NUM_PRIMITIVES] = { GL_QUAD_STRIP, }; -static const GLenum _elementTypeToGLType[NUM_TYPES]= { +static const GLenum _elementTypeToGLType[gpu::NUM_TYPES] = { GL_FLOAT, GL_INT, GL_UNSIGNED_INT, @@ -49,10 +47,8 @@ static const GLenum _elementTypeToGLType[NUM_TYPES]= { GL_UNSIGNED_BYTE }; -#if _DEBUG -#define CHECK_GL_ERROR() ::gpu::GLBackend::checkGLError(__FUNCTION__) -#else -#define CHECK_GL_ERROR() false -#endif +// Stupid preprocessor trick to turn the line macro into a string +#define CHECK_GL_ERROR_HELPER(x) #x +#define CHECK_GL_ERROR() gpu::GLBackend::checkGLErrorDebug(__FUNCTION__ ":" CHECK_GL_ERROR_HELPER(__LINE__)) #endif diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp index 999173e5b7..b8f98590ff 100755 --- a/libraries/gpu/src/gpu/GLBackendTexture.cpp +++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp @@ -11,6 +11,7 @@ #include "GPULogging.h" #include "GLBackendShared.h" +using namespace gpu; GLBackend::GLTexture::GLTexture() : _storageStamp(0), From 440491700303d67a925bc48707c7cd7e926f7fb1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 18 Jun 2015 11:16:25 -0700 Subject: [PATCH 18/35] Fix HMD projection post-merge --- interface/src/devices/OculusManager.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 8032fb7065..3686143bdc 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -212,8 +212,6 @@ bool OculusManager::_eyePerFrameMode = false; ovrEyeType OculusManager::_lastEyeRendered = ovrEye_Count; ovrSizei OculusManager::_recommendedTexSize = { 0, 0 }; float OculusManager::_offscreenRenderScale = 1.0; - -static glm::mat4 _eyeProjections[ovrEye_Count]; static glm::mat4 _combinedProjection; static ovrPosef _eyeRenderPoses[ovrEye_Count]; ovrRecti OculusManager::_eyeViewports[ovrEye_Count]; @@ -266,7 +264,13 @@ void OculusManager::connect(QOpenGLContext* shareContext) { for_each_eye([&](ovrEyeType eye) { _eyeFov[eye] = _ovrHmd->DefaultEyeFov[eye]; + _eyeProjection[eye] = toGlm(ovrMatrix4f_Projection(_eyeFov[eye], + DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded)); }); + ovrFovPort combinedFov = _ovrHmd->MaxEyeFov[0]; + combinedFov.RightTan = _ovrHmd->MaxEyeFov[1].RightTan; + _combinedProjection = toGlm(ovrMatrix4f_Projection(combinedFov, + DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded)); _recommendedTexSize = ovrHmd_GetFovTextureSize(_ovrHmd, ovrEye_Left, _eyeFov[ovrEye_Left], 1.0f); _renderTargetSize = { _recommendedTexSize.w * 2, _recommendedTexSize.h }; @@ -523,18 +527,12 @@ void OculusManager::endFrameTiming() { //Sets the camera FoV and aspect ratio void OculusManager::configureCamera(Camera& camera) { - ovrFovPort fov; if (_activeEye == ovrEye_Count) { // When not rendering, provide a FOV encompasing both eyes - fov = _eyeFov[0]; - fov.RightTan = _eyeFov[1].RightTan; - } else { - // When rendering, provide the exact FOV - fov = _eyeFov[_activeEye]; - } - // Convert the FOV to the correct projection matrix - glm::mat4 projection = toGlm(ovrMatrix4f_Projection(fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded)); - camera.setProjection(projection); + camera.setProjection(_combinedProjection); + return; + } + camera.setProjection(_eyeProjection[_activeEye]); } //Displays everything for the oculus, frame timing must be active @@ -812,7 +810,7 @@ int OculusManager::getHMDScreen() { } mat4 OculusManager::getEyeProjection(int eye) { - return _eyeProjections[eye]; + return _eyeProjection[eye]; } mat4 OculusManager::getEyePose(int eye) { From 0cdee2d6ec9c092db92bddd9ae20a777e2c3ed8a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 18 Jun 2015 12:14:55 -0700 Subject: [PATCH 19/35] preprocessor error on Linux/Mac --- libraries/gpu/src/gpu/GLBackendShared.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/GLBackendShared.h b/libraries/gpu/src/gpu/GLBackendShared.h index 9f5c93278b..27f58fcbe3 100644 --- a/libraries/gpu/src/gpu/GLBackendShared.h +++ b/libraries/gpu/src/gpu/GLBackendShared.h @@ -49,6 +49,8 @@ static const GLenum _elementTypeToGLType[gpu::NUM_TYPES] = { // Stupid preprocessor trick to turn the line macro into a string #define CHECK_GL_ERROR_HELPER(x) #x -#define CHECK_GL_ERROR() gpu::GLBackend::checkGLErrorDebug(__FUNCTION__ ":" CHECK_GL_ERROR_HELPER(__LINE__)) +// FIXME doesn't build on Linux or Mac. Hmmmm +// #define CHECK_GL_ERROR() gpu::GLBackend::checkGLErrorDebug(__FUNCTION__ ":" CHECK_GL_ERROR_HELPER(__LINE__)) +#define CHECK_GL_ERROR() gpu::GLBackend::checkGLErrorDebug(__FUNCTION__) #endif From 1243c547671a9fa1ae5664fa1b75ffb56dc7a424 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 18 Jun 2015 12:44:57 -0700 Subject: [PATCH 20/35] More merge bugs --- interface/src/devices/OculusManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 3686143bdc..84469f8b29 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -709,7 +709,7 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const }); // restore our normal viewport - ovrHmd_EndFrame(_ovrHmd, eyeRenderPose, _eyeTextures); + ovrHmd_EndFrame(_ovrHmd, _eyeRenderPoses, _eyeTextures); glCanvas->makeCurrent(); #endif From 7bbaf026f3d5ded6b039545bc0d2ae5649e2e574 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Thu, 18 Jun 2015 13:33:37 -0700 Subject: [PATCH 21/35] use a monospace font on all platforms --- interface/resources/qml/Stats.qml | 4 ++-- interface/src/ui/Stats.cpp | 3 +++ interface/src/ui/Stats.h | 7 ++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index c77ab8ec27..e52c07dda3 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -239,8 +239,8 @@ Hifi.Stats { x: 4; y: 4 id: perfText color: root.fontColor - font.family: "Lucida Console" - text: "-------------------------------------------------------- Function " + + font.family: root.monospaceFont + text: "-------------------------------------------------------- Function " + "------------------------------------------------------- --msecs- -calls--\n" + root.timingStats; } diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index f541357486..c1d77b5fc2 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -14,6 +14,7 @@ #include "Stats.h" #include +#include #include #include @@ -50,6 +51,8 @@ Stats* Stats::getInstance() { Stats::Stats(QQuickItem* parent) : QQuickItem(parent) { INSTANCE = this; + const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); + _monospaceFont = font.family(); } bool Stats::includeTimingRecord(const QString& name) { diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index efe340ed18..58d920d9a5 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -32,6 +32,8 @@ class Stats : public QQuickItem { HIFI_QML_DECL Q_PROPERTY(bool expanded READ isExpanded WRITE setExpanded NOTIFY expandedChanged) Q_PROPERTY(bool timingExpanded READ isTimingExpanded NOTIFY timingExpandedChanged) + Q_PROPERTY(QString monospaceFont READ monospaceFont) + STATS_PROPERTY(int, serverCount, 0) STATS_PROPERTY(int, framerate, 0) STATS_PROPERTY(int, avatarCount, 0) @@ -79,7 +81,9 @@ public: Stats(QQuickItem* parent = nullptr); bool includeTimingRecord(const QString& name); void setRenderDetails(const RenderDetails& details); - + const QString& monospaceFont() { + return _monospaceFont; + } void updateStats(); bool isExpanded() { return _expanded; } @@ -141,6 +145,7 @@ private: bool _resetRecentMaxPacketsSoon{ true }; bool _expanded{ false }; bool _timingExpanded{ false }; + QString _monospaceFont; }; #endif // hifi_Stats_h From 100f7acddc89e8e003c9dfc017b2a33692ca8bc6 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Thu, 18 Jun 2015 13:46:27 -0700 Subject: [PATCH 22/35] Working on tooltip placement --- interface/resources/qml/Tooltip.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/Tooltip.qml b/interface/resources/qml/Tooltip.qml index f1504317e2..0ad3efc5a5 100644 --- a/interface/resources/qml/Tooltip.qml +++ b/interface/resources/qml/Tooltip.qml @@ -6,8 +6,9 @@ import "styles" Hifi.Tooltip { id: root HifiConstants { id: hifi } - x: lastMousePosition.x - y: lastMousePosition.y + // FIXME adjust position based on the edges of the screen + x: lastMousePosition.x + 20 + y: lastMousePosition.y + 5 implicitWidth: border.implicitWidth implicitHeight: border.implicitHeight From 1bae1d6065ef96b67bfe07393c16257da2c476f7 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Thu, 18 Jun 2015 13:59:48 -0700 Subject: [PATCH 23/35] Changing font size for timings --- interface/resources/qml/Stats.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index e52c07dda3..eb1cb1d337 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -240,6 +240,7 @@ Hifi.Stats { id: perfText color: root.fontColor font.family: root.monospaceFont + font.pixelSize: 10 text: "-------------------------------------------------------- Function " + "------------------------------------------------------- --msecs- -calls--\n" + root.timingStats; From 96ac5070f67d1a3ac8a4f6d2eb9c8e6ad1a965c8 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Thu, 18 Jun 2015 14:00:17 -0700 Subject: [PATCH 24/35] Adding some more protection to the viewport --- interface/src/ui/ApplicationOverlay.cpp | 51 +++++++++---------------- 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 46264730c2..e278820195 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -92,11 +92,14 @@ ApplicationOverlay::~ApplicationOverlay() { // Renders the overlays either to a texture or to the screen void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); + + // TODO move to Application::idle()? + Stats::getInstance()->updateStats(); buildFramebufferObject(); // First render the mirror to the mirror FBO - renderRearViewToFbo(renderArgs); + // renderRearViewToFbo(renderArgs); // Execute the batch into our framebuffer _overlayFramebuffer->bind(); @@ -104,12 +107,15 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { fboViewport(_overlayFramebuffer); // Now render the overlay components together into a single texture - //renderOverlays(renderArgs); + renderOverlays(renderArgs); + fboViewport(_overlayFramebuffer); //renderAudioMeter(renderArgs); //renderCameraToggle(renderArgs); + fboViewport(_overlayFramebuffer); renderStatsAndLogs(renderArgs); - renderRearView(renderArgs); - + fboViewport(_overlayFramebuffer); + // renderRearView(renderArgs); + // fboViewport(_overlayFramebuffer); renderDomainConnectionStatusBorder(renderArgs); renderQmlUi(renderArgs); @@ -153,6 +159,7 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); + fboViewport(_overlayFramebuffer); } void ApplicationOverlay::renderCameraToggle(RenderArgs* renderArgs) { @@ -171,6 +178,7 @@ void ApplicationOverlay::renderCameraToggle(RenderArgs* renderArgs) { } DependencyManager::get()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, boxed); + fboViewport(_overlayFramebuffer); } void ApplicationOverlay::renderAudioMeter(RenderArgs* renderArgs) { @@ -287,6 +295,7 @@ void ApplicationOverlay::renderAudioMeter(RenderArgs* renderArgs) { audioLevel, AUDIO_METER_HEIGHT, quadColor, _audioBlueQuad); } + fboViewport(_overlayFramebuffer); } void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) { @@ -296,16 +305,9 @@ void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) { float fov = MIRROR_FIELD_OF_VIEW; MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); // bool eyeRelativeCamera = false; - bool billboard = false; - if (billboard) { - fov = BILLBOARD_FIELD_OF_VIEW; // degees - _mirrorCamera.setPosition(myAvatar->getPosition() + - myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * myAvatar->getScale()); - - } else if (RearMirrorTools::rearViewZoomLevel.get() == BODY) { + if (RearMirrorTools::rearViewZoomLevel.get() == BODY) { _mirrorCamera.setPosition(myAvatar->getChestPosition() + myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * myAvatar->getScale()); - } else { // HEAD zoom level // FIXME note that the positioing of the camera relative to the avatar can suffer limited // precision as the user's position moves further away from the origin. Thus at @@ -330,14 +332,7 @@ void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) { _mirrorCamera.setRotation(myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); // set the bounds of rear mirror view - if (billboard) { - //QSize size = DependencyManager::get()->getFrameBufferSize(); - //glViewport(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); - //glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); - } else { - auto mirrorSize = _mirrorFramebuffer->size(); - fboViewport(_mirrorFramebuffer); - } + fboViewport(_mirrorFramebuffer); _mirrorFramebuffer->bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -351,23 +346,14 @@ void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) { glLoadMatrixf(glm::value_ptr(glm::mat4_cast(_mirrorCamera.getOrientation()) * glm::translate(glm::mat4(), _mirrorCamera.getPosition()))); { renderArgs->_context->syncCache(); - qApp->displaySide(renderArgs, _mirrorCamera, true, billboard); + qApp->displaySide(renderArgs, _mirrorCamera, true, false); } glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); _mirrorFramebuffer->release(); - - // if (!billboard) { - // _rearMirrorTools->render(renderArgs, false, _glWidget->mapFromGlobal(QCursor::pos())); - // } - - // // reset Viewport and projection matrix - // glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); - // glDisable(GL_SCISSOR_TEST); - // updateProjectionMatrix(_myCamera, updateViewFrustum); - //} + fboViewport(_overlayFramebuffer); } void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) { @@ -399,7 +385,7 @@ void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) { // Display stats and log text onscreen // Determine whether to compute timing details - Stats::getInstance()->updateStats(); + /* // Show on-screen msec timer @@ -425,6 +411,7 @@ void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) { glEnable(GL_LIGHTING); glEnable(GL_BLEND); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + fboViewport(_overlayFramebuffer); */ } From 68595ef5dc90fb2eab487ef050677c0195109f6d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 18 Jun 2015 12:47:23 -0700 Subject: [PATCH 25/35] Delete sync object after use --- interface/src/devices/OculusManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 84469f8b29..be8e18c038 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -701,6 +701,7 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const // rendering to complete before it starts the distortion rendering, // but without triggering a CPU/GPU synchronization glWaitSync(syncObject, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(syncObject); GLuint textureId = gpu::GLBackend::getTextureID(finalFbo->getRenderBuffer(0)); for_each_eye([&](ovrEyeType eye) { From edfb25bfe2290f4f1fbe66ec9b549bf7deaa891c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 18 Jun 2015 15:05:32 -0700 Subject: [PATCH 26/35] Removing debug define for VR overlay --- interface/src/ui/ApplicationCompositor.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 9ba69437e6..d78d79b1ab 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -284,7 +284,6 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int mat4 eyePose = qApp->getEyePose(eye); glm::mat4 overlayXfm = glm::inverse(eyePose); -#define DEBUG_OVERLAY #ifdef DEBUG_OVERLAY { batch.setModelTransform(glm::translate(mat4(), vec3(0, 0, -2))); From 8e5da5acaa7a20f0fed165e9bab426e8c0a1ea2c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 18 Jun 2015 17:05:49 -0700 Subject: [PATCH 27/35] Fix debug compile error --- libraries/gpu/src/gpu/GLBackend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 1e61c0e6aa..a3bc90c219 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -145,7 +145,7 @@ bool GLBackend::checkGLError(const char* name) { bool GLBackend::checkGLErrorDebug(const char* name) { #ifdef DEBUG - checkGLError(name); + return checkGLError(name); #else Q_UNUSED(name); return false; From 9bef5f011bc1da1e06aadd1d2c7f09a48fc187e8 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 19 Jun 2015 00:03:46 -0700 Subject: [PATCH 28/35] Reverting mirror functionality --- interface/resources/qml/Stats.qml | 6 +- interface/src/Application.cpp | 251 ++++++++++++++---------- interface/src/Application.h | 18 +- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/devices/OculusManager.cpp | 4 +- interface/src/ui/ApplicationOverlay.cpp | 101 +--------- interface/src/ui/ApplicationOverlay.h | 7 - interface/src/ui/Stats.cpp | 2 +- libraries/gpu/src/gpu/GLBackend.cpp | 16 ++ libraries/gpu/src/gpu/GLBackend.h | 6 +- libraries/gpu/src/gpu/GLBackendShared.h | 2 + 11 files changed, 196 insertions(+), 221 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index eb5a389c61..8ddea6a355 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -239,9 +239,9 @@ Hifi.Stats { id: perfText color: root.fontColor font.family: root.monospaceFont - font.pixelSize: 10 - text: "-------------------------------------------------------- Function " + - "------------------------------------------------------- --msecs- -calls--\n" + + font.pixelSize: 12 + text: "------------------------------------------ Function " + + "--------------------------------------- --msecs- -calls--\n" + root.timingStats; } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6b7f42fe0b..dc0eb3d23f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -151,7 +151,6 @@ #include "ui/AddressBarDialog.h" #include "ui/UpdateDialog.h" -static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU #if defined(Q_OS_WIN) @@ -325,11 +324,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _viewFrustum(), _lastQueriedViewFrustum(), _lastQueriedTime(usecTimestampNow()), + _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _firstRun("firstRun", true), _previousScriptLocation("LastScriptLocation"), _scriptsLocationHandle("scriptsLocation"), _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), _viewTransform(), + _scaleMirror(1.0f), + _rotateMirror(0.0f), + _raiseMirror(0.0f), _cursorVisible(true), _lastMouseMove(usecTimestampNow()), _lastMouseMoveWasSimulated(false), @@ -821,23 +824,6 @@ void Application::initializeGL() { InfoView::show(INFO_HELP_PATH, true); } -template -void with_stable_stack_check(F f) { - - GLint mvDepth, prDepth; - glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &mvDepth); - glGetIntegerv(GL_PROJECTION_STACK_DEPTH, &prDepth); - - f(); - - GLint mvDepthFinal, prDepthFinal; - glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &mvDepthFinal); - glGetIntegerv(GL_PROJECTION_STACK_DEPTH, &prDepthFinal); - Q_ASSERT(mvDepth == mvDepthFinal); - Q_ASSERT(prDepth == prDepthFinal); - -} - void Application::initializeUi() { AddressBarDialog::registerType(); ErrorDialog::registerType(); @@ -875,12 +861,7 @@ void Application::initializeUi() { }); } -void Application::paintGL() { - PROFILE_RANGE(__FUNCTION__); - _glWidget->makeCurrent(); - - auto lodManager = DependencyManager::get(); - +/* { PerformanceTimer perfTimer("renderOverlay"); gpu::Context context(new gpu::GLBackend()); @@ -889,6 +870,13 @@ void Application::paintGL() { RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); _applicationOverlay.renderOverlay(&renderArgs); } +*/ + +void Application::paintGL() { + PROFILE_RANGE(__FUNCTION__); + _glWidget->makeCurrent(); + + auto lodManager = DependencyManager::get(); gpu::Context context(new gpu::GLBackend()); RenderArgs renderArgs(&context, nullptr, getViewFrustum(), lodManager->getOctreeSizeScale(), @@ -943,10 +931,6 @@ void Application::paintGL() { } } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - // TODO put the mirror modifiers somewhere both the app and the overlay can access it - float _rotateMirror = 0.0f; - float _raiseMirror = 0.0f; - float _scaleMirror = 1.0f; _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0) + @@ -986,31 +970,32 @@ void Application::paintGL() { QSize size = DependencyManager::get()->getFrameBufferSize(); glViewport(0, 0, size.width(), size.height()); - with_stable_stack_check([&]{ - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - displaySide(&renderArgs, _myCamera); - _compositor.displayOverlayTexture(&renderArgs); - glPopMatrix(); - }); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + displaySide(&renderArgs, _myCamera); + glPopMatrix(); - //renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; - //if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { - // _rearMirrorTools->render(&renderArgs, true, _glWidget->mapFromGlobal(QCursor::pos())); - //} else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { - // renderRearViewMirror(&renderArgs, _mirrorViewRect); - //} - //renderArgs._renderMode = RenderArgs::NORMAL_RENDER_MODE; + renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; + if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { + _rearMirrorTools->render(&renderArgs, true, _glWidget->mapFromGlobal(QCursor::pos())); + } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { + renderRearViewMirror(&renderArgs, _mirrorViewRect); + } + + renderArgs._renderMode = RenderArgs::NORMAL_RENDER_MODE; auto finalFbo = DependencyManager::get()->render(&renderArgs); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_READ_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo)); glBlitFramebuffer(0, 0, _renderResolution.x, _renderResolution.y, 0, 0, _glWidget->getDeviceSize().width(), _glWidget->getDeviceSize().height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + _compositor.displayOverlayTexture(&renderArgs); } @@ -1071,21 +1056,24 @@ void Application::resizeGL() { } else { renderSize = _glWidget->getDeviceSize() * getRenderResolutionScale(); } - if (_renderResolution == toGlm(renderSize)) { - return; + + if (_renderResolution != toGlm(renderSize)) { + _renderResolution = toGlm(renderSize); + DependencyManager::get()->setFrameBufferSize(renderSize); + resetCamerasOnResizeGL(_myCamera, _renderResolution); + + glViewport(0, 0, _renderResolution.x, _renderResolution.y); // shouldn't this account for the menu??? + + updateProjectionMatrix(); + glLoadIdentity(); } - _renderResolution = toGlm(renderSize); - DependencyManager::get()->setFrameBufferSize(renderSize); - resetCamerasOnResizeGL(_myCamera, _renderResolution); - - glViewport(0, 0, _renderResolution.x, _renderResolution.y); // shouldn't this account for the menu??? - - updateProjectionMatrix(); - glLoadIdentity(); - auto offscreenUi = DependencyManager::get(); - offscreenUi->resize(_glWidget->size()); + + auto canvasSize = _glWidget->size(); + if (canvasSize != offscreenUi->getWindow()->size()) { + offscreenUi->resize(canvasSize); + } _glWidget->makeCurrent(); } @@ -1303,37 +1291,37 @@ void Application::keyPressEvent(QKeyEvent* event) { Menu::getInstance()->triggerOption(MenuOption::Chat); break; - //case Qt::Key_Up: - // if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - // if (!isShifted) { - // _scaleMirror *= 0.95f; - // } else { - // _raiseMirror += 0.05f; - // } - // } - // break; + case Qt::Key_Up: + if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + if (!isShifted) { + _scaleMirror *= 0.95f; + } else { + _raiseMirror += 0.05f; + } + } + break; - //case Qt::Key_Down: - // if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - // if (!isShifted) { - // _scaleMirror *= 1.05f; - // } else { - // _raiseMirror -= 0.05f; - // } - // } - // break; + case Qt::Key_Down: + if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + if (!isShifted) { + _scaleMirror *= 1.05f; + } else { + _raiseMirror -= 0.05f; + } + } + break; - //case Qt::Key_Left: - // if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - // _rotateMirror += PI / 20.0f; - // } - // break; + case Qt::Key_Left: + if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + _rotateMirror += PI / 20.0f; + } + break; - //case Qt::Key_Right: - // if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - // _rotateMirror -= PI / 20.0f; - // } - // break; + case Qt::Key_Right: + if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + _rotateMirror -= PI / 20.0f; + } + break; #if 0 case Qt::Key_I: @@ -1598,12 +1586,11 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { // stop propagation return; } -#if 0 + if (_rearMirrorTools->mousePressEvent(getMouseX(), getMouseY())) { // stop propagation return; } -#endif } // nobody handled this - make it an action event on the _window object @@ -1654,15 +1641,6 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { if (event->button() == Qt::LeftButton) { _mousePressed = false; -#if 0 - if (Menu::getInstance()->isOptionChecked(MenuOption::Stats) && mouseOnScreen()) { - // let's set horizontal offset to give stats some margin to mirror - int horizontalOffset = MIRROR_VIEW_WIDTH; - Stats::getInstance()->checkClick(getMouseX(), getMouseY(), - getMouseDragStartedX(), getMouseDragStartedY(), horizontalOffset); - } -#endif - // fire an action end event HFActionEvent actionEvent(HFActionEvent::endType(), computePickRay(event->x(), event->y())); @@ -1847,7 +1825,6 @@ void Application::idle() { targetFramePeriod = 1000.0 / targetFramerate; } double timeSinceLastUpdate = (double)_lastTimeUpdated.nsecsElapsed() / 1000000.0; - //if (true) { if (timeSinceLastUpdate > targetFramePeriod) { _lastTimeUpdated.start(); { @@ -1874,7 +1851,7 @@ void Application::idle() { } // After finishing all of the above work, restart the idle timer, allowing 2ms to process events. - idleTimer->start(0); + idleTimer->start(2); } } @@ -2181,9 +2158,7 @@ void Application::init() { DependencyManager::get()->init(); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); -#if 0 _mirrorCamera.setMode(CAMERA_MODE_MIRROR); -#endif TV3DManager::connect(); if (TV3DManager::isConnected()) { @@ -2248,13 +2223,12 @@ void Application::init() { _entityClipboardRenderer.setViewFrustum(getViewFrustum()); _entityClipboardRenderer.setTree(&_entityClipboard); -#if 0 _rearMirrorTools = new RearMirrorTools(_mirrorViewRect); connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView())); connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView())); connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView())); connect(_rearMirrorTools, SIGNAL(resetView()), SLOT(resetSensors())); -#endif + // initialize the GlowEffect with our widget bool glow = Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect); @@ -3166,7 +3140,6 @@ PickRay Application::computePickRay(float x, float y) const { return result; } -#if 0 QImage Application::renderAvatarBillboard(RenderArgs* renderArgs) { auto primaryFramebuffer = DependencyManager::get()->getPrimaryFramebuffer(); glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFramebuffer)); @@ -3192,7 +3165,6 @@ QImage Application::renderAvatarBillboard(RenderArgs* renderArgs) { glBindFramebuffer(GL_FRAMEBUFFER, 0); return image; } -#endif ViewFrustum* Application::getViewFrustum() { #ifdef DEBUG @@ -3410,7 +3382,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se viewTransform.setTranslation(theCamera.getPosition()); viewTransform.setRotation(rotation); if (theCamera.getMode() == CAMERA_MODE_MIRROR) { - //viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f)); +// viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f)); } if (renderArgs->_renderSide != RenderArgs::MONO) { glm::mat4 invView = glm::inverse(_untranslatedViewMatrix); @@ -3677,6 +3649,79 @@ glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { return screenPoint; } +void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool billboard) { + // Grab current viewport to reset it at the end + int viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + float aspect = (float)region.width() / region.height(); + float fov = MIRROR_FIELD_OF_VIEW; + + // bool eyeRelativeCamera = false; + if (billboard) { + fov = BILLBOARD_FIELD_OF_VIEW; // degees + _mirrorCamera.setPosition(_myAvatar->getPosition() + + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale()); + + } else if (RearMirrorTools::rearViewZoomLevel.get() == BODY) { + _mirrorCamera.setPosition(_myAvatar->getChestPosition() + + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); + + } else { // HEAD zoom level + // FIXME note that the positioing of the camera relative to the avatar can suffer limited + // precision as the user's position moves further away from the origin. Thus at + // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways + // wildly as you rotate your avatar because the floating point values are becoming + // larger, squeezing out the available digits of precision you have available at the + // human scale for camera positioning. + + // Previously there was a hack to correct this using the mechanism of repositioning + // the avatar at the origin of the world for the purposes of rendering the mirror, + // but it resulted in failing to render the avatar's head model in the mirror view + // when in first person mode. Presumably this was because of some missed culling logic + // that was not accounted for in the hack. + + // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further + // investigated in order to adapt the technique while fixing the head rendering issue, + // but the complexity of the hack suggests that a better approach + _mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() + + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); + } + _mirrorCamera.setProjection(glm::perspective(glm::radians(fov), aspect, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); + _mirrorCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); + + // set the bounds of rear mirror view + if (billboard) { + QSize size = DependencyManager::get()->getFrameBufferSize(); + glViewport(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); + glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); + } else { + // if not rendering the billboard, the region is in device independent coordinates; must convert to device + QSize size = DependencyManager::get()->getFrameBufferSize(); + float ratio = (float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale(); + int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio; + glViewport(x, size.height() - y - height, width, height); + glScissor(x, size.height() - y - height, width, height); + } + bool updateViewFrustum = false; + updateProjectionMatrix(_mirrorCamera, updateViewFrustum); + glEnable(GL_SCISSOR_TEST); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // render rear mirror view + glPushMatrix(); + displaySide(renderArgs, _mirrorCamera, true, billboard); + glPopMatrix(); + + if (!billboard) { + _rearMirrorTools->render(renderArgs, false, _glWidget->mapFromGlobal(QCursor::pos())); + } + + // reset Viewport and projection matrix + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + glDisable(GL_SCISSOR_TEST); + updateProjectionMatrix(_myCamera, updateViewFrustum); +} + void Application::resetSensors() { DependencyManager::get()->reset(); DependencyManager::get()->reset(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 605c85f7f7..99834a13d2 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -108,6 +108,15 @@ static const QString FST_EXTENSION = ".fst"; static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; // degrees static const float BILLBOARD_DISTANCE = 5.56f; // meters +static const int MIRROR_VIEW_TOP_PADDING = 5; +static const int MIRROR_VIEW_LEFT_PADDING = 10; +static const int MIRROR_VIEW_WIDTH = 265; +static const int MIRROR_VIEW_HEIGHT = 215; +static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; +static const float MIRROR_REARVIEW_DISTANCE = 0.722f; +static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f; +static const float MIRROR_FIELD_OF_VIEW = 30.0f; + static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; static const QString INFO_HELP_PATH = "html/interface-welcome.html"; @@ -469,7 +478,6 @@ private slots: void faceTrackerMuteToggled(); void setCursorVisible(bool visible); - //void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool billboard = false); private: void resetCamerasOnResizeGL(Camera& camera, const glm::uvec2& size); @@ -504,6 +512,7 @@ private: glm::vec3 getSunDirection(); void updateShadowMap(RenderArgs* renderArgs); + void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool billboard = false); void setMenuShortcutsEnabled(bool enabled); static void attachNewHeadToNode(Node *newNode); @@ -555,6 +564,9 @@ private: UserInputMapper _userInputMapper; // User input mapper allowing to mapp different real devices to the action channels that the application has to offer MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be) Camera _myCamera; // My view onto the world + Camera _mirrorCamera; // Cammera for mirror view + QRect _mirrorViewRect; + RearMirrorTools* _rearMirrorTools; Setting::Handle _firstRun; Setting::Handle _previousScriptLocation; @@ -566,6 +578,10 @@ private: glm::vec3 _viewMatrixTranslation; glm::mat4 _projectionMatrix; + float _scaleMirror; + float _rotateMirror; + float _raiseMirror; + static const int CASCADED_SHADOW_MATRIX_COUNT = 4; glm::mat4 _shadowMatrices[CASCADED_SHADOW_MATRIX_COUNT]; glm::vec3 _shadowDistances; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6b0a398246..16cba72fcb 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1466,7 +1466,6 @@ void MyAvatar::maybeUpdateBillboard() { return; } } - /* gpu::Context context(new gpu::GLBackend()); RenderArgs renderArgs(&context); QImage image = qApp->renderAvatarBillboard(&renderArgs); @@ -1477,7 +1476,6 @@ void MyAvatar::maybeUpdateBillboard() { _billboardValid = true; sendBillboardPacket(); - */ } void MyAvatar::increaseSize() { @@ -1577,7 +1575,7 @@ glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { glm::vec3 result; - const auto& compositor = Application::getInstance()->getApplicationCompositor(); + const auto& compositor = qApp->getApplicationCompositor(); if (compositor.calculateRayUICollisionPoint(position, direction, result)) { return result; } diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 782d9dadba..53b3702cca 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -646,11 +646,12 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const renderArgs->_renderSide = RenderArgs::MONO; qApp->displaySide(renderArgs, *_camera); - //qApp->getApplicationCompositor().displayOverlayTexture(renderArgs); qApp->getApplicationCompositor().displayOverlayTextureHmd(renderArgs, eye); }); _activeEye = ovrEye_Count; + glPopMatrix(); + gpu::FramebufferPointer finalFbo; //Bind the output texture from the glow shader. If glow effect is disabled, we just grab the texture if (Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect)) { @@ -661,7 +662,6 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const finalFbo = DependencyManager::get()->getPrimaryFramebuffer(); glBindFramebuffer(GL_FRAMEBUFFER, 0); } - glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 08de5be0c8..b24054f8a8 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -42,14 +42,6 @@ const int AUDIO_METER_GAP = 5; const int MUTE_ICON_PADDING = 10; const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f }; const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; -static const int MIRROR_VIEW_TOP_PADDING = 5; -static const int MIRROR_VIEW_LEFT_PADDING = 10; -static const int MIRROR_VIEW_WIDTH = 265; -static const int MIRROR_VIEW_HEIGHT = 215; -static const int STATS_HORIZONTAL_OFFSET = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; -static const float MIRROR_REARVIEW_DISTANCE = 0.722f; -static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f; -static const float MIRROR_FIELD_OF_VIEW = 30.0f; static const float ORTHO_NEAR_CLIP = -10000; static const float ORTHO_FAR_CLIP = 10000; @@ -59,8 +51,7 @@ static void fboViewport(QOpenGLFramebufferObject* fbo) { glViewport(0, 0, size.width(), size.height()); } -ApplicationOverlay::ApplicationOverlay() : - _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)) +ApplicationOverlay::ApplicationOverlay() { auto geometryCache = DependencyManager::get(); _audioRedQuad = geometryCache->allocateID(); @@ -295,94 +286,16 @@ void ApplicationOverlay::renderAudioMeter(RenderArgs* renderArgs) { } void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) { - // Grab current viewport to reset it at the end - auto mirrorSize = _mirrorFramebuffer->size(); - float aspect = (float)mirrorSize.width() / mirrorSize.height(); - float fov = MIRROR_FIELD_OF_VIEW; - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - // bool eyeRelativeCamera = false; - if (RearMirrorTools::rearViewZoomLevel.get() == BODY) { - _mirrorCamera.setPosition(myAvatar->getChestPosition() + - myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * myAvatar->getScale()); - } else { // HEAD zoom level - // FIXME note that the positioing of the camera relative to the avatar can suffer limited - // precision as the user's position moves further away from the origin. Thus at - // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways - // wildly as you rotate your avatar because the floating point values are becoming - // larger, squeezing out the available digits of precision you have available at the - // human scale for camera positioning. - - // Previously there was a hack to correct this using the mechanism of repositioning - // the avatar at the origin of the world for the purposes of rendering the mirror, - // but it resulted in failing to render the avatar's head model in the mirror view - // when in first person mode. Presumably this was because of some missed culling logic - // that was not accounted for in the hack. - - // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further - // investigated in order to adapt the technique while fixing the head rendering issue, - // but the complexity of the hack suggests that a better approach - _mirrorCamera.setPosition(myAvatar->getHead()->getEyePosition() + - myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * myAvatar->getScale()); - } - _mirrorCamera.setProjection(glm::perspective(glm::radians(fov), aspect, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); - _mirrorCamera.setRotation(myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); - - // set the bounds of rear mirror view - fboViewport(_mirrorFramebuffer); - - _mirrorFramebuffer->bind(); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glLoadMatrixf(glm::value_ptr(_mirrorCamera.getProjection())); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glLoadMatrixf(glm::value_ptr(glm::mat4_cast(_mirrorCamera.getOrientation()) * glm::translate(glm::mat4(), _mirrorCamera.getPosition()))); - { - renderArgs->_context->syncCache(); - qApp->displaySide(renderArgs, _mirrorCamera, true, false); - } - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - _mirrorFramebuffer->release(); - fboViewport(_overlayFramebuffer); } void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) { - if (_mirrorFramebuffer && _mirrorFramebuffer->texture()) { - gpu::Batch batch; - auto geometryCache = DependencyManager::get(); - geometryCache->useSimpleDrawPipeline(batch); - batch._glDisable(GL_DEPTH_TEST); - batch._glDisable(GL_LIGHTING); - batch._glEnable(GL_BLEND); - batch.setProjectionTransform(mat4()); - batch.setModelTransform(mat4()); - auto textureCache = DependencyManager::get(); - batch.setUniformTexture(0, textureCache->getBlueTexture()); - geometryCache->renderUnitQuad(batch, glm::vec4(1)); - batch._glBindTexture(GL_TEXTURE_2D, _mirrorFramebuffer->texture()); - geometryCache->renderUnitQuad(batch, glm::vec4(1)); - renderArgs->_context->syncCache(); - auto overlaySize = _overlayFramebuffer->size(); - glViewport(MIRROR_VIEW_LEFT_PADDING, overlaySize.height() - MIRROR_VIEW_TOP_PADDING - MIRROR_VIEW_HEIGHT, - MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT); - renderArgs->_context->render(batch); - fboViewport(_overlayFramebuffer); - } } void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) { - // Display stats and log text onscreen // Determine whether to compute timing details - /* // Show on-screen msec timer if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) { @@ -457,18 +370,6 @@ GLuint ApplicationOverlay::getOverlayTexture() { } void ApplicationOverlay::buildFramebufferObject() { - if (!_mirrorFramebuffer) { - _mirrorFramebuffer = new QOpenGLFramebufferObject(QSize(MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT), QOpenGLFramebufferObject::Depth); - glBindTexture(GL_TEXTURE_2D, _mirrorFramebuffer->texture()); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - GLfloat borderColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); - glBindTexture(GL_TEXTURE_2D, 0); - } - auto canvasSize = qApp->getCanvasSize(); QSize fboSize = QSize(canvasSize.x, canvasSize.y); if (_overlayFramebuffer && fboSize == _overlayFramebuffer->size()) { diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index ab242e592b..e50622d122 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -49,15 +49,8 @@ private: int _domainStatusBorder; int _magnifierBorder; - float _scaleMirror{ 1.0f }; - float _rotateMirror{ 0.0f }; - float _raiseMirror{ 0.0f }; - - Camera _mirrorCamera; ivec2 _previousBorderSize{ -1 }; - QRect _mirrorViewRect; QOpenGLFramebufferObject* _overlayFramebuffer{ nullptr }; - QOpenGLFramebufferObject* _mirrorFramebuffer{ nullptr }; }; #endif // hifi_ApplicationOverlay_h diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index c1d77b5fc2..acc9521c72 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -333,7 +333,7 @@ void Stats::updateStats() { QString functionName = j.value(); const PerformanceTimerRecord& record = allRecords.value(functionName); perfLines += QString("%1: %2 [%3]\n"). - arg(QString(qPrintable(functionName)), 120, noBreakingSpace). + arg(QString(qPrintable(functionName)), 90, noBreakingSpace). arg((float)record.getMovingAverage() / (float)USECS_PER_MSEC, 8, 'f', 3, noBreakingSpace). arg((int)record.getCount(), 6, 10, noBreakingSpace); linesDisplayed++; diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 5103633761..cd57d13615 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -689,4 +689,20 @@ void GLBackend::fetchMatrix(GLenum target, glm::mat4 & m) { glGetFloatv(target, glm::value_ptr(m)); } +void GLBackend::checkGLStackStable(std::function f) { +#ifdef DEBUG + GLint mvDepth, prDepth; + glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &mvDepth); + glGetIntegerv(GL_PROJECTION_STACK_DEPTH, &prDepth); +#endif + f(); + +#ifdef DEBUG + GLint mvDepthFinal, prDepthFinal; + glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &mvDepthFinal); + glGetIntegerv(GL_PROJECTION_STACK_DEPTH, &prDepthFinal); + Q_ASSERT(mvDepth == mvDepthFinal); + Q_ASSERT(prDepth == prDepthFinal); +#endif +} diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index b6a93d1ea3..28236c68c9 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -12,11 +12,13 @@ #define hifi_gpu_GLBackend_h #include +#include +#include + #include "GPUConfig.h" #include "Context.h" #include "Batch.h" -#include namespace gpu { @@ -50,6 +52,8 @@ public: // Only checks in debug builds static bool checkGLErrorDebug(const char* name = nullptr); + static void checkGLStackStable(std::function f); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); diff --git a/libraries/gpu/src/gpu/GLBackendShared.h b/libraries/gpu/src/gpu/GLBackendShared.h index 27f58fcbe3..365934379f 100644 --- a/libraries/gpu/src/gpu/GLBackendShared.h +++ b/libraries/gpu/src/gpu/GLBackendShared.h @@ -53,4 +53,6 @@ static const GLenum _elementTypeToGLType[gpu::NUM_TYPES] = { // #define CHECK_GL_ERROR() gpu::GLBackend::checkGLErrorDebug(__FUNCTION__ ":" CHECK_GL_ERROR_HELPER(__LINE__)) #define CHECK_GL_ERROR() gpu::GLBackend::checkGLErrorDebug(__FUNCTION__) +#define CHECK_GL_STACK_STABLE(f) gpu::GLBackend::checkGLStackStable(f) + #endif From 99c217184b20833933273855cf2135a2ef753962 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 19 Jun 2015 14:46:39 -0700 Subject: [PATCH 29/35] Fixing the mic/camera buttons and the mirror UI buttons --- interface/resources/qml/AvatarInputs.qml | 177 ++++++++ interface/resources/qml/Stats.qml | 462 +++++++++++---------- interface/src/Application.cpp | 83 +--- interface/src/Application.h | 2 - interface/src/audio/AudioToolBox.cpp | 114 ----- interface/src/audio/AudioToolBox.h | 36 -- interface/src/devices/CameraToolBox.cpp | 121 ------ interface/src/devices/CameraToolBox.h | 45 -- interface/src/ui/ApplicationCompositor.cpp | 15 - interface/src/ui/ApplicationOverlay.cpp | 146 +------ interface/src/ui/ApplicationOverlay.h | 4 +- interface/src/ui/AvatarInputs.cpp | 133 ++++++ interface/src/ui/AvatarInputs.h | 59 +++ interface/src/ui/RearMirrorTools.cpp | 134 ------ interface/src/ui/RearMirrorTools.h | 56 --- interface/src/ui/Stats.cpp | 7 +- interface/src/ui/Stats.h | 5 +- 17 files changed, 626 insertions(+), 973 deletions(-) create mode 100644 interface/resources/qml/AvatarInputs.qml delete mode 100644 interface/src/audio/AudioToolBox.cpp delete mode 100644 interface/src/audio/AudioToolBox.h delete mode 100644 interface/src/devices/CameraToolBox.cpp delete mode 100644 interface/src/devices/CameraToolBox.h create mode 100644 interface/src/ui/AvatarInputs.cpp create mode 100644 interface/src/ui/AvatarInputs.h delete mode 100644 interface/src/ui/RearMirrorTools.cpp delete mode 100644 interface/src/ui/RearMirrorTools.h diff --git a/interface/resources/qml/AvatarInputs.qml b/interface/resources/qml/AvatarInputs.qml new file mode 100644 index 0000000000..7888ffd967 --- /dev/null +++ b/interface/resources/qml/AvatarInputs.qml @@ -0,0 +1,177 @@ +// +// Created by Bradley Austin Davis on 2015/06/19 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 as Hifi +import QtQuick 2.4 +import QtQuick.Controls 1.3 +import QtGraphicalEffects 1.0 + +Hifi.AvatarInputs { + id: root + objectName: "AvatarInputs" + anchors.fill: parent + +// width: 800 +// height: 600 +// color: "black" + readonly property int iconPadding: 5 + readonly property int mirrorHeight: 215 + readonly property int mirrorWidth: 265 + readonly property int mirrorTopPad: iconPadding + readonly property int mirrorLeftPad: 10 + readonly property int iconSize: 24 + + Item { + id: mirror + width: root.mirrorWidth + height: root.mirrorVisible ? root.mirrorHeight : 0 + visible: root.mirrorVisible + anchors.left: parent.left + anchors.leftMargin: root.mirrorLeftPad + anchors.top: parent.top + anchors.topMargin: root.mirrorTopPad + clip: true + + MouseArea { + id: hover + anchors.fill: parent + hoverEnabled: true + propagateComposedEvents: true + } + + Image { + id: closeMirror + visible: hover.containsMouse + width: root.iconSize + height: root.iconSize + anchors.top: parent.top + anchors.topMargin: root.iconPadding + anchors.left: parent.left + anchors.leftMargin: root.iconPadding + source: "../images/close.svg" + MouseArea { + anchors.fill: parent + onClicked: { + root.closeMirror(); + } + } + } + + Image { + id: zoomIn + visible: hover.containsMouse + width: root.iconSize + height: root.iconSize + anchors.bottom: parent.bottom + anchors.bottomMargin: root.iconPadding + anchors.left: parent.left + anchors.leftMargin: root.iconPadding + source: root.mirrorZoomed ? "../images/minus.svg" : "../images/plus.svg" + MouseArea { + anchors.fill: parent + onClicked: { + root.toggleZoom(); + } + } + } + } + + Item { + width: root.mirrorWidth + height: 44 + + x: root.mirrorLeftPad + y: root.mirrorVisible ? root.mirrorTopPad + root.mirrorHeight : 5 + + + + Rectangle { + anchors.fill: parent + color: root.mirrorVisible ? (root.audioClipping ? "red" : "#696969") : "#00000000" + + Image { + id: faceMute + width: root.iconSize + height: root.iconSize + visible: root.cameraEnabled + anchors.left: parent.left + anchors.leftMargin: root.iconPadding + anchors.verticalCenter: parent.verticalCenter + source: root.cameraMuted ? "../images/face-mute.svg" : "../images/face.svg" + MouseArea { + anchors.fill: parent + onClicked: { + root.toggleCameraMute() + } + onDoubleClicked: { + root.resetSensors(); + } + } + } + + Image { + id: micMute + width: root.iconSize + height: root.iconSize + anchors.left: root.cameraEnabled ? faceMute.right : parent.left + anchors.leftMargin: root.iconPadding + anchors.verticalCenter: parent.verticalCenter + source: root.audioMuted ? "../images/mic-mute.svg" : "../images/mic.svg" + MouseArea { + anchors.fill: parent + onClicked: { + root.toggleAudioMute() + } + } + } + + Item { + id: audioMeter + anchors.verticalCenter: parent.verticalCenter + anchors.left: micMute.right + anchors.leftMargin: root.iconPadding + anchors.right: parent.right + anchors.rightMargin: root.iconPadding + height: 8 + Rectangle { + id: blueRect + color: "blue" + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + width: parent.width / 4 + } + Rectangle { + id: greenRect + color: "green" + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: blueRect.right + anchors.right: redRect.left + } + Rectangle { + id: redRect + color: "red" + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + width: parent.width / 5 + } + Rectangle { + z: 100 + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + width: (1.0 - root.audioLevel) * parent.width + color: "black" + } + } + } + } +} + diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 8ddea6a355..128dbcd3a6 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -2,255 +2,257 @@ import Hifi 1.0 as Hifi import QtQuick 2.3 import QtQuick.Controls 1.2 -Hifi.Stats { - id: root - objectName: "Stats" - implicitHeight: row.height - implicitWidth: row.width - readonly property int sTATS_GENERAL_MIN_WIDTH: 165 - readonly property int sTATS_PING_MIN_WIDTH: 190 - readonly property int sTATS_GEO_MIN_WIDTH: 240 - readonly property int sTATS_OCTREE_MIN_WIDTH: 410 - readonly property int fontSize: 12 - readonly property string fontColor: "white" - readonly property string bgColor: "#99333333" +Item { + anchors.fill: parent + anchors.leftMargin: 300 + Hifi.Stats { + id: root + objectName: "Stats" + implicitHeight: row.height + implicitWidth: row.width - onParentChanged: { - root.x = parent.width - root.width; - } + anchors.horizontalCenter: parent.horizontalCenter + readonly property int sTATS_GENERAL_MIN_WIDTH: 165 + readonly property int sTATS_PING_MIN_WIDTH: 190 + readonly property int sTATS_GEO_MIN_WIDTH: 240 + readonly property int sTATS_OCTREE_MIN_WIDTH: 410 + readonly property int fontSize: 12 + readonly property string fontColor: "white" + readonly property string bgColor: "#99333333" - Row { - id: row - spacing: 8 - Rectangle { - width: generalCol.width + 8; - height: generalCol.height + 8; - color: root.bgColor; + Row { + id: row + spacing: 8 + Rectangle { + width: generalCol.width + 8; + height: generalCol.height + 8; + color: root.bgColor; - MouseArea { - anchors.fill: parent - onClicked: { root.expanded = !root.expanded; } - } - - Column { - id: generalCol - spacing: 4; x: 4; y: 4; - width: sTATS_GENERAL_MIN_WIDTH - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - text: "Servers: " + root.serverCount + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - text: "Avatars: " + root.avatarCount - } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - text: "Framerate: " + root.framerate - } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount - } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2) - } - } - } - - Rectangle { - width: pingCol.width + 8 - height: pingCol.height + 8 - color: root.bgColor; - visible: root.audioPing != -2 - MouseArea { - anchors.fill: parent - onClicked: { root.expanded = !root.expanded; } - } - Column { - id: pingCol - spacing: 4; x: 4; y: 4; - width: sTATS_PING_MIN_WIDTH - Text { - color: root.fontColor - font.pixelSize: root.fontSize - text: "Audio ping: " + root.audioPing - } - Text { - color: root.fontColor - font.pixelSize: root.fontSize - text: "Avatar ping: " + root.avatarPing - } - Text { - color: root.fontColor - font.pixelSize: root.fontSize - text: "Entities avg ping: " + root.entitiesPing - } - Text { - color: root.fontColor - font.pixelSize: root.fontSize - visible: root.expanded; - text: "Voxel max ping: " + 0 - } - } - } - Rectangle { - width: geoCol.width + 8 - height: geoCol.height + 8 - color: root.bgColor; - MouseArea { - anchors.fill: parent - onClicked: { root.expanded = !root.expanded; } + Column { + id: generalCol + spacing: 4; x: 4; y: 4; + width: sTATS_GENERAL_MIN_WIDTH + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Servers: " + root.serverCount + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Avatars: " + root.avatarCount + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Framerate: " + root.framerate + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2) + } + } } - Column { - id: geoCol - spacing: 4; x: 4; y: 4; - width: sTATS_GEO_MIN_WIDTH - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - text: "Position: " + root.position.x.toFixed(1) + ", " + - root.position.y.toFixed(1) + ", " + root.position.z.toFixed(1) + + Rectangle { + width: pingCol.width + 8 + height: pingCol.height + 8 + color: root.bgColor; + visible: root.audioPing != -2 + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - text: "Velocity: " + root.velocity.toFixed(1) + Column { + id: pingCol + spacing: 4; x: 4; y: 4; + width: sTATS_PING_MIN_WIDTH + Text { + color: root.fontColor + font.pixelSize: root.fontSize + text: "Audio ping: " + root.audioPing + } + Text { + color: root.fontColor + font.pixelSize: root.fontSize + text: "Avatar ping: " + root.avatarPing + } + Text { + color: root.fontColor + font.pixelSize: root.fontSize + text: "Entities avg ping: " + root.entitiesPing + } + Text { + color: root.fontColor + font.pixelSize: root.fontSize + visible: root.expanded; + text: "Voxel max ping: " + 0 + } } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - text: "Yaw: " + root.yaw.toFixed(1) + } + + Rectangle { + width: geoCol.width + 8 + height: geoCol.height + 8 + color: root.bgColor; + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - visible: root.expanded; - text: "Avatar Mixer: " + root.avatarMixerKbps + " kbps, " + - root.avatarMixerPps + "pps"; + Column { + id: geoCol + spacing: 4; x: 4; y: 4; + width: sTATS_GEO_MIN_WIDTH + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Position: " + root.position.x.toFixed(1) + ", " + + root.position.y.toFixed(1) + ", " + root.position.z.toFixed(1) + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Velocity: " + root.velocity.toFixed(1) + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Yaw: " + root.yaw.toFixed(1) + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded; + text: "Avatar Mixer: " + root.avatarMixerKbps + " kbps, " + + root.avatarMixerPps + "pps"; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded; + text: "Downloads: "; + } } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - visible: root.expanded; - text: "Downloads: "; + } + Rectangle { + width: octreeCol.width + 8 + height: octreeCol.height + 8 + color: root.bgColor; + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + } + Column { + id: octreeCol + spacing: 4; x: 4; y: 4; + width: sTATS_OCTREE_MIN_WIDTH + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Triangles: " + root.triangles + + " / Quads: " + root.quads + " / Material Switches: " + root.materialSwitches + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded; + text: "\tMesh Parts Rendered Opaque: " + root.meshOpaque + + " / Translucent: " + root.meshTranslucent; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded; + text: "\tOpaque considered: " + root.opaqueConsidered + + " / Out of view: " + root.opaqueOutOfView + " / Too small: " + root.opaqueTooSmall; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: !root.expanded + text: "Octree Elements Server: " + root.serverElements + + " Local: " + root.localElements; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "Octree Sending Mode: " + root.sendingMode; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "Octree Packets to Process: " + root.packetStats; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "Octree Elements - "; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "\tServer: " + root.serverElements + + " Internal: " + root.serverInternal + + " Leaves: " + root.serverLeaves; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "\tLocal: " + root.localElements + + " Internal: " + root.localInternal + + " Leaves: " + root.localLeaves; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "LOD: " + root.lodStatus; + } } } } + Rectangle { - width: octreeCol.width + 8 - height: octreeCol.height + 8 + y: 250 + visible: root.timingExpanded + width: perfText.width + 8 + height: perfText.height + 8 color: root.bgColor; - MouseArea { - anchors.fill: parent - onClicked: { root.expanded = !root.expanded; } + Text { + x: 4; y: 4 + id: perfText + color: root.fontColor + font.family: root.monospaceFont + font.pixelSize: 12 + text: "------------------------------------------ Function " + + "--------------------------------------- --msecs- -calls--\n" + + root.timingStats; } - Column { - id: octreeCol - spacing: 4; x: 4; y: 4; - width: sTATS_OCTREE_MIN_WIDTH - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - text: "Triangles: " + root.triangles + - " / Quads: " + root.quads + " / Material Switches: " + root.materialSwitches - } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - visible: root.expanded; - text: "\tMesh Parts Rendered Opaque: " + root.meshOpaque + - " / Translucent: " + root.meshTranslucent; - } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - visible: root.expanded; - text: "\tOpaque considered: " + root.opaqueConsidered + - " / Out of view: " + root.opaqueOutOfView + " / Too small: " + root.opaqueTooSmall; - } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - visible: !root.expanded - text: "Octree Elements Server: " + root.serverElements + - " Local: " + root.localElements; - } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - visible: root.expanded - text: "Octree Sending Mode: " + root.sendingMode; - } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - visible: root.expanded - text: "Octree Packets to Process: " + root.packetStats; - } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - visible: root.expanded - text: "Octree Elements - "; - } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - visible: root.expanded - text: "\tServer: " + root.serverElements + - " Internal: " + root.serverInternal + - " Leaves: " + root.serverLeaves; - } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - visible: root.expanded - text: "\tLocal: " + root.localElements + - " Internal: " + root.localInternal + - " Leaves: " + root.localLeaves; - } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - visible: root.expanded - text: "LOD: " + root.lodStatus; - } + } + + Connections { + target: root.parent + onWidthChanged: { + root.x = root.parent.width - root.width; } } } - Rectangle { - y: 250 - visible: root.timingExpanded - width: perfText.width + 8 - height: perfText.height + 8 - color: root.bgColor; - Text { - x: 4; y: 4 - id: perfText - color: root.fontColor - font.family: root.monospaceFont - font.pixelSize: 12 - text: "------------------------------------------ Function " + - "--------------------------------------- --msecs- -calls--\n" + - root.timingStats; - } - } - - Connections { - target: root.parent - onWidthChanged: { - root.x = root.parent.width - root.width; - } - } } - diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dc0eb3d23f..fafe339bd1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -66,6 +66,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -83,6 +86,7 @@ #include #include #include +#include #include #include #include @@ -107,11 +111,9 @@ #include "avatar/AvatarManager.h" -#include "audio/AudioToolBox.h" #include "audio/AudioIOStatsRenderer.h" #include "audio/AudioScope.h" -#include "devices/CameraToolBox.h" #include "devices/DdeFaceTracker.h" #include "devices/Faceshift.h" #include "devices/Leapmotion.h" @@ -121,12 +123,6 @@ #include "devices/OculusManager.h" #include "devices/TV3DManager.h" -#include "gpu/Batch.h" -#include "gpu/Context.h" -#include "gpu/GLBackend.h" - -#include "RenderDeferredTask.h" - #include "scripting/AccountScriptingInterface.h" #include "scripting/AudioDeviceScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" @@ -142,6 +138,7 @@ #include "SpeechRecognizer.h" #endif +#include "ui/AvatarInputs.h" #include "ui/DataWebDialog.h" #include "ui/DialogsManager.h" #include "ui/LoginDialog.h" @@ -282,8 +279,6 @@ bool setupEssentials(int& argc, char** argv) { auto animationCache = DependencyManager::set(); auto ddeFaceTracker = DependencyManager::set(); auto modelBlender = DependencyManager::set(); - auto audioToolBox = DependencyManager::set(); - auto cameraToolBox = DependencyManager::set(); auto avatarManager = DependencyManager::set(); auto lodManager = DependencyManager::set(); auto jsConsole = DependencyManager::set(); @@ -861,23 +856,11 @@ void Application::initializeUi() { }); } -/* - { - PerformanceTimer perfTimer("renderOverlay"); - gpu::Context context(new gpu::GLBackend()); - RenderArgs renderArgs(&context, nullptr, getViewFrustum(), lodManager->getOctreeSizeScale(), - lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, - RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); - _applicationOverlay.renderOverlay(&renderArgs); - } -*/ - void Application::paintGL() { PROFILE_RANGE(__FUNCTION__); _glWidget->makeCurrent(); auto lodManager = DependencyManager::get(); - gpu::Context context(new gpu::GLBackend()); RenderArgs renderArgs(&context, nullptr, getViewFrustum(), lodManager->getOctreeSizeScale(), lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, @@ -895,6 +878,17 @@ void Application::paintGL() { PerformanceWarning warn(showWarnings, "Application::paintGL()"); resizeGL(); + { + PerformanceTimer perfTimer("renderOverlay"); + /* + gpu::Context context(new gpu::GLBackend()); + RenderArgs renderArgs(&context, nullptr, getViewFrustum(), lodManager->getOctreeSizeScale(), + lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, + RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); + */ + _applicationOverlay.renderOverlay(&renderArgs); + } + glEnable(GL_LINE_SMOOTH); Menu::getInstance()->setIsOptionChecked("First Person", _myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); @@ -977,9 +971,7 @@ void Application::paintGL() { glPopMatrix(); renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; - if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { - _rearMirrorTools->render(&renderArgs, true, _glWidget->mapFromGlobal(QCursor::pos())); - } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { renderRearViewMirror(&renderArgs, _mirrorViewRect); } @@ -1576,23 +1568,6 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { _mouseDragStarted = getTrueMouse(); _mousePressed = true; - if (mouseOnScreen()) { - if (DependencyManager::get()->mousePressEvent(getMouseX(), getMouseY())) { - // stop propagation - return; - } - - if (DependencyManager::get()->mousePressEvent(getMouseX(), getMouseY())) { - // stop propagation - return; - } - - if (_rearMirrorTools->mousePressEvent(getMouseX(), getMouseY())) { - // stop propagation - return; - } - } - // nobody handled this - make it an action event on the _window object HFActionEvent actionEvent(HFActionEvent::startType(), computePickRay(event->x(), event->y())); @@ -1609,17 +1584,6 @@ void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceI if (_controllerScriptingInterface.isMouseCaptured()) { return; } - - if (activeWindow() == _window) { - if (event->button() == Qt::LeftButton) { - if (mouseOnScreen()) { - if (DependencyManager::get()->mouseDoublePressEvent(getMouseX(), getMouseY())) { - // stop propagation - return; - } - } - } - } } void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { @@ -2223,13 +2187,6 @@ void Application::init() { _entityClipboardRenderer.setViewFrustum(getViewFrustum()); _entityClipboardRenderer.setTree(&_entityClipboard); - _rearMirrorTools = new RearMirrorTools(_mirrorViewRect); - connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView())); - connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView())); - connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView())); - connect(_rearMirrorTools, SIGNAL(resetView()), SLOT(resetSensors())); - - // initialize the GlowEffect with our widget bool glow = Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect); DependencyManager::get()->init(glow); @@ -3662,7 +3619,7 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi _mirrorCamera.setPosition(_myAvatar->getPosition() + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale()); - } else if (RearMirrorTools::rearViewZoomLevel.get() == BODY) { + } else if (!AvatarInputs::getInstance()->mirrorZoomed()) { _mirrorCamera.setPosition(_myAvatar->getChestPosition() + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); @@ -3712,10 +3669,6 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi displaySide(renderArgs, _mirrorCamera, true, billboard); glPopMatrix(); - if (!billboard) { - _rearMirrorTools->render(renderArgs, false, _glWidget->mapFromGlobal(QCursor::pos())); - } - // reset Viewport and projection matrix glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); glDisable(GL_SCISSOR_TEST); diff --git a/interface/src/Application.h b/interface/src/Application.h index 99834a13d2..b126757621 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -62,7 +62,6 @@ #include "ui/ModelsBrowser.h" #include "ui/NodeBounds.h" #include "ui/OctreeStatsDialog.h" -#include "ui/RearMirrorTools.h" #include "ui/SnapshotShareDialog.h" #include "ui/LodToolsDialog.h" #include "ui/LogDialog.h" @@ -566,7 +565,6 @@ private: Camera _myCamera; // My view onto the world Camera _mirrorCamera; // Cammera for mirror view QRect _mirrorViewRect; - RearMirrorTools* _rearMirrorTools; Setting::Handle _firstRun; Setting::Handle _previousScriptLocation; diff --git a/interface/src/audio/AudioToolBox.cpp b/interface/src/audio/AudioToolBox.cpp deleted file mode 100644 index 68328e151e..0000000000 --- a/interface/src/audio/AudioToolBox.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// -// AudioToolBox.cpp -// interface/src/audio -// -// Created by Stephen Birarda on 2014-12-16. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "InterfaceConfig.h" - -#include -#include -#include -#include -#include - -#include "Application.h" -#include "AudioToolBox.h" - -// Mute icon configration -const int MUTE_ICON_SIZE = 24; - -AudioToolBox::AudioToolBox() : - _iconPulseTimeReference(usecTimestampNow()) -{ -} - -bool AudioToolBox::mousePressEvent(int x, int y) { - if (_iconBounds.contains(x, y)) { - DependencyManager::get()->toggleMute(); - return true; - } - return false; -} - -void AudioToolBox::render(int x, int y, int padding, bool boxed) { - glEnable(GL_TEXTURE_2D); - - if (!_micTexture) { - _micTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic.svg"); - } - if (!_muteTexture) { - _muteTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic-mute.svg"); - } - if (_boxTexture) { - _boxTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/audio-box.svg"); - } - - auto audioIO = DependencyManager::get(); - - if (boxed) { - bool isClipping = ((audioIO->getTimeSinceLastClip() > 0.0f) && (audioIO->getTimeSinceLastClip() < 1.0f)); - const int BOX_LEFT_PADDING = 5; - const int BOX_TOP_PADDING = 10; - const int BOX_WIDTH = 266; - const int BOX_HEIGHT = 44; - - QRect boxBounds = QRect(x - BOX_LEFT_PADDING, y - BOX_TOP_PADDING, BOX_WIDTH, BOX_HEIGHT); - glm::vec4 quadColor; - - if (isClipping) { - quadColor = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f); - } else { - quadColor = glm::vec4(0.41f, 0.41f, 0.41f, 1.0f); - } - glm::vec2 topLeft(boxBounds.left(), boxBounds.top()); - glm::vec2 bottomRight(boxBounds.right(), boxBounds.bottom()); - static const glm::vec2 texCoordTopLeft(1,1); - static const glm::vec2 texCoordBottomRight(0, 0); - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_boxTexture)); - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor); - } - - float iconColor = 1.0f; - - _iconBounds = QRect(x + padding, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE); - if (!audioIO->isMuted()) { - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_micTexture)); - iconColor = 1.0f; - } else { - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_muteTexture)); - - // Make muted icon pulsate - static const float PULSE_MIN = 0.4f; - static const float PULSE_MAX = 1.0f; - static const float PULSE_FREQUENCY = 1.0f; // in Hz - qint64 now = usecTimestampNow(); - if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) { - // Prevents t from getting too big, which would diminish glm::cos precision - _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND); - } - float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND; - float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f; - iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor; - } - - glm::vec4 quadColor(iconColor, iconColor, iconColor, 1.0f); - - glm::vec2 topLeft(_iconBounds.left(), _iconBounds.top()); - glm::vec2 bottomRight(_iconBounds.right(), _iconBounds.bottom()); - glm::vec2 texCoordTopLeft(1,1); - glm::vec2 texCoordBottomRight(0,0); - - if (_boxQuadID == GeometryCache::UNKNOWN_ID) { - _boxQuadID = DependencyManager::get()->allocateID(); - } - - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor, _boxQuadID); - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); -} \ No newline at end of file diff --git a/interface/src/audio/AudioToolBox.h b/interface/src/audio/AudioToolBox.h deleted file mode 100644 index ea2ef9f96a..0000000000 --- a/interface/src/audio/AudioToolBox.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// AudioToolBox.h -// interface/src/audio -// -// Created by Stephen Birarda on 2014-12-16. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_AudioToolBox_h -#define hifi_AudioToolBox_h - -#include -#include -#include - -class AudioToolBox : public Dependency { - SINGLETON_DEPENDENCY -public: - void render(int x, int y, int padding, bool boxed); - - bool mousePressEvent(int x, int y); -protected: - AudioToolBox(); -private: - gpu::TexturePointer _micTexture; - gpu::TexturePointer _muteTexture; - gpu::TexturePointer _boxTexture; - int _boxQuadID = GeometryCache::UNKNOWN_ID; - QRect _iconBounds; - qint64 _iconPulseTimeReference = 0; -}; - -#endif // hifi_AudioToolBox_h \ No newline at end of file diff --git a/interface/src/devices/CameraToolBox.cpp b/interface/src/devices/CameraToolBox.cpp deleted file mode 100644 index 27cee5185b..0000000000 --- a/interface/src/devices/CameraToolBox.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// -// CameraToolBox.cpp -// interface/src/devices -// -// Created by David Rowe on 30 Apr 2015. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "InterfaceConfig.h" - -#include -#include - -#include "gpu/GLBackend.h" -#include "Application.h" -#include "CameraToolBox.h" -#include "FaceTracker.h" - - -CameraToolBox::CameraToolBox() : - _iconPulseTimeReference(usecTimestampNow()), - _doubleClickTimer(NULL) -{ -} - -CameraToolBox::~CameraToolBox() { - if (_doubleClickTimer) { - _doubleClickTimer->stop(); - delete _doubleClickTimer; - } -} - -bool CameraToolBox::mousePressEvent(int x, int y) { - if (_iconBounds.contains(x, y)) { - if (!_doubleClickTimer) { - // Toggle mute after waiting to check that it's not a double-click. - const int DOUBLE_CLICK_WAIT = 200; // ms - _doubleClickTimer = new QTimer(this); - connect(_doubleClickTimer, SIGNAL(timeout()), this, SLOT(toggleMute())); - _doubleClickTimer->setSingleShot(true); - _doubleClickTimer->setInterval(DOUBLE_CLICK_WAIT); - _doubleClickTimer->start(); - } - return true; - } - return false; -} - -bool CameraToolBox::mouseDoublePressEvent(int x, int y) { - if (_iconBounds.contains(x, y)) { - if (_doubleClickTimer) { - _doubleClickTimer->stop(); - delete _doubleClickTimer; - _doubleClickTimer = NULL; - } - Application::getInstance()->resetSensors(); - return true; - } - return false; -} - -void CameraToolBox::toggleMute() { - delete _doubleClickTimer; - _doubleClickTimer = NULL; - - FaceTracker* faceTracker = Application::getInstance()->getSelectedFaceTracker(); - if (faceTracker) { - faceTracker->toggleMute(); - } -} - -void CameraToolBox::render(int x, int y, bool boxed) { - glEnable(GL_TEXTURE_2D); - - if (!_enabledTexture) { - _enabledTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face.svg"); - } - if (!_mutedTexture) { - _mutedTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face-mute.svg"); - } - - const int MUTE_ICON_SIZE = 24; - _iconBounds = QRect(x, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE); - float iconColor = 1.0f; - if (!Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)) { - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_enabledTexture)); - } else { - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_mutedTexture)); - - // Make muted icon pulsate - static const float PULSE_MIN = 0.4f; - static const float PULSE_MAX = 1.0f; - static const float PULSE_FREQUENCY = 1.0f; // in Hz - qint64 now = usecTimestampNow(); - if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) { - // Prevents t from getting too big, which would diminish glm::cos precision - _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND); - } - float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND; - float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f; - iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor; - } - - glm::vec4 quadColor(iconColor, iconColor, iconColor, 1.0f); - - glm::vec2 topLeft(_iconBounds.left(), _iconBounds.top()); - glm::vec2 bottomRight(_iconBounds.right(), _iconBounds.bottom()); - glm::vec2 texCoordTopLeft(1,1); - glm::vec2 texCoordBottomRight(0,0); - - if (_boxQuadID == GeometryCache::UNKNOWN_ID) { - _boxQuadID = DependencyManager::get()->allocateID(); - } - - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor, _boxQuadID); - - glDisable(GL_TEXTURE_2D); -} \ No newline at end of file diff --git a/interface/src/devices/CameraToolBox.h b/interface/src/devices/CameraToolBox.h deleted file mode 100644 index 89e0c6a8dc..0000000000 --- a/interface/src/devices/CameraToolBox.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// CameraToolBox.h -// interface/src/devices -// -// Created by David Rowe on 30 Apr 2015. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_CameraToolBox_h -#define hifi_CameraToolBox_h - -#include - -#include -#include - -class CameraToolBox : public QObject, public Dependency { - Q_OBJECT - SINGLETON_DEPENDENCY - -public: - void render(int x, int y, bool boxed); - bool mousePressEvent(int x, int y); - bool mouseDoublePressEvent(int x, int y); - -protected: - CameraToolBox(); - ~CameraToolBox(); - -private slots: - void toggleMute(); - -private: - gpu::TexturePointer _enabledTexture; - gpu::TexturePointer _mutedTexture; - int _boxQuadID = GeometryCache::UNKNOWN_ID; - QRect _iconBounds; - qint64 _iconPulseTimeReference = 0; - QTimer* _doubleClickTimer; -}; - -#endif // hifi_CameraToolBox_h \ No newline at end of file diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index c2d2d91216..8e8290f060 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -16,27 +16,12 @@ #include #include -#include #include -#include -#include #include -#include #include - -#include "AudioClient.h" -#include "audio/AudioIOStatsRenderer.h" -#include "audio/AudioScope.h" -#include "audio/AudioToolBox.h" #include "Application.h" -#include "devices/CameraToolBox.h" -#include "Util.h" -#include "ui/Stats.h" - -#include "../../libraries/render-utils/standardTransformPNTC_vert.h" -#include "../../libraries/render-utils/standardDrawTexture_frag.h" // Used to animate the magnification windows static const float MAG_SPEED = 0.08f; diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index b24054f8a8..8e5bdca8c0 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -29,13 +29,12 @@ #include "AudioClient.h" #include "audio/AudioIOStatsRenderer.h" #include "audio/AudioScope.h" -#include "audio/AudioToolBox.h" #include "Application.h" #include "ApplicationOverlay.h" -#include "devices/CameraToolBox.h" #include "Util.h" #include "ui/Stats.h" +#include "ui/AvatarInputs.h" const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; const int AUDIO_METER_GAP = 5; @@ -86,12 +85,10 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { // TODO move to Application::idle()? Stats::getInstance()->updateStats(); + AvatarInputs::getInstance()->update(); buildFramebufferObject(); - // First render the mirror to the mirror FBO - // renderRearViewToFbo(renderArgs); - // Execute the batch into our framebuffer _overlayFramebuffer->bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -99,10 +96,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { // Now render the overlay components together into a single texture renderOverlays(renderArgs); - //renderAudioMeter(renderArgs); - //renderCameraToggle(renderArgs); renderStatsAndLogs(renderArgs); - // renderRearView(renderArgs); renderDomainConnectionStatusBorder(renderArgs); renderQmlUi(renderArgs); _overlayFramebuffer->release(); @@ -149,142 +143,6 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { fboViewport(_overlayFramebuffer); } -void ApplicationOverlay::renderCameraToggle(RenderArgs* renderArgs) { - if (Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)) { - return; - } - - int audioMeterY; - bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode(); - bool boxed = smallMirrorVisible && - !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror); - if (boxed) { - audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_GAP + MUTE_ICON_PADDING; - } else { - audioMeterY = AUDIO_METER_GAP + MUTE_ICON_PADDING; - } - - DependencyManager::get()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, boxed); - fboViewport(_overlayFramebuffer); -} - -void ApplicationOverlay::renderAudioMeter(RenderArgs* renderArgs) { - auto audio = DependencyManager::get(); - - // Audio VU Meter and Mute Icon - const int MUTE_ICON_SIZE = 24; - const int AUDIO_METER_HEIGHT = 8; - const int INTER_ICON_GAP = 2; - - int cameraSpace = 0; - int audioMeterWidth = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - MUTE_ICON_PADDING; - int audioMeterScaleWidth = audioMeterWidth - 2; - int audioMeterX = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_GAP; - if (!Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)) { - cameraSpace = MUTE_ICON_SIZE + INTER_ICON_GAP; - audioMeterWidth -= cameraSpace; - audioMeterScaleWidth -= cameraSpace; - audioMeterX += cameraSpace; - } - - int audioMeterY; - bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode(); - bool boxed = smallMirrorVisible && - !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror); - if (boxed) { - audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_GAP + MUTE_ICON_PADDING; - } else { - audioMeterY = AUDIO_METER_GAP + MUTE_ICON_PADDING; - } - - const glm::vec4 AUDIO_METER_BLUE = { 0.0, 0.0, 1.0, 1.0 }; - const glm::vec4 AUDIO_METER_GREEN = { 0.0, 1.0, 0.0, 1.0 }; - const glm::vec4 AUDIO_METER_RED = { 1.0, 0.0, 0.0, 1.0 }; - const float CLIPPING_INDICATOR_TIME = 1.0f; - const float AUDIO_METER_AVERAGING = 0.5; - const float LOG2 = log(2.0f); - const float METER_LOUDNESS_SCALE = 2.8f / 5.0f; - const float LOG2_LOUDNESS_FLOOR = 11.0f; - float audioGreenStart = 0.25f * audioMeterScaleWidth; - float audioRedStart = 0.8f * audioMeterScaleWidth; - float audioLevel = 0.0f; - float loudness = audio->getLastInputLoudness() + 1.0f; - - _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.0f - AUDIO_METER_AVERAGING) * loudness; - float log2loudness = logf(_trailingAudioLoudness) / LOG2; - - if (log2loudness <= LOG2_LOUDNESS_FLOOR) { - audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE * audioMeterScaleWidth; - } else { - audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE * audioMeterScaleWidth; - } - if (audioLevel > audioMeterScaleWidth) { - audioLevel = audioMeterScaleWidth; - } - bool isClipping = ((audio->getTimeSinceLastClip() > 0.0f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); - - DependencyManager::get()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, cameraSpace, boxed); - - auto canvasSize = qApp->getCanvasSize(); - DependencyManager::get()->render(canvasSize.x, canvasSize.y); - DependencyManager::get()->render(WHITE_TEXT, canvasSize.x, canvasSize.y); - - audioMeterY += AUDIO_METER_HEIGHT; - - // Draw audio meter background Quad - DependencyManager::get()->renderQuad(audioMeterX, audioMeterY, audioMeterWidth, AUDIO_METER_HEIGHT, - glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); - - if (audioLevel > audioRedStart) { - glm::vec4 quadColor; - if (!isClipping) { - quadColor = AUDIO_METER_RED; - } else { - quadColor = glm::vec4(1, 1, 1, 1); - } - // Draw Red Quad - DependencyManager::get()->renderQuad(audioMeterX + audioRedStart, - audioMeterY, - audioLevel - audioRedStart, - AUDIO_METER_HEIGHT, quadColor, - _audioRedQuad); - - audioLevel = audioRedStart; - } - - if (audioLevel > audioGreenStart) { - glm::vec4 quadColor; - if (!isClipping) { - quadColor = AUDIO_METER_GREEN; - } else { - quadColor = glm::vec4(1, 1, 1, 1); - } - // Draw Green Quad - DependencyManager::get()->renderQuad(audioMeterX + audioGreenStart, - audioMeterY, - audioLevel - audioGreenStart, - AUDIO_METER_HEIGHT, quadColor, - _audioGreenQuad); - - audioLevel = audioGreenStart; - } - - if (audioLevel >= 0) { - glm::vec4 quadColor; - if (!isClipping) { - quadColor = AUDIO_METER_BLUE; - } else { - quadColor = glm::vec4(1, 1, 1, 1); - } - // Draw Blue (low level) quad - DependencyManager::get()->renderQuad(audioMeterX, - audioMeterY, - audioLevel, AUDIO_METER_HEIGHT, quadColor, - _audioBlueQuad); - } - fboViewport(_overlayFramebuffer); -} - void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) { } diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index e50622d122..e7b28d53ed 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -29,8 +29,8 @@ public: private: - void renderAudioMeter(RenderArgs* renderArgs); - void renderCameraToggle(RenderArgs* renderArgs); + //void renderAudioMeter(RenderArgs* renderArgs); + //void renderCameraToggle(RenderArgs* renderArgs); void renderStatsAndLogs(RenderArgs* renderArgs); void renderDomainConnectionStatusBorder(RenderArgs* renderArgs); void renderRearViewToFbo(RenderArgs* renderArgs); diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp new file mode 100644 index 0000000000..4066dab80c --- /dev/null +++ b/interface/src/ui/AvatarInputs.cpp @@ -0,0 +1,133 @@ +// +// Created by Bradley Austin Davis 2015/06/19 +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#include "Application.h" +#include "AvatarInputs.h" + +#include + +#include "Menu.h" +#include "devices/FaceTracker.h" + +HIFI_QML_DEF(AvatarInputs) + + +static AvatarInputs* INSTANCE{ nullptr }; +static const char SETTINGS_GROUP_NAME[] = "Rear View Tools"; +static const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel"; + +static Setting::Handle rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS, 0); + +AvatarInputs* AvatarInputs::getInstance() { + if (!INSTANCE) { + AvatarInputs::registerType(); + AvatarInputs::show(); + Q_ASSERT(INSTANCE); + } + return INSTANCE; +} + +AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) { + INSTANCE = this; + _mirrorZoomed = rearViewZoomLevel.get() != 0; +} + +#define AI_UPDATE(name, src) \ + { \ + auto val = src; \ + if (_##name != val) { \ + _##name = val; \ + emit name##Changed(); \ + } \ + } + +#define AI_UPDATE_FLOAT(name, src, epsilon) \ + { \ + float val = src; \ + if (abs(_##name - val) >= epsilon) { \ + _##name = val; \ + emit name##Changed(); \ + } \ + } + +void AvatarInputs::update() { + if (!Menu::getInstance()) { + return; + } + AI_UPDATE(mirrorVisible, Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode() + && !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)); + AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)); + AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)); + + auto audioIO = DependencyManager::get(); + const float CLIPPING_INDICATOR_TIME = 1.0f; + const float AUDIO_METER_AVERAGING = 0.5; + const float LOG2 = log(2.0f); + const float METER_LOUDNESS_SCALE = 2.8f / 5.0f; + const float LOG2_LOUDNESS_FLOOR = 11.0f; + float audioLevel = 0.0f; + auto audio = DependencyManager::get(); + float loudness = audio->getLastInputLoudness() + 1.0f; + + _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.0f - AUDIO_METER_AVERAGING) * loudness; + + float log2loudness = logf(_trailingAudioLoudness) / LOG2; + + if (log2loudness <= LOG2_LOUDNESS_FLOOR) { + audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE; + } else { + audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE; + } + if (audioLevel > 1.0) { + audioLevel = 1.0; + } + AI_UPDATE_FLOAT(audioLevel, audioLevel, 0.01); + AI_UPDATE(audioClipping, ((audioIO->getTimeSinceLastClip() > 0.0f) && (audioIO->getTimeSinceLastClip() < 1.0f))); + AI_UPDATE(audioMuted, audioIO->isMuted()); + + //// Make muted icon pulsate + //static const float PULSE_MIN = 0.4f; + //static const float PULSE_MAX = 1.0f; + //static const float PULSE_FREQUENCY = 1.0f; // in Hz + //qint64 now = usecTimestampNow(); + //if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) { + // // Prevents t from getting too big, which would diminish glm::cos precision + // _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND); + //} + //float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND; + //float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f; + //iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor; +} + +void AvatarInputs::toggleCameraMute() { + FaceTracker* faceTracker = Application::getInstance()->getSelectedFaceTracker(); + if (faceTracker) { + faceTracker->toggleMute(); + } +} + +void AvatarInputs::toggleAudioMute() { + DependencyManager::get()->toggleMute(); +} + +void AvatarInputs::resetSensors() { + qApp->resetSensors(); +} + +void AvatarInputs::toggleZoom() { + _mirrorZoomed = !_mirrorZoomed; + rearViewZoomLevel.set(_mirrorZoomed ? 0 : 1); + emit mirrorZoomedChanged(); +} + +void AvatarInputs::closeMirror() { + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { + Menu::getInstance()->triggerOption(MenuOption::Mirror); + } +} diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h new file mode 100644 index 0000000000..8ed4e8f163 --- /dev/null +++ b/interface/src/ui/AvatarInputs.h @@ -0,0 +1,59 @@ +// +// Created by Bradley Austin Davis 2015/06/19 +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AvatarInputs_h +#define hifi_AvatarInputs_h + +#include +#include + +#define AI_PROPERTY(type, name, initialValue) \ + Q_PROPERTY(type name READ name NOTIFY name##Changed) \ +public: \ + type name() { return _##name; }; \ +private: \ + type _##name{ initialValue }; + +class AvatarInputs : public QQuickItem { + Q_OBJECT + HIFI_QML_DECL + + AI_PROPERTY(bool, cameraEnabled, false) + AI_PROPERTY(bool, cameraMuted, false) + AI_PROPERTY(bool, audioMuted, false) + AI_PROPERTY(bool, audioClipping, false) + AI_PROPERTY(float, audioLevel, 0) + AI_PROPERTY(bool, mirrorVisible, false) + AI_PROPERTY(bool, mirrorZoomed, true) + +public: + static AvatarInputs* getInstance(); + AvatarInputs(QQuickItem* parent = nullptr); + void update(); + +signals: + void cameraEnabledChanged(); + void cameraMutedChanged(); + void audioMutedChanged(); + void audioClippingChanged(); + void audioLevelChanged(); + void mirrorVisibleChanged(); + void mirrorZoomedChanged(); + +protected: + Q_INVOKABLE void resetSensors(); + Q_INVOKABLE void toggleCameraMute(); + Q_INVOKABLE void toggleAudioMute(); + Q_INVOKABLE void toggleZoom(); + Q_INVOKABLE void closeMirror(); + +private: + float _trailingAudioLoudness{ 0 }; +}; + +#endif // hifi_AvatarInputs_h diff --git a/interface/src/ui/RearMirrorTools.cpp b/interface/src/ui/RearMirrorTools.cpp deleted file mode 100644 index 33f3e1b487..0000000000 --- a/interface/src/ui/RearMirrorTools.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// -// RearMirrorTools.cpp -// interface/src/ui -// -// Created by Stojce Slavkovski on 10/23/2013. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "InterfaceConfig.h" - -#include - -#include -#include -#include - -#include "Application.h" -#include "RearMirrorTools.h" -#include "Util.h" - -const int ICON_SIZE = 24; -const int ICON_PADDING = 5; - -const char SETTINGS_GROUP_NAME[] = "Rear View Tools"; -const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel"; -Setting::Handle RearMirrorTools::rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS, - ZoomLevel::HEAD); - -RearMirrorTools::RearMirrorTools(QRect& bounds) : - _bounds(bounds), - _windowed(false), - _fullScreen(false) -{ - _closeTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/close.svg"); - - _zoomHeadTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/plus.svg"); - _zoomBodyTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/minus.svg"); - - _shrinkIconRect = QRect(ICON_PADDING, ICON_PADDING, ICON_SIZE, ICON_SIZE); - _closeIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE); - _resetIconRect = QRect(_bounds.width() - ICON_SIZE - ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE); - _bodyZoomIconRect = QRect(_bounds.width() - ICON_SIZE - ICON_PADDING, _bounds.bottom() - ICON_PADDING - ICON_SIZE, ICON_SIZE, ICON_SIZE); - _headZoomIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.bottom() - ICON_PADDING - ICON_SIZE, ICON_SIZE, ICON_SIZE); -} - -void RearMirrorTools::render(RenderArgs* renderArgs, bool fullScreen, const QPoint & mousePosition) { - if (fullScreen) { - _fullScreen = true; - displayIcon(QRect(QPoint(), qApp->getDeviceSize()), _shrinkIconRect, _closeTexture); - } else { - // render rear view tools if mouse is in the bounds - _windowed = _bounds.contains(mousePosition); - if (_windowed) { - displayIcon(_bounds, _closeIconRect, _closeTexture); - - ZoomLevel zoomLevel = (ZoomLevel)rearViewZoomLevel.get(); - displayIcon(_bounds, _headZoomIconRect, _zoomHeadTexture, zoomLevel == HEAD); - displayIcon(_bounds, _bodyZoomIconRect, _zoomBodyTexture, zoomLevel == BODY); - } - } -} - -bool RearMirrorTools::mousePressEvent(int x, int y) { - if (_windowed) { - if (_closeIconRect.contains(x, y)) { - _windowed = false; - emit closeView(); - return true; - } - - if (_headZoomIconRect.contains(x, y)) { - rearViewZoomLevel.set(HEAD); - return true; - } - - if (_bodyZoomIconRect.contains(x, y)) { - rearViewZoomLevel.set(BODY); - return true; - } - - if (_bounds.contains(x, y)) { - _windowed = false; - emit restoreView(); - return true; - } - } - - if (_fullScreen) { - if (_shrinkIconRect.contains(x, y)) { - _fullScreen = false; - emit shrinkView(); - return true; - } - } - return false; -} - -void RearMirrorTools::displayIcon(QRect bounds, QRect iconBounds, const gpu::TexturePointer& texture, bool selected) { - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - - glOrtho(bounds.left(), bounds.right(), bounds.bottom(), bounds.top(), -1.0, 1.0); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glEnable(GL_TEXTURE_2D); - - glm::vec4 quadColor; - if (selected) { - quadColor = glm::vec4(.5f, .5f, .5f, 1.0f); - } else { - quadColor = glm::vec4(1, 1, 1, 1); - } - - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(texture)); - - glm::vec2 topLeft(iconBounds.left(), iconBounds.top()); - glm::vec2 bottomRight(iconBounds.right(), iconBounds.bottom()); - static const glm::vec2 texCoordTopLeft(0.0f, 1.0f); - static const glm::vec2 texCoordBottomRight(1.0f, 0.0f); - - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor); - - glPopMatrix(); - - glMatrixMode(GL_MODELVIEW); - - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); -} diff --git a/interface/src/ui/RearMirrorTools.h b/interface/src/ui/RearMirrorTools.h deleted file mode 100644 index c633d72a49..0000000000 --- a/interface/src/ui/RearMirrorTools.h +++ /dev/null @@ -1,56 +0,0 @@ -// -// RearMirrorTools.h -// interface/src/ui -// -// Created by Stojce Slavkovski on 10/23/2013. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_RearMirrorTools_h -#define hifi_RearMirrorTools_h - -#include -#include - -enum ZoomLevel { - HEAD = 0, - BODY = 1 -}; - -class RearMirrorTools : public QObject { - Q_OBJECT -public: - RearMirrorTools(QRect& bounds); - void render(RenderArgs* renderArgs, bool fullScreen, const QPoint & mousePos); - bool mousePressEvent(int x, int y); - - static Setting::Handle rearViewZoomLevel; - -signals: - void closeView(); - void shrinkView(); - void resetView(); - void restoreView(); - -private: - QRect _bounds; - gpu::TexturePointer _closeTexture; - gpu::TexturePointer _zoomBodyTexture; - gpu::TexturePointer _zoomHeadTexture; - - QRect _closeIconRect; - QRect _resetIconRect; - QRect _shrinkIconRect; - QRect _headZoomIconRect; - QRect _bodyZoomIconRect; - - bool _windowed; - bool _fullScreen; - - void displayIcon(QRect bounds, QRect iconBounds, const gpu::TexturePointer& texture, bool selected = false); -}; - -#endif // hifi_RearMirrorTools_h diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index acc9521c72..934f53ffc7 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -1,8 +1,5 @@ // -// Stats.cpp -// interface/src/ui -// -// Created by Lucas Crisman on 22/03/14. +// Created by Bradley Austin Davis 2015/06/17 // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -376,4 +373,4 @@ void Stats::display( } -*/ \ No newline at end of file +*/ diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 58d920d9a5..8582d436af 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -1,8 +1,5 @@ // -// Stats.h -// interface/src/ui -// -// Created by Lucas Crisman on 22/03/14. +// Created by Bradley Austin Davis 2015/06/17 // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. From 3ab8025b821afc46f5136914d33a2618da384e11 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 19 Jun 2015 16:14:05 -0700 Subject: [PATCH 30/35] Responding to Sam's comments --- interface/src/ui/ApplicationOverlay.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 8e5bdca8c0..cae62ef61f 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -108,9 +108,6 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { gpu::Batch batch; auto geometryCache = DependencyManager::get(); geometryCache->useSimpleDrawPipeline(batch); - batch._glDisable(GL_DEPTH_TEST); - batch._glDisable(GL_LIGHTING); - batch._glEnable(GL_BLEND); batch.setProjectionTransform(mat4()); batch.setModelTransform(mat4()); batch._glBindTexture(GL_TEXTURE_2D, _uiTexture); @@ -122,6 +119,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { glm::vec2 size = qApp->getCanvasSize(); + mat4 legacyProjection = glm::ortho(0, size.x, size.y, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP); glMatrixMode(GL_PROJECTION); glPushMatrix(); @@ -140,6 +138,8 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); + + renderArgs->_context->syncCache(); fboViewport(_overlayFramebuffer); } @@ -184,7 +184,7 @@ void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) { void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderArgs) { auto geometryCache = DependencyManager::get(); - std::once_flag once; + static std::once_flag once; std::call_once(once, [&] { QVector points; static const float B = 0.99f; @@ -200,9 +200,6 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderAr gpu::Batch batch; auto geometryCache = DependencyManager::get(); geometryCache->useSimpleDrawPipeline(batch); - batch._glDisable(GL_DEPTH_TEST); - batch._glDisable(GL_LIGHTING); - batch._glEnable(GL_BLEND); batch.setProjectionTransform(mat4()); batch.setModelTransform(mat4()); batch.setUniformTexture(0, DependencyManager::get()->getWhiteTexture()); From 5bbfb97c37351cc3259a418856b386e8aa7b3525 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Sun, 21 Jun 2015 13:31:18 -0700 Subject: [PATCH 31/35] Fixing blurry UI on retina displays --- interface/src/ui/ApplicationOverlay.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index cae62ef61f..c236164957 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -225,8 +225,7 @@ GLuint ApplicationOverlay::getOverlayTexture() { } void ApplicationOverlay::buildFramebufferObject() { - auto canvasSize = qApp->getCanvasSize(); - QSize fboSize = QSize(canvasSize.x, canvasSize.y); + QSize fboSize = qApp->getDeviceSize(); if (_overlayFramebuffer && fboSize == _overlayFramebuffer->size()) { // Already built return; From 012b0973b980c186cc4352da9050c3ffa9638ba5 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 21 Jun 2015 13:41:21 -0700 Subject: [PATCH 32/35] removing cruft --- interface/src/ui/ApplicationOverlay.cpp | 3 --- interface/src/ui/ApplicationOverlay.h | 8 +------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index c236164957..63f68b86ce 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -53,9 +53,6 @@ static void fboViewport(QOpenGLFramebufferObject* fbo) { ApplicationOverlay::ApplicationOverlay() { auto geometryCache = DependencyManager::get(); - _audioRedQuad = geometryCache->allocateID(); - _audioGreenQuad = geometryCache->allocateID(); - _audioBlueQuad = geometryCache->allocateID(); _domainStatusBorder = geometryCache->allocateID(); _magnifierBorder = geometryCache->allocateID(); diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index e7b28d53ed..2f434ed7e1 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -28,9 +28,6 @@ public: GLuint getOverlayTexture(); private: - - //void renderAudioMeter(RenderArgs* renderArgs); - //void renderCameraToggle(RenderArgs* renderArgs); void renderStatsAndLogs(RenderArgs* renderArgs); void renderDomainConnectionStatusBorder(RenderArgs* renderArgs); void renderRearViewToFbo(RenderArgs* renderArgs); @@ -42,10 +39,7 @@ private: float _alpha{ 1.0f }; float _trailingAudioLoudness{ 0.0f }; GLuint _uiTexture{ 0 }; - - int _audioRedQuad; - int _audioGreenQuad; - int _audioBlueQuad; + int _domainStatusBorder; int _magnifierBorder; From 4ccdc78866d61b398adc1360519dd5f1a0644ed3 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Sun, 21 Jun 2015 15:53:24 -0700 Subject: [PATCH 33/35] Fixing frustum perspective bug --- libraries/octree/src/ViewFrustum.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index 815376a456..974b1066a8 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -65,9 +65,10 @@ void ViewFrustum::setProjection(const glm::mat4& projection) { _farClip = -_corners[BOTTOM_LEFT_FAR].z; _aspectRatio = (_corners[TOP_RIGHT_NEAR].x - _corners[BOTTOM_LEFT_NEAR].x) / (_corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_LEFT_NEAR].y); - glm::vec3 right = glm::normalize(glm::vec3(_corners[TOP_RIGHT_NEAR])); - glm::vec3 left = glm::normalize(glm::vec3(_corners[TOP_LEFT_NEAR])); - _fieldOfView = abs(glm::degrees(glm::angle(right, left))); + vec4 top = _inverseProjection * vec4(0, 1, -1, 1); + top /= top.w; + static const vec3 center(0, 0, -1); + _fieldOfView = glm::degrees(2.0f * abs(glm::angle(center, glm::normalize(vec3(top))))); } // ViewFrustum::calculateViewFrustum() From 02510790c38b66f3d4b6cf05f24da2118493f33f Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Sun, 21 Jun 2015 17:16:30 -0700 Subject: [PATCH 34/35] Fixing projection during star rendering --- interface/src/starfield/renderer/Renderer.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/interface/src/starfield/renderer/Renderer.cpp b/interface/src/starfield/renderer/Renderer.cpp index 1ebb4245e2..5fc858d952 100644 --- a/interface/src/starfield/renderer/Renderer.cpp +++ b/interface/src/starfield/renderer/Renderer.cpp @@ -11,6 +11,7 @@ // #include "starfield/renderer/Renderer.h" +#include "Application.h" using namespace starfield; @@ -52,6 +53,10 @@ void Renderer::render(float perspective, float aspect, mat4 const& orientation, matrix[3][1] = 0.0f; matrix[3][2] = 0.0f; + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(glm::value_ptr(qApp->getDisplayViewFrustum()->getProjection())); + glMatrixMode(GL_MODELVIEW); // extract local z vector vec3 ahead = vec3(matrix[2]); @@ -74,6 +79,10 @@ void Renderer::render(float perspective, float aspect, mat4 const& orientation, floodFill(cursor, TileSelection(*this, _tileArray, _tileArray + _tiling.getTileCount(), (TileSelection::Cursor*) _batchCountArray)); this->glBatch(glm::value_ptr(matrix), prepareBatch((unsigned*) _batchOffs, _outIndexPos), alpha); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + } // renderer construction From 1b44c220a729093141cb029ad0bb223b1af2dd03 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 22 Jun 2015 09:42:21 -0700 Subject: [PATCH 35/35] Prep work for procedural stars & stuff --- libraries/octree/src/ViewFrustum.cpp | 8 +- libraries/render-utils/src/GeometryCache.cpp | 5 ++ libraries/render-utils/src/GeometryCache.h | 1 + .../src/standardTransformPNTC.slv | 2 + libraries/render-utils/src/stars.slf | 76 +++++++++++++++++++ 5 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 libraries/render-utils/src/stars.slf diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index 974b1066a8..820ab32b67 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -65,10 +65,10 @@ void ViewFrustum::setProjection(const glm::mat4& projection) { _farClip = -_corners[BOTTOM_LEFT_FAR].z; _aspectRatio = (_corners[TOP_RIGHT_NEAR].x - _corners[BOTTOM_LEFT_NEAR].x) / (_corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_LEFT_NEAR].y); - vec4 top = _inverseProjection * vec4(0, 1, -1, 1); - top /= top.w; - static const vec3 center(0, 0, -1); - _fieldOfView = glm::degrees(2.0f * abs(glm::angle(center, glm::normalize(vec3(top))))); + + glm::vec4 top = _inverseProjection * vec4(0, 1, -1, 1); + top /= top.w; + _fieldOfView = abs(glm::degrees(2.0f * abs(glm::angle(vec3(0, 0, -1), glm::normalize(vec3(top)))))); } // ViewFrustum::calculateViewFrustum() diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 6ebe4dd46c..3e7e9a0adf 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1185,6 +1185,11 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co batch.draw(gpu::QUADS, 4, 0); } +void GeometryCache::renderUnitCube(gpu::Batch& batch) { + static const glm::vec4 color(1); + renderSolidCube(batch, 1, color); +} + void GeometryCache::renderUnitQuad(const glm::vec4& color, int id) { gpu::Batch batch; renderUnitQuad(batch, color, id); diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 2155586f4b..f97ab8a773 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -155,6 +155,7 @@ public: void renderBevelCornersRect(int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID); void renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID); + void renderUnitCube(gpu::Batch& batch); void renderUnitQuad(const glm::vec4& color = glm::vec4(1), int id = UNKNOWN_ID); void renderUnitQuad(gpu::Batch& batch, const glm::vec4& color = glm::vec4(1), int id = UNKNOWN_ID); diff --git a/libraries/render-utils/src/standardTransformPNTC.slv b/libraries/render-utils/src/standardTransformPNTC.slv index fd2c28049f..93abea267c 100644 --- a/libraries/render-utils/src/standardTransformPNTC.slv +++ b/libraries/render-utils/src/standardTransformPNTC.slv @@ -16,6 +16,7 @@ <$declareStandardTransform()$> +varying vec3 varPosition; varying vec3 varNormal; varying vec2 varTexcoord; varying vec4 varColor; @@ -30,4 +31,5 @@ void main(void) { <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> <$transformModelToEyeDir(cam, obj, gl_Normal, varNormal)$> varNormal = normalize(varNormal); + varPosition = gl_Vertex.xyz; } \ No newline at end of file diff --git a/libraries/render-utils/src/stars.slf b/libraries/render-utils/src/stars.slf new file mode 100644 index 0000000000..f0f81cb5f4 --- /dev/null +++ b/libraries/render-utils/src/stars.slf @@ -0,0 +1,76 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// stars.frag +// fragment shader +// +// Created by Bradley Austin Davis on 2015/06/19 + +varying vec2 varTexcoord; +varying vec3 varNomral; +varying vec3 varPosition; + + +const int star_iterations = 14; +const float time_scale = 0.2; +const vec3 col_star = vec3( 1.0, 0.7, 0.5 ); + +float hash( float n ) { return fract(sin(n)*123.456789); } + +vec2 rotate( in vec2 uv, float a) +{ + float c = cos( a ); + float s = sin( a ); + return vec2( c * uv.x - s * uv.y, s * uv.x + c * uv.y ); +} + +float noise( in vec3 p ) +{ + vec3 fl = floor( p ); + vec3 fr = fract( p ); + fr = fr * fr * ( 3.0 - 2.0 * fr ); + + float n = fl.x + fl.y * 157.0 + 113.0 * fl.z; + return mix( mix( mix( hash( n + 0.0), hash( n + 1.0 ), fr.x ), + mix( hash( n + 157.0), hash( n + 158.0 ), fr.x ), fr.y ), + mix( mix( hash( n + 113.0), hash( n + 114.0 ), fr.x ), + mix( hash( n + 270.0), hash( n + 271.0 ), fr.x ), fr.y ), fr.z ); +} + +float fbm( in vec2 p, float t ) +{ + float f; + f = 0.5000 * noise( vec3( p, t ) ); p *= 2.1; + f += 0.2500 * noise( vec3( p, t ) ); p *= 2.2; + f += 0.1250 * noise( vec3( p, t ) ); p *= 2.3; + f += 0.0625 * noise( vec3( p, t ) ); + return f; +} + +vec3 doBackgroundStars( in vec3 dir ) +{ + vec3 n = abs( dir ); + vec2 uv = ( n.x > n.y && n.x > n.z ) ? dir.yz / dir.x: + ( n.y > n.x && n.y > n.z ) ? dir.zx / dir.y: + dir.xy / dir.z; + + float f = 0.0; + + for( int i = 0 ; i < star_iterations; ++i ) + { + uv = rotate( 1.07 * uv + vec2( 0.7 ), 0.5 ); + + float t = 10. * uv.x * uv.y; + vec2 u = cos( 100. * uv ) * fbm( 10. * uv, 0.0 ); + f += smoothstep( 0.5, 0.55, u.x * u.y ) * ( 0.25 * sin( t ) + 0.75 ); + } + + return f * col_star; +} + +void main(void) { + vec3 c = doBackgroundStars( normalize(varPosition) ); + c = pow( c, vec3( 0.4545 ) ); + gl_FragColor = vec4( c, 1.0 ); +} +