From 18d723b6b43b62f57ddeaf362bcc95ebf1afa261 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 10 Oct 2017 16:17:39 -0700 Subject: [PATCH 01/62] div by zero fixes, detected by address sanitizer --- interface/src/LODManager.cpp | 1 + libraries/animation/src/Rig.cpp | 7 ++++++- .../avatars-renderer/src/avatars-renderer/Avatar.cpp | 3 ++- libraries/shared/src/MovingMinMaxAvg.h | 8 ++++++-- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index d3c8746e16..199f3ea2c6 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -54,6 +54,7 @@ void LODManager::autoAdjustLOD(float batchTime, float engineRunTime, float delta float renderTime = batchTime + OVERLAY_AND_SWAP_TIME_BUDGET; float maxTime = glm::max(renderTime, engineRunTime); const float BLEND_TIMESCALE = 0.3f; // sec + const float safeDeltaTime = (deltaTime == 0.0f) ? 0.001f : deltaTime; float blend = BLEND_TIMESCALE / deltaTimeSec; if (blend > 1.0f) { blend = 1.0f; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0897c26a12..83d258fb08 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -38,7 +38,12 @@ static std::mutex rigRegistryMutex; static bool isEqual(const glm::vec3& u, const glm::vec3& v) { const float EPSILON = 0.0001f; - return glm::length(u - v) / glm::length(u) <= EPSILON; + float uLen = glm::length(u); + if (uLen == 0.0f) { + return glm::length(v) <= EPSILON; + } else { + return glm::length(u - v) / glm::length(u) <= EPSILON; + } } static bool isEqual(const glm::quat& p, const glm::quat& q) { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 87d4a2d343..a52582ff45 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -446,7 +446,8 @@ void Avatar::applyPositionDelta(const glm::vec3& delta) { void Avatar::measureMotionDerivatives(float deltaTime) { PerformanceTimer perfTimer("derivatives"); // linear - float invDeltaTime = 1.0f / deltaTime; + const float safeDeltaTime = (deltaTime == 0.0f) ? 0.001f : deltaTime; + float invDeltaTime = 1.0f / safeDeltaTime; // Floating point error prevents us from computing velocity in a naive way // (e.g. vel = (pos - oldPos) / dt) so instead we use _positionOffsetAccumulator. glm::vec3 velocity = _positionDeltaAccumulator * invDeltaTime; diff --git a/libraries/shared/src/MovingMinMaxAvg.h b/libraries/shared/src/MovingMinMaxAvg.h index 580baf7317..782b0dc523 100644 --- a/libraries/shared/src/MovingMinMaxAvg.h +++ b/libraries/shared/src/MovingMinMaxAvg.h @@ -59,8 +59,12 @@ public: _max = other._max; } double totalSamples = _samples + other._samples; - _average = _average * ((double)_samples / totalSamples) - + other._average * ((double)other._samples / totalSamples); + if (totalSamples > 0) { + _average = _average * ((double)_samples / totalSamples) + + other._average * ((double)other._samples / totalSamples); + } else { + _average = 0.0f; + } _samples += other._samples; } From cbb0d27f684b4e0f3c091c6a9268c1e2309426f7 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 10 Oct 2017 16:20:52 -0700 Subject: [PATCH 02/62] div by zero fix --- interface/src/LODManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 199f3ea2c6..4bdb13545e 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -55,7 +55,7 @@ void LODManager::autoAdjustLOD(float batchTime, float engineRunTime, float delta float maxTime = glm::max(renderTime, engineRunTime); const float BLEND_TIMESCALE = 0.3f; // sec const float safeDeltaTime = (deltaTime == 0.0f) ? 0.001f : deltaTime; - float blend = BLEND_TIMESCALE / deltaTimeSec; + float blend = BLEND_TIMESCALE / safeDeltaTime; if (blend > 1.0f) { blend = 1.0f; } From fe57a209792e06952da19999e3aeb277a13d3822 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 10 Oct 2017 16:22:49 -0700 Subject: [PATCH 03/62] div by zero fix --- libraries/animation/src/Rig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 83d258fb08..bd19dcc0e8 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -42,7 +42,7 @@ static bool isEqual(const glm::vec3& u, const glm::vec3& v) { if (uLen == 0.0f) { return glm::length(v) <= EPSILON; } else { - return glm::length(u - v) / glm::length(u) <= EPSILON; + return (glm::length(u - v) / uLen) <= EPSILON; } } From cb01c5cada93ed0d4e02f250c7a9047efcc1d227 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 10 Oct 2017 16:30:11 -0700 Subject: [PATCH 04/62] compile fix --- interface/src/LODManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 4bdb13545e..8f01296e1c 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -54,7 +54,7 @@ void LODManager::autoAdjustLOD(float batchTime, float engineRunTime, float delta float renderTime = batchTime + OVERLAY_AND_SWAP_TIME_BUDGET; float maxTime = glm::max(renderTime, engineRunTime); const float BLEND_TIMESCALE = 0.3f; // sec - const float safeDeltaTime = (deltaTime == 0.0f) ? 0.001f : deltaTime; + const float safeDeltaTime = (deltaTimeSec == 0.0f) ? 0.001f : deltaTimeSec; float blend = BLEND_TIMESCALE / safeDeltaTime; if (blend > 1.0f) { blend = 1.0f; From 049e3d47b67802e99e0de9215374f08131873683 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 13 Oct 2017 10:23:42 +0200 Subject: [PATCH 05/62] Preparing for support of multiple concurrent outlines by adding mask id buffer --- libraries/gpu-gl/src/gpu/gl/GLShared.h | 1 + libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp | 7 ++ libraries/gpu/src/gpu/Format.cpp | 2 + libraries/gpu/src/gpu/Format.h | 2 + libraries/render-utils/src/OutlineEffect.cpp | 113 +++++++++++++----- libraries/render-utils/src/OutlineEffect.h | 29 +++-- 6 files changed, 110 insertions(+), 44 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.h b/libraries/gpu-gl/src/gpu/gl/GLShared.h index 1b898e5c22..a1cf27afa6 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShared.h +++ b/libraries/gpu-gl/src/gpu/gl/GLShared.h @@ -110,6 +110,7 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = { GL_SHORT, GL_UNSIGNED_SHORT, GL_BYTE, + GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE }; diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp index 192a82dafc..27319e1696 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp @@ -212,6 +212,9 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { case gpu::NUINT8: result = GL_RGBA8; break; + case gpu::NUINT2: + result = GL_RGBA2; + break; case gpu::NINT8: result = GL_RGBA8_SNORM; break; @@ -660,6 +663,10 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.format = GL_RGBA; texel.internalFormat = GL_RGBA8_SNORM; break; + case gpu::NUINT2: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA2; + break; case gpu::NUINT32: case gpu::NINT32: case gpu::COMPRESSED: diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index 7efe4d3ed6..3b153097cf 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -19,6 +19,8 @@ const Element Element::COLOR_SRGBA_32{ VEC4, NUINT8, SRGBA }; const Element Element::COLOR_BGRA_32{ VEC4, NUINT8, BGRA }; const Element Element::COLOR_SBGRA_32{ VEC4, NUINT8, SBGRA }; +const Element Element::COLOR_RGBA_2{ VEC4, NUINT2, RGBA }; + const Element Element::COLOR_COMPRESSED_RED{ TILE4x4, COMPRESSED, COMPRESSED_BC4_RED }; const Element Element::COLOR_COMPRESSED_SRGB { TILE4x4, COMPRESSED, COMPRESSED_BC1_SRGB }; const Element Element::COLOR_COMPRESSED_SRGBA_MASK { TILE4x4, COMPRESSED, COMPRESSED_BC1_SRGBA }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 0654b23581..9d5d2fc49d 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -38,6 +38,7 @@ enum Type : uint8_t { NUINT16, NINT8, NUINT8, + NUINT2, COMPRESSED, @@ -309,6 +310,7 @@ public: static const Element COLOR_SRGBA_32; static const Element COLOR_BGRA_32; static const Element COLOR_SBGRA_32; + static const Element COLOR_RGBA_2; static const Element COLOR_R11G11B10; static const Element COLOR_RGB9E5; static const Element COLOR_COMPRESSED_RED; diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index d5b3b1c3bb..375ba462b9 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -27,12 +27,10 @@ using namespace render; -extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state); - -OutlineFramebuffer::OutlineFramebuffer() { +OutlineRessources::OutlineRessources() { } -void OutlineFramebuffer::update(const gpu::TexturePointer& colorBuffer) { +void OutlineRessources::update(const gpu::TexturePointer& colorBuffer) { // If the depth buffer or size changed, we need to delete our FBOs and recreate them at the // new correct dimensions. if (_depthTexture) { @@ -44,37 +42,48 @@ void OutlineFramebuffer::update(const gpu::TexturePointer& colorBuffer) { } } -void OutlineFramebuffer::clear() { - _depthFramebuffer.reset(); +void OutlineRessources::clear() { + _frameBuffer.reset(); _depthTexture.reset(); + _idTexture.reset(); } -void OutlineFramebuffer::allocate() { +void OutlineRessources::allocate() { auto width = _frameSize.x; auto height = _frameSize.y; - auto format = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); + auto depthFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); - _depthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(format, width, height)); - _depthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("outlineDepth")); - _depthFramebuffer->setDepthStencilBuffer(_depthTexture, format); + _idTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_2, width, height)); + _depthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, width, height)); + + _frameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("outlineDepth")); + _frameBuffer->setDepthStencilBuffer(_depthTexture, depthFormat); + _frameBuffer->setRenderBuffer(0, _idTexture); } -gpu::FramebufferPointer OutlineFramebuffer::getDepthFramebuffer() { - if (!_depthFramebuffer) { +gpu::FramebufferPointer OutlineRessources::getFramebuffer() { + if (!_frameBuffer) { allocate(); } - return _depthFramebuffer; + return _frameBuffer; } -gpu::TexturePointer OutlineFramebuffer::getDepthTexture() { +gpu::TexturePointer OutlineRessources::getDepthTexture() { if (!_depthTexture) { allocate(); } return _depthTexture; } -void DrawOutlineDepth::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output) { +gpu::TexturePointer OutlineRessources::getIDTexture() { + if (!_idTexture) { + allocate(); + } + return _idTexture; +} + +void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); auto& inShapes = inputs.get0(); @@ -84,19 +93,19 @@ void DrawOutlineDepth::run(const render::RenderContextPointer& renderContext, co RenderArgs* args = renderContext->args; ShapeKey::Builder defaultKeyBuilder; - if (!_outlineFramebuffer) { - _outlineFramebuffer = std::make_shared(); + if (!_outlineRessources) { + _outlineRessources = std::make_shared(); } - _outlineFramebuffer->update(deferredFrameBuffer->getDeferredColorTexture()); + _outlineRessources->update(deferredFrameBuffer->getDeferredColorTexture()); gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; - batch.setFramebuffer(_outlineFramebuffer->getDepthFramebuffer()); + batch.setFramebuffer(_outlineRessources->getFramebuffer()); // Clear it batch.clearFramebuffer( - gpu::Framebuffer::BUFFER_DEPTH, - vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, false); + gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH, + vec4(0.0f, 0.0f, 0.0f, 0.0f), 1.0f, 0, false); // Setup camera, projection and viewport for all items batch.setViewportTransform(args->_viewport); @@ -110,14 +119,14 @@ void DrawOutlineDepth::run(const render::RenderContextPointer& renderContext, co batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - auto shadowPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder); - auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned()); + auto maskPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder); + auto maskSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned()); std::vector skinnedShapeKeys{}; // Iterate through all inShapes and render the unskinned - args->_shapePipeline = shadowPipeline; - batch.setPipeline(shadowPipeline->pipeline); + args->_shapePipeline = maskPipeline; + batch.setPipeline(maskPipeline->pipeline); for (auto items : inShapes) { if (items.first.isSkinned()) { skinnedShapeKeys.push_back(items.first); @@ -128,8 +137,8 @@ void DrawOutlineDepth::run(const render::RenderContextPointer& renderContext, co } // Reiterate to render the skinned - args->_shapePipeline = shadowSkinnedPipeline; - batch.setPipeline(shadowSkinnedPipeline->pipeline); + args->_shapePipeline = maskSkinnedPipeline; + batch.setPipeline(maskSkinnedPipeline->pipeline); for (const auto& key : skinnedShapeKeys) { renderItems(renderContext, inShapes.at(key)); } @@ -138,7 +147,7 @@ void DrawOutlineDepth::run(const render::RenderContextPointer& renderContext, co args->_batch = nullptr; }); - output = _outlineFramebuffer; + output = _outlineRessources; } else { output = nullptr; } @@ -348,7 +357,7 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende state->setDepthTest(true, true, gpu::LESS_EQUAL); state->setColorWriteMask(false, false, false, false); - initZPassPipelines(*shapePlumberZPass, state); + initMaskPipelines(*shapePlumberZPass, state); } const auto outlinedItemIDs = task.addJob("OutlineMetaToSubItemIDs", selectedMetas); @@ -359,8 +368,8 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende const auto sortedShapes = task.addJob("OutlineDepthSort", sortedPipelines); // Draw depth of outlined objects in separate buffer - const auto drawOutlineDepthInputs = DrawOutlineDepth::Inputs(sortedShapes, sceneFrameBuffer).asVarying(); - const auto outlinedFrameBuffer = task.addJob("OutlineDepth", drawOutlineDepthInputs, shapePlumberZPass); + const auto drawMaskInputs = DrawOutlineMask::Inputs(sortedShapes, sceneFrameBuffer).asVarying(); + const auto outlinedFrameBuffer = task.addJob("OutlineMask", drawMaskInputs, shapePlumberZPass); // Draw outline const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlinedFrameBuffer, sceneFrameBuffer, primaryFramebuffer).asVarying(); @@ -369,3 +378,43 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende // Debug outline task.addJob("OutlineDebug", outlinedFrameBuffer); } + +#include "model_shadow_vert.h" +#include "model_shadow_fade_vert.h" +#include "skin_model_shadow_vert.h" +#include "skin_model_shadow_fade_vert.h" + +#include "model_shadow_frag.h" +#include "model_shadow_fade_frag.h" +#include "skin_model_shadow_frag.h" +#include "skin_model_shadow_fade_frag.h" + +void DrawOutlineTask::initMaskPipelines(render::ShapePlumber& shapePlumber, gpu::StatePointer state) { + auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); + auto modelPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); + gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(modelVertex, modelPixel); + shapePlumber.addPipeline( + ShapeKey::Filter::Builder().withoutSkinned().withoutFade(), + modelProgram, state); + + auto skinVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert)); + auto skinPixel = gpu::Shader::createPixel(std::string(skin_model_shadow_frag)); + gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(skinVertex, skinPixel); + shapePlumber.addPipeline( + ShapeKey::Filter::Builder().withSkinned().withoutFade(), + skinProgram, state); + + auto modelFadeVertex = gpu::Shader::createVertex(std::string(model_shadow_fade_vert)); + auto modelFadePixel = gpu::Shader::createPixel(std::string(model_shadow_fade_frag)); + gpu::ShaderPointer modelFadeProgram = gpu::Shader::createProgram(modelFadeVertex, modelFadePixel); + shapePlumber.addPipeline( + ShapeKey::Filter::Builder().withoutSkinned().withFade(), + modelFadeProgram, state); + + auto skinFadeVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_fade_vert)); + auto skinFadePixel = gpu::Shader::createPixel(std::string(skin_model_shadow_fade_frag)); + gpu::ShaderPointer skinFadeProgram = gpu::Shader::createProgram(skinFadeVertex, skinFadePixel); + shapePlumber.addPipeline( + ShapeKey::Filter::Builder().withSkinned().withFade(), + skinFadeProgram, state); +} diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index f88092429f..b04f102a83 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -16,11 +16,12 @@ #include "DeferredFramebuffer.h" #include "DeferredFrameTransform.h" -class OutlineFramebuffer { +class OutlineRessources { public: - OutlineFramebuffer(); + OutlineRessources(); - gpu::FramebufferPointer getDepthFramebuffer(); + gpu::FramebufferPointer getFramebuffer(); + gpu::TexturePointer getIDTexture(); gpu::TexturePointer getDepthTexture(); // Update the source framebuffer size which will drive the allocation of all the other resources. @@ -32,30 +33,31 @@ protected: void clear(); void allocate(); - gpu::FramebufferPointer _depthFramebuffer; + gpu::FramebufferPointer _frameBuffer; gpu::TexturePointer _depthTexture; + gpu::TexturePointer _idTexture; glm::ivec2 _frameSize; }; -using OutlineFramebufferPointer = std::shared_ptr; +using OutlineRessourcesPointer = std::shared_ptr; -class DrawOutlineDepth { +class DrawOutlineMask { public: using Inputs = render::VaryingSet2; // Output will contain outlined objects only z-depth texture and the input primary buffer but without the primary depth buffer - using Outputs = OutlineFramebufferPointer; - using JobModel = render::Job::ModelIO; + using Outputs = OutlineRessourcesPointer; + using JobModel = render::Job::ModelIO; - DrawOutlineDepth(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + DrawOutlineMask(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output); protected: render::ShapePlumberPointer _shapePlumber; - OutlineFramebufferPointer _outlineFramebuffer; + OutlineRessourcesPointer _outlineRessources; }; class DrawOutlineConfig : public render::Job::Config { @@ -93,7 +95,7 @@ signals: class DrawOutline { public: - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet4; using Config = DrawOutlineConfig; using JobModel = render::Job::ModelI; @@ -146,7 +148,7 @@ signals: class DebugOutline { public: - using Inputs = OutlineFramebufferPointer; + using Inputs = OutlineRessourcesPointer; using Config = DebugOutlineConfig; using JobModel = render::Job::ModelI; @@ -176,6 +178,9 @@ public: void configure(const Config& config); void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); +private: + + static void initMaskPipelines(render::ShapePlumber& plumber, gpu::StatePointer state); }; #endif // hifi_render_utils_OutlineEffect_h From 2658d658148ce359ebd9fd6903bafce0747dcc5d Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 13 Oct 2017 12:02:21 +0200 Subject: [PATCH 06/62] Preparing for outline color mask --- libraries/render-utils/src/OutlineEffect.cpp | 145 ++++++++++++------ libraries/render-utils/src/OutlineEffect.h | 20 ++- .../render-utils/src/RenderDeferredTask.cpp | 2 +- libraries/render-utils/src/model_outline.slf | 21 +++ .../render-utils/src/model_outline_fade.slf | 31 ++++ .../developer/utilities/render/outline.qml | 6 +- 6 files changed, 166 insertions(+), 59 deletions(-) create mode 100644 libraries/render-utils/src/model_outline.slf create mode 100644 libraries/render-utils/src/model_outline_fade.slf diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index 375ba462b9..68f1759437 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -76,7 +76,7 @@ gpu::TexturePointer OutlineRessources::getDepthTexture() { return _depthTexture; } -gpu::TexturePointer OutlineRessources::getIDTexture() { +gpu::TexturePointer OutlineRessources::getIdTexture() { if (!_idTexture) { allocate(); } @@ -123,10 +123,13 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con auto maskSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned()); std::vector skinnedShapeKeys{}; + auto colorLoc = maskPipeline.get()->pipeline->getProgram()->getUniforms().findLocation("color"); + glm::vec4 idColor{ 1.0f, 0.0f, 0.0f, 0.0f }; // Iterate through all inShapes and render the unskinned args->_shapePipeline = maskPipeline; batch.setPipeline(maskPipeline->pipeline); + batch._glUniform4f(colorLoc, idColor.r, idColor.g, idColor.b, idColor.a); for (auto items : inShapes) { if (items.first.isSkinned()) { skinnedShapeKeys.push_back(items.first); @@ -136,9 +139,11 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con } } + colorLoc = maskSkinnedPipeline.get()->pipeline->getProgram()->getUniforms().findLocation("color"); // Reiterate to render the skinned args->_shapePipeline = maskSkinnedPipeline; batch.setPipeline(maskSkinnedPipeline->pipeline); + batch._glUniform4f(colorLoc, idColor.r, idColor.g, idColor.b, idColor.a); for (const auto& key : skinnedShapeKeys) { renderItems(renderContext, inShapes.at(key)); } @@ -250,24 +255,26 @@ const gpu::PipelinePointer& DrawOutline::getPipeline(bool isFilled) { } DebugOutline::DebugOutline() { - _geometryId = DependencyManager::get()->allocateID(); + _geometryDepthId = DependencyManager::get()->allocateID(); + _geometryColorId = DependencyManager::get()->allocateID(); } DebugOutline::~DebugOutline() { auto geometryCache = DependencyManager::get(); if (geometryCache) { - geometryCache->releaseID(_geometryId); + geometryCache->releaseID(_geometryDepthId); + geometryCache->releaseID(_geometryColorId); } } void DebugOutline::configure(const Config& config) { - _isDisplayDepthEnabled = config.viewOutlinedDepth; + _isDisplayEnabled = config.viewMask; } void DebugOutline::run(const render::RenderContextPointer& renderContext, const Inputs& input) { const auto outlineFramebuffer = input; - if (_isDisplayDepthEnabled && outlineFramebuffer) { + if (_isDisplayEnabled && outlineFramebuffer) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); RenderArgs* args = renderContext->args; @@ -286,52 +293,101 @@ void DebugOutline::run(const render::RenderContextPointer& renderContext, const batch.setViewTransform(viewMat, true); batch.setModelTransform(Transform()); - batch.setPipeline(getDebugPipeline()); - batch.setResourceTexture(0, outlineFramebuffer->getDepthTexture()); + const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); - const glm::vec4 color(1.0f, 0.5f, 0.2f, 1.0f); - const glm::vec2 bottomLeft(-1.0f, -1.0f); - const glm::vec2 topRight(1.0f, 1.0f); - geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryId); + batch.setPipeline(getDepthPipeline()); + batch.setResourceTexture(0, outlineFramebuffer->getDepthTexture()); + { + const glm::vec2 bottomLeft(-1.0f, -1.0f); + const glm::vec2 topRight(0.0f, 1.0f); + geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryDepthId); + } + + batch.setPipeline(getIdPipeline()); + batch.setResourceTexture(0, outlineFramebuffer->getIdTexture()); + { + const glm::vec2 bottomLeft(0.0f, -1.0f); + const glm::vec2 topRight(1.0f, 1.0f); + geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryColorId); + } batch.setResourceTexture(0, nullptr); }); } } -const gpu::PipelinePointer& DebugOutline::getDebugPipeline() { - if (!_debugPipeline) { - static const std::string VERTEX_SHADER{ debug_deferred_buffer_vert }; - static const std::string FRAGMENT_SHADER{ debug_deferred_buffer_frag }; - static const std::string SOURCE_PLACEHOLDER{ "//SOURCE_PLACEHOLDER" }; - static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER.find(SOURCE_PLACEHOLDER); - Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO, - "Could not find source placeholder"); - static const std::string DEFAULT_DEPTH_SHADER{ +void DebugOutline::initializePipelines() { + static const std::string VERTEX_SHADER{ debug_deferred_buffer_vert }; + static const std::string FRAGMENT_SHADER{ debug_deferred_buffer_frag }; + static const std::string SOURCE_PLACEHOLDER{ "//SOURCE_PLACEHOLDER" }; + static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER.find(SOURCE_PLACEHOLDER); + Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO, + "Could not find source placeholder"); + + auto state = std::make_shared(); + state->setDepthTest(gpu::State::DepthTest(false)); + + const auto vs = gpu::Shader::createVertex(VERTEX_SHADER); + + // Depth shader + { + static const std::string DEPTH_SHADER{ "vec4 getFragmentColor() {" " float Zdb = texelFetch(depthMap, ivec2(gl_FragCoord.xy), 0).x;" " Zdb = 1.0-(1.0-Zdb)*100;" - " return vec4(Zdb, Zdb, Zdb, 1.0);" - " }" + " return vec4(Zdb, Zdb, Zdb, 1.0); " + "}" }; - auto bakedFragmentShader = FRAGMENT_SHADER; - bakedFragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), DEFAULT_DEPTH_SHADER); + auto fragmentShader = FRAGMENT_SHADER; + fragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), DEPTH_SHADER); - static const auto vs = gpu::Shader::createVertex(VERTEX_SHADER); - const auto ps = gpu::Shader::createPixel(bakedFragmentShader); + const auto ps = gpu::Shader::createPixel(fragmentShader); const auto program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding("depthMap", 0)); gpu::Shader::makeProgram(*program, slotBindings); - auto state = std::make_shared(); - state->setDepthTest(gpu::State::DepthTest(false)); - _debugPipeline = gpu::Pipeline::create(program, state); + _depthPipeline = gpu::Pipeline::create(program, state); } - return _debugPipeline; + // ID shader + { + static const std::string ID_SHADER{ + "vec4 getFragmentColor() {" + " return texelFetch(albedoMap, ivec2(gl_FragCoord.xy), 0); " + "}" + }; + + auto fragmentShader = FRAGMENT_SHADER; + fragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), ID_SHADER); + + const auto ps = gpu::Shader::createPixel(fragmentShader); + const auto program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding("albedoMap", 0)); + gpu::Shader::makeProgram(*program, slotBindings); + + _idPipeline = gpu::Pipeline::create(program, state); + } +} + +const gpu::PipelinePointer& DebugOutline::getDepthPipeline() { + if (!_depthPipeline) { + initializePipelines(); + } + + return _depthPipeline; +} + +const gpu::PipelinePointer& DebugOutline::getIdPipeline() { + if (!_idPipeline) { + initializePipelines(); + } + + return _idPipeline; } DrawOutlineTask::DrawOutlineTask() { @@ -345,19 +401,18 @@ void DrawOutlineTask::configure(const Config& config) { void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { const auto input = inputs.get(); const auto selectedMetas = inputs.getN(0); - const auto shapePlumber = input.get1(); - const auto sceneFrameBuffer = inputs.getN(2); - const auto primaryFramebuffer = inputs.getN(3); - const auto deferredFrameTransform = inputs.getN(4); + const auto sceneFrameBuffer = inputs.getN(1); + const auto primaryFramebuffer = inputs.getN(2); + const auto deferredFrameTransform = inputs.getN(3); // Prepare the ShapePipeline - ShapePlumberPointer shapePlumberZPass = std::make_shared(); + ShapePlumberPointer shapePlumber = std::make_shared(); { auto state = std::make_shared(); state->setDepthTest(true, true, gpu::LESS_EQUAL); state->setColorWriteMask(false, false, false, false); - initMaskPipelines(*shapePlumberZPass, state); + initMaskPipelines(*shapePlumber, state); } const auto outlinedItemIDs = task.addJob("OutlineMetaToSubItemIDs", selectedMetas); @@ -369,7 +424,7 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende // Draw depth of outlined objects in separate buffer const auto drawMaskInputs = DrawOutlineMask::Inputs(sortedShapes, sceneFrameBuffer).asVarying(); - const auto outlinedFrameBuffer = task.addJob("OutlineMask", drawMaskInputs, shapePlumberZPass); + const auto outlinedFrameBuffer = task.addJob("OutlineMask", drawMaskInputs, shapePlumber); // Draw outline const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlinedFrameBuffer, sceneFrameBuffer, primaryFramebuffer).asVarying(); @@ -384,36 +439,32 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende #include "skin_model_shadow_vert.h" #include "skin_model_shadow_fade_vert.h" -#include "model_shadow_frag.h" -#include "model_shadow_fade_frag.h" -#include "skin_model_shadow_frag.h" -#include "skin_model_shadow_fade_frag.h" +#include "model_outline_frag.h" +#include "model_outline_fade_frag.h" void DrawOutlineTask::initMaskPipelines(render::ShapePlumber& shapePlumber, gpu::StatePointer state) { auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); - auto modelPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); + auto modelPixel = gpu::Shader::createPixel(std::string(model_outline_frag)); gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(modelVertex, modelPixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withoutSkinned().withoutFade(), modelProgram, state); auto skinVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert)); - auto skinPixel = gpu::Shader::createPixel(std::string(skin_model_shadow_frag)); - gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(skinVertex, skinPixel); + gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(skinVertex, modelPixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withSkinned().withoutFade(), skinProgram, state); auto modelFadeVertex = gpu::Shader::createVertex(std::string(model_shadow_fade_vert)); - auto modelFadePixel = gpu::Shader::createPixel(std::string(model_shadow_fade_frag)); + auto modelFadePixel = gpu::Shader::createPixel(std::string(model_outline_fade_frag)); gpu::ShaderPointer modelFadeProgram = gpu::Shader::createProgram(modelFadeVertex, modelFadePixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withoutSkinned().withFade(), modelFadeProgram, state); auto skinFadeVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_fade_vert)); - auto skinFadePixel = gpu::Shader::createPixel(std::string(skin_model_shadow_fade_frag)); - gpu::ShaderPointer skinFadeProgram = gpu::Shader::createProgram(skinFadeVertex, skinFadePixel); + gpu::ShaderPointer skinFadeProgram = gpu::Shader::createProgram(skinFadeVertex, modelFadePixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withSkinned().withFade(), skinFadeProgram, state); diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index b04f102a83..511ed4a992 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -21,7 +21,7 @@ public: OutlineRessources(); gpu::FramebufferPointer getFramebuffer(); - gpu::TexturePointer getIDTexture(); + gpu::TexturePointer getIdTexture(); gpu::TexturePointer getDepthTexture(); // Update the source framebuffer size which will drive the allocation of all the other resources. @@ -136,11 +136,11 @@ private: class DebugOutlineConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(bool viewOutlinedDepth MEMBER viewOutlinedDepth NOTIFY dirty) + Q_PROPERTY(bool viewMask MEMBER viewMask NOTIFY dirty) public: - bool viewOutlinedDepth{ false }; + bool viewMask{ false }; signals: void dirty(); @@ -160,16 +160,20 @@ public: private: - const gpu::PipelinePointer& getDebugPipeline(); + gpu::PipelinePointer _depthPipeline; + gpu::PipelinePointer _idPipeline; + int _geometryDepthId{ 0 }; + int _geometryColorId{ 0 }; + bool _isDisplayEnabled{ false }; - gpu::PipelinePointer _debugPipeline; - int _geometryId{ 0 }; - bool _isDisplayDepthEnabled{ false }; + const gpu::PipelinePointer& getDepthPipeline(); + const gpu::PipelinePointer& getIdPipeline(); + void initializePipelines(); }; class DrawOutlineTask { public: - using Inputs = render::VaryingSet5; + using Inputs = render::VaryingSet4; using Config = render::Task::Config; using JobModel = render::Task::ModelI; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 45e6fd4ba4..1739f67748 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -173,7 +173,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("ToneMapping", toneMappingInputs); const auto outlineRangeTimer = task.addJob("BeginOutlineRangeTimer", "Outline"); - const auto outlineInputs = DrawOutlineTask::Inputs(selectedItems, shapePlumber, deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying(); + const auto outlineInputs = DrawOutlineTask::Inputs(selectedItems, deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying(); task.addJob("DrawOutline", outlineInputs); task.addJob("EndOutlineRangeTimer", outlineRangeTimer); diff --git a/libraries/render-utils/src/model_outline.slf b/libraries/render-utils/src/model_outline.slf new file mode 100644 index 0000000000..8c11b2b295 --- /dev/null +++ b/libraries/render-utils/src/model_outline.slf @@ -0,0 +1,21 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// model_outline.frag +// fragment shader +// +// Created by Olivier Prat on 10/13/17. +// Copyright 2017 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 +// + +layout(location = 0) out vec4 _fragColor; + +uniform vec4 color; + +void main(void) { + _fragColor = color; +} diff --git a/libraries/render-utils/src/model_outline_fade.slf b/libraries/render-utils/src/model_outline_fade.slf new file mode 100644 index 0000000000..69ff54dea6 --- /dev/null +++ b/libraries/render-utils/src/model_outline_fade.slf @@ -0,0 +1,31 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// model_outline_fade.frag +// fragment shader +// +// Created by Olivier Prat on 10/13/17. +// Copyright 2017 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 Fade.slh@> +<$declareFadeFragment()$> + +layout(location = 0) out vec4 _fragColor; + +uniform vec4 color; + +in vec4 _worldPosition; + +void main(void) { + FadeObjectParams fadeParams; + + <$fetchFadeObjectParams(fadeParams)$> + applyFadeClip(fadeParams, _worldPosition.xyz); + + _fragColor = color; +} diff --git a/scripts/developer/utilities/render/outline.qml b/scripts/developer/utilities/render/outline.qml index e17f7c1f1c..1b5cbf6e1c 100644 --- a/scripts/developer/utilities/render/outline.qml +++ b/scripts/developer/utilities/render/outline.qml @@ -21,10 +21,10 @@ Item { spacing: 8 CheckBox { - text: "View Outlined Depth" - checked: root.debugConfig["viewOutlinedDepth"] + text: "View Mask" + checked: root.debugConfig["viewMask"] onCheckedChanged: { - root.debugConfig["viewOutlinedDepth"] = checked; + root.debugConfig["viewMask"] = checked; } } CheckBox { From 8d79313a9debcdcceebf0a6ebca5e2b849f0787a Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 16 Oct 2017 09:42:58 +0200 Subject: [PATCH 07/62] Working mask rendering and debugging --- libraries/render-utils/src/OutlineEffect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index 68f1759437..96a37eca6a 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -410,7 +410,7 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende { auto state = std::make_shared(); state->setDepthTest(true, true, gpu::LESS_EQUAL); - state->setColorWriteMask(false, false, false, false); + state->setColorWriteMask(true, true, true, true); initMaskPipelines(*shapePlumber, state); } From e42699c0ea615a47eee62c8281fd282b9542c3d6 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 16 Oct 2017 10:57:06 +0200 Subject: [PATCH 08/62] Added multiple selections but still only first linked to outline --- .../ui/overlays/ContextOverlayInterface.cpp | 9 ++-- .../src/ui/overlays/ContextOverlayInterface.h | 7 ++- libraries/render-utils/src/OutlineEffect.cpp | 4 +- libraries/render-utils/src/OutlineEffect.h | 8 +++- .../render-utils/src/RenderDeferredTask.cpp | 43 +++++++++++++------ .../render-utils/src/RenderDeferredTask.h | 4 ++ 6 files changed, 55 insertions(+), 20 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 8cbb214857..f677dc6550 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -34,7 +34,12 @@ ContextOverlayInterface::ContextOverlayInterface() { _tabletScriptingInterface = DependencyManager::get(); _selectionScriptingInterface = DependencyManager::get(); - _selectionToSceneHandler.initialize("contextOverlayHighlightList"); + _selectionToSceneHandlers[0].initialize("contextOverlayHighlightList"); + connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandlers[0], &SelectionToSceneHandler::selectedItemsListChanged); + for (auto i = 1; i < MAX_HIGHLIGHT_COUNT; i++) { + _selectionToSceneHandlers[i].initialize(QString("contextOverlayHighlightList")+QString::number(i)); + connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandlers[i], &SelectionToSceneHandler::selectedItemsListChanged); + } _entityPropertyFlags += PROP_POSITION; _entityPropertyFlags += PROP_ROTATION; @@ -61,8 +66,6 @@ ContextOverlayInterface::ContextOverlayInterface() { }); auto entityScriptingInterface = DependencyManager::get().data(); connect(entityScriptingInterface, &EntityScriptingInterface::deletingEntity, this, &ContextOverlayInterface::deletingEntity); - - connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandler, &SelectionToSceneHandler::selectedItemsListChanged); } static const uint32_t MOUSE_HW_ID = 0; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index b4d3ddc0c2..2a96ea3d3e 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -47,6 +47,11 @@ class ContextOverlayInterface : public QObject, public Dependency { OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; std::shared_ptr _contextOverlay { nullptr }; public: + + enum { + MAX_HIGHLIGHT_COUNT = 4 + }; + ContextOverlayInterface(); Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } @@ -86,7 +91,7 @@ private: void deletingEntity(const EntityItemID& entityItemID); - SelectionToSceneHandler _selectionToSceneHandler; + SelectionToSceneHandler _selectionToSceneHandlers[MAX_HIGHLIGHT_COUNT]; }; #endif // hifi_ContextOverlayInterface_h diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index 96a37eca6a..89cedf0676 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -399,8 +399,8 @@ void DrawOutlineTask::configure(const Config& config) { } void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { - const auto input = inputs.get(); - const auto selectedMetas = inputs.getN(0); + const auto groups = inputs.getN(0).get(); + const auto selectedMetas = groups[0]; const auto sceneFrameBuffer = inputs.getN(1); const auto primaryFramebuffer = inputs.getN(2); const auto deferredFrameTransform = inputs.getN(3); diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index 511ed4a992..c32d352a01 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -173,7 +173,13 @@ private: class DrawOutlineTask { public: - using Inputs = render::VaryingSet4; + + enum { + MAX_GROUP_COUNT = 7 + }; + + using Groups = render::VaryingArray; + using Inputs = render::VaryingSet4; using Config = render::Task::Config; using JobModel = render::Task::ModelI; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 1739f67748..c20eb10cf6 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -44,6 +44,7 @@ #include +#include using namespace render; extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest = false); @@ -57,6 +58,18 @@ void RenderDeferredTask::configure(const Config& config) { } +const render::Varying RenderDeferredTask::addSelectItemJobs(JobModel& task, const char* selectionName, + const render::Varying& metas, + const render::Varying& opaques, + const render::Varying& transparents) { + const auto selectMetaInput = SelectItems::Inputs(metas, Varying()).asVarying(); + const auto selectedMetas = task.addJob("MetaSelection", selectMetaInput, selectionName); + const auto selectMetaAndOpaqueInput = SelectItems::Inputs(opaques, selectedMetas).asVarying(); + const auto selectedMetasAndOpaques = task.addJob("OpaqueSelection", selectMetaAndOpaqueInput, selectionName); + const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques).asVarying(); + return task.addJob("TransparentSelection", selectItemInput, selectionName); +} + void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { const auto& items = input.get(); auto fadeEffect = DependencyManager::get(); @@ -94,15 +107,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren // draw a stencil mask in hidden regions of the framebuffer. task.addJob("PrepareStencil", primaryFramebuffer); - // Select items that need to be outlined - const auto selectionName = "contextOverlayHighlightList"; - const auto selectMetaInput = SelectItems::Inputs(metas, Varying()).asVarying(); - const auto selectedMetas = task.addJob("PassTestMetaSelection", selectMetaInput, selectionName); - const auto selectMetaAndOpaqueInput = SelectItems::Inputs(opaques, selectedMetas).asVarying(); - const auto selectedMetasAndOpaques = task.addJob("PassTestOpaqueSelection", selectMetaAndOpaqueInput, selectionName); - const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques).asVarying(); - const auto selectedItems = task.addJob("PassTestTransparentSelection", selectItemInput, selectionName); - // Render opaque objects in DeferredBuffer const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel).asVarying(); task.addJob("DrawOpaqueDeferred", opaqueInputs, shapePlumber); @@ -172,9 +176,22 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying(); task.addJob("ToneMapping", toneMappingInputs); + // Select items that need to be outlined + const auto selectionBaseName = "contextOverlayHighlightList"; + const auto selectedItems = addSelectItemJobs(task, selectionBaseName, metas, opaques, transparents); + DrawOutlineTask::Groups outlineGroups; + outlineGroups[0] = selectedItems; + for (auto i = 1; i < DrawOutlineTask::MAX_GROUP_COUNT; i++) { + std::ostringstream selectionName; + selectionName << selectionBaseName; + selectionName << i; + outlineGroups[i] = addSelectItemJobs(task, selectionName.str().c_str(), metas, opaques, transparents); + } const auto outlineRangeTimer = task.addJob("BeginOutlineRangeTimer", "Outline"); - const auto outlineInputs = DrawOutlineTask::Inputs(selectedItems, deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying(); + + const auto outlineInputs = DrawOutlineTask::Inputs(outlineGroups, deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying(); task.addJob("DrawOutline", outlineInputs); + task.addJob("EndOutlineRangeTimer", outlineRangeTimer); { // DEbug the bounds of the rendered items, still look at the zbuffer @@ -184,6 +201,9 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawLightBounds", lights); task.addJob("DrawZones", zones); + + // Render.getConfig("RenderMainView.DrawSelectionBounds").enabled = true + task.addJob("DrawSelectionBounds", selectedItems); } // Layered Overlays @@ -230,9 +250,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren } task.addJob("DrawZoneStack", deferredFrameTransform); - - // Render.getConfig("RenderMainView.DrawSelectionBounds").enabled = true - task.addJob("DrawSelectionBounds", selectedItems); } // AA job to be revisited diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 452420589b..567e7f6ccd 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -198,6 +198,10 @@ public: void configure(const Config& config); void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); +private: + + static const render::Varying addSelectItemJobs(JobModel& task, const char* selectionName, + const render::Varying& metas, const render::Varying& opaques, const render::Varying& transparents); }; #endif // hifi_RenderDeferredTask_h From 1b67223e0eaaff87363c7f18bf8a0e3c4255943a Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 16 Oct 2017 12:38:44 +0200 Subject: [PATCH 09/62] Preparing for multiple outline groups in shader --- libraries/render-utils/src/Outline.slh | 8 +- libraries/render-utils/src/OutlineEffect.cpp | 221 +++++++++++------- libraries/render-utils/src/OutlineEffect.h | 92 +++++--- libraries/render-utils/src/Outline_shared.slh | 4 + .../render-utils/src/RenderDeferredTask.cpp | 2 +- 5 files changed, 210 insertions(+), 117 deletions(-) diff --git a/libraries/render-utils/src/Outline.slh b/libraries/render-utils/src/Outline.slh index ac56e4c95c..4e71116f79 100644 --- a/libraries/render-utils/src/Outline.slh +++ b/libraries/render-utils/src/Outline.slh @@ -21,11 +21,13 @@ uniform outlineParamsBuffer { uniform sampler2D sceneDepthMap; uniform sampler2D outlinedDepthMap; +uniform sampler2D outlinedIdMap; in vec2 varTexCoord0; out vec4 outFragColor; const float FAR_Z = 1.0; +const float ID_THRESHOLD = 1.f/64.f; const float LINEAR_DEPTH_BIAS = 5e-3; const float OPACITY_EPSILON = 5e-3; @@ -37,10 +39,11 @@ void main(void) { // sides of the silhouette vec2 halfTexel = getInvWidthHeight() / 2; vec2 texCoord0 = varTexCoord0+halfTexel; + vec4 outlinedIdColor = texture(outlinedIdMap, texCoord0); float outlinedDepth = texture(outlinedDepthMap, texCoord0).x; float intensity = 0.0; - if (outlinedDepth < FAR_Z) { + if (outlinedDepth < FAR_Z && distance(outlinedIdColor, params._idColor) < ID_THRESHOLD) { // We're not on the far plane so we are on the outlined object, thus no outline to do! <@if IS_FILLED@> // But we need to fill the interior @@ -75,7 +78,8 @@ void main(void) { if (uv.x>=0.0 && uv.x<=1.0) { outlinedDepth = texture(outlinedDepthMap, uv).x; - intensity += (outlinedDepth < FAR_Z) ? 1.0 : 0.0; + outlinedIdColor = texture(outlinedIdMap, uv); + intensity += (outlinedDepth < FAR_Z && distance(outlinedIdColor, params._idColor) < ID_THRESHOLD) ? 1.0 : 0.0; weight += 1.f; } uv.x += deltaUv.x; diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index 89cedf0676..0ffd2581cf 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -83,81 +83,139 @@ gpu::TexturePointer OutlineRessources::getIdTexture() { return _idTexture; } +glm::vec4 encodeIdToColor(unsigned int id) { + union { + struct { + unsigned int r : 2; + unsigned int g : 2; + unsigned int b : 2; + unsigned int a : 2; + }; + unsigned char id; + } groupId; + + assert(id < 254); + groupId.id = id+1; + + glm::vec4 idColor{ groupId.r, groupId.g, groupId.b, groupId.a }; + + // Normalize. Since we put 2 bits into each color component, each component has a maximum + // value of 3. + idColor /= 3.f; + return idColor; +} + void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); - auto& inShapes = inputs.get0(); + auto& groups = inputs.get0(); auto& deferredFrameBuffer = inputs.get1(); - if (!inShapes.empty()) { - RenderArgs* args = renderContext->args; - ShapeKey::Builder defaultKeyBuilder; + RenderArgs* args = renderContext->args; + ShapeKey::Builder defaultKeyBuilder; + auto hasOutline = false; - if (!_outlineRessources) { - _outlineRessources = std::make_shared(); + if (!_outlineRessources) { + _outlineRessources = std::make_shared(); + } + _outlineRessources->update(deferredFrameBuffer->getDeferredColorTexture()); + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + + auto maskPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder); + auto maskSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned()); + auto colorLoc = maskPipeline.get()->pipeline->getProgram()->getUniforms().findLocation("color"); + auto skinnedColorLoc = maskSkinnedPipeline.get()->pipeline->getProgram()->getUniforms().findLocation("color"); + unsigned int groupId = 0; + + for (auto& inShapeBounds : groups) { + if (!inShapeBounds.isNull()) { + auto& inShapes = inShapeBounds.get(); + + if (!inShapes.empty()) { + if (!hasOutline) { + batch.setFramebuffer(_outlineRessources->getFramebuffer()); + // Clear it only if it hasn't been done before + batch.clearFramebuffer( + gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH, + vec4(0.0f, 0.0f, 0.0f, 0.0f), 1.0f, 0, false); + + // Setup camera, projection and viewport for all items + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); + + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + hasOutline = true; + } + + std::vector skinnedShapeKeys{}; + // Encode group id in quantized color + glm::vec4 idColor = encodeIdToColor(groupId); + + // Iterate through all inShapes and render the unskinned + args->_shapePipeline = maskPipeline; + batch.setPipeline(maskPipeline->pipeline); + batch._glUniform4f(colorLoc, idColor.r, idColor.g, idColor.b, idColor.a); + for (auto items : inShapes) { + if (items.first.isSkinned()) { + skinnedShapeKeys.push_back(items.first); + } else { + renderItems(renderContext, items.second); + } + } + + // Reiterate to render the skinned + args->_shapePipeline = maskSkinnedPipeline; + batch.setPipeline(maskSkinnedPipeline->pipeline); + batch._glUniform4f(skinnedColorLoc, idColor.r, idColor.g, idColor.b, idColor.a); + for (const auto& key : skinnedShapeKeys) { + renderItems(renderContext, inShapes.at(key)); + } + } + } + + ++groupId; } - _outlineRessources->update(deferredFrameBuffer->getDeferredColorTexture()); - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { - args->_batch = &batch; - - batch.setFramebuffer(_outlineRessources->getFramebuffer()); - // Clear it - batch.clearFramebuffer( - gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH, - vec4(0.0f, 0.0f, 0.0f, 0.0f), 1.0f, 0, false); - - // Setup camera, projection and viewport for all items - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - glm::mat4 projMat; - Transform viewMat; - args->getViewFrustum().evalProjectionMatrix(projMat); - args->getViewFrustum().evalViewTransform(viewMat); - - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - - auto maskPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder); - auto maskSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned()); - - std::vector skinnedShapeKeys{}; - auto colorLoc = maskPipeline.get()->pipeline->getProgram()->getUniforms().findLocation("color"); - glm::vec4 idColor{ 1.0f, 0.0f, 0.0f, 0.0f }; - - // Iterate through all inShapes and render the unskinned - args->_shapePipeline = maskPipeline; - batch.setPipeline(maskPipeline->pipeline); - batch._glUniform4f(colorLoc, idColor.r, idColor.g, idColor.b, idColor.a); - for (auto items : inShapes) { - if (items.first.isSkinned()) { - skinnedShapeKeys.push_back(items.first); - } - else { - renderItems(renderContext, items.second); - } - } - - colorLoc = maskSkinnedPipeline.get()->pipeline->getProgram()->getUniforms().findLocation("color"); - // Reiterate to render the skinned - args->_shapePipeline = maskSkinnedPipeline; - batch.setPipeline(maskSkinnedPipeline->pipeline); - batch._glUniform4f(colorLoc, idColor.r, idColor.g, idColor.b, idColor.a); - for (const auto& key : skinnedShapeKeys) { - renderItems(renderContext, inShapes.at(key)); - } - - args->_shapePipeline = nullptr; - args->_batch = nullptr; - }); + args->_shapePipeline = nullptr; + args->_batch = nullptr; + }); + if (hasOutline) { output = _outlineRessources; } else { output = nullptr; } } +PrepareDrawOutline::PrepareDrawOutline() { + +} + +void PrepareDrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { + auto destinationFrameBuffer = inputs; + auto framebufferSize = destinationFrameBuffer->getSize(); + + if (!_primaryWithoutDepthBuffer || framebufferSize != _frameBufferSize) { + // Failing to recreate this frame buffer when the screen has been resized creates a bug on Mac + _primaryWithoutDepthBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithoutDepth")); + _primaryWithoutDepthBuffer->setRenderBuffer(0, destinationFrameBuffer->getRenderBuffer(0)); + _frameBufferSize = framebufferSize; + } + + outputs = _primaryWithoutDepthBuffer; +} + +gpu::PipelinePointer DrawOutline::_pipeline; +gpu::PipelinePointer DrawOutline::_pipelineFilled; + DrawOutline::DrawOutline() { } @@ -170,6 +228,7 @@ void DrawOutline::configure(const Config& config) { _fillOpacityOccluded = config.fillOpacityOccluded; _threshold = config.glow ? 1.f : 1e-3f; _intensity = config.intensity * (config.glow ? 2.f : 1.f); + _hasConfigurationChanged = true; } void DrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { @@ -179,20 +238,16 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I auto sceneDepthBuffer = inputs.get2(); const auto frameTransform = inputs.get0(); auto outlinedDepthTexture = outlineFrameBuffer->getDepthTexture(); + auto outlinedIdTexture = outlineFrameBuffer->getIdTexture(); auto destinationFrameBuffer = inputs.get3(); auto framebufferSize = glm::ivec2(outlinedDepthTexture->getDimensions()); - if (!_primaryWithoutDepthBuffer || framebufferSize!=_frameBufferSize) { - // Failing to recreate this frame buffer when the screen has been resized creates a bug on Mac - _primaryWithoutDepthBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithoutDepth")); - _primaryWithoutDepthBuffer->setRenderBuffer(0, destinationFrameBuffer->getRenderBuffer(0)); - _frameBufferSize = framebufferSize; - } - if (sceneDepthBuffer) { const auto OPACITY_EPSILON = 5e-3f; auto pipeline = getPipeline(_fillOpacityUnoccluded>OPACITY_EPSILON || _fillOpacityOccluded>OPACITY_EPSILON); auto args = renderContext->args; + + if (_hasConfigurationChanged) { auto& configuration = _configuration.edit(); configuration._color = _color; @@ -201,24 +256,27 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I configuration._fillOpacityOccluded = _fillOpacityOccluded; configuration._threshold = _threshold; configuration._blurKernelSize = _blurKernelSize; - configuration._size.x = _size * _frameBufferSize.y / _frameBufferSize.x; + configuration._size.x = (_size * framebufferSize.y) / framebufferSize.x; configuration._size.y = _size; + configuration._idColor = encodeIdToColor(0); + _hasConfigurationChanged = false; } gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); - batch.setFramebuffer(_primaryWithoutDepthBuffer); + batch.setFramebuffer(destinationFrameBuffer); batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); - batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(_frameBufferSize, args->_viewport)); + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport)); batch.setPipeline(pipeline); batch.setUniformBuffer(OUTLINE_PARAMS_SLOT, _configuration); batch.setUniformBuffer(FRAME_TRANSFORM_SLOT, frameTransform->getFrameTransformBuffer()); batch.setResourceTexture(SCENE_DEPTH_SLOT, sceneDepthBuffer->getPrimaryDepthTexture()); batch.setResourceTexture(OUTLINED_DEPTH_SLOT, outlinedDepthTexture); + batch.setResourceTexture(OUTLINED_ID_SLOT, outlinedIdTexture); batch.draw(gpu::TRIANGLE_STRIP, 4); // Restore previous frame buffer @@ -239,6 +297,7 @@ const gpu::PipelinePointer& DrawOutline::getPipeline(bool isFilled) { slotBindings.insert(gpu::Shader::Binding("deferredFrameTransformBuffer", FRAME_TRANSFORM_SLOT)); slotBindings.insert(gpu::Shader::Binding("sceneDepthMap", SCENE_DEPTH_SLOT)); slotBindings.insert(gpu::Shader::Binding("outlinedDepthMap", OUTLINED_DEPTH_SLOT)); + slotBindings.insert(gpu::Shader::Binding("outlinedIdMap", OUTLINED_ID_SLOT)); gpu::Shader::makeProgram(*program, slotBindings); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); @@ -400,7 +459,6 @@ void DrawOutlineTask::configure(const Config& config) { void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { const auto groups = inputs.getN(0).get(); - const auto selectedMetas = groups[0]; const auto sceneFrameBuffer = inputs.getN(1); const auto primaryFramebuffer = inputs.getN(2); const auto deferredFrameTransform = inputs.getN(3); @@ -415,19 +473,26 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende initMaskPipelines(*shapePlumber, state); } - const auto outlinedItemIDs = task.addJob("OutlineMetaToSubItemIDs", selectedMetas); - const auto outlinedItems = task.addJob("OutlineMetaToSubItems", outlinedItemIDs, true); + DrawOutlineMask::Groups sortedBounds; + for (auto i = 0; i < DrawOutline::MAX_GROUP_COUNT; i++) { + const auto groupItems = groups[i]; + const auto outlinedItemIDs = task.addJob("OutlineMetaToSubItemIDs", groupItems); + const auto outlinedItems = task.addJob("OutlineMetaToSubItems", outlinedItemIDs, true); - // Sort - const auto sortedPipelines = task.addJob("OutlinePipelineSort", outlinedItems); - const auto sortedShapes = task.addJob("OutlineDepthSort", sortedPipelines); + // Sort + const auto sortedPipelines = task.addJob("OutlinePipelineSort", outlinedItems); + sortedBounds[i] = task.addJob("OutlineDepthSort", sortedPipelines); + } // Draw depth of outlined objects in separate buffer - const auto drawMaskInputs = DrawOutlineMask::Inputs(sortedShapes, sceneFrameBuffer).asVarying(); + const auto drawMaskInputs = DrawOutlineMask::Inputs(sortedBounds, sceneFrameBuffer).asVarying(); const auto outlinedFrameBuffer = task.addJob("OutlineMask", drawMaskInputs, shapePlumber); + // Prepare for outline group rendering. + const auto destFrameBuffer = task.addJob("PrepareOutline", primaryFramebuffer); + // Draw outline - const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlinedFrameBuffer, sceneFrameBuffer, primaryFramebuffer).asVarying(); + const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlinedFrameBuffer, sceneFrameBuffer, destFrameBuffer).asVarying(); task.addJob("OutlineEffect", drawOutlineInputs); // Debug outline diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index c32d352a01..ce0b2917a4 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -42,22 +42,22 @@ protected: using OutlineRessourcesPointer = std::shared_ptr; -class DrawOutlineMask { +class PrepareDrawOutline { public: + using Inputs = gpu::FramebufferPointer; + using Outputs = gpu::FramebufferPointer; + using Config = render::Job::Config; + using JobModel = render::Job::ModelIO; - using Inputs = render::VaryingSet2; - // Output will contain outlined objects only z-depth texture and the input primary buffer but without the primary depth buffer - using Outputs = OutlineRessourcesPointer; - using JobModel = render::Job::ModelIO; + PrepareDrawOutline(); - DrawOutlineMask(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); - void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output); +private: -protected: + gpu::FramebufferPointer _primaryWithoutDepthBuffer; + gpu::Vec2u _frameBufferSize{ 0, 0 }; - render::ShapePlumberPointer _shapePlumber; - OutlineRessourcesPointer _outlineRessources; }; class DrawOutlineConfig : public render::Job::Config { @@ -95,6 +95,10 @@ signals: class DrawOutline { public: + enum { + MAX_GROUP_COUNT = 7 + }; + using Inputs = render::VaryingSet4; using Config = DrawOutlineConfig; using JobModel = render::Job::ModelI; @@ -109,6 +113,7 @@ private: enum { SCENE_DEPTH_SLOT = 0, OUTLINED_DEPTH_SLOT, + OUTLINED_ID_SLOT, OUTLINE_PARAMS_SLOT = 0, FRAME_TRANSFORM_SLOT @@ -118,12 +123,10 @@ private: using OutlineConfigurationBuffer = gpu::StructBuffer; - const gpu::PipelinePointer& getPipeline(bool isFilled); + static const gpu::PipelinePointer& getPipeline(bool isFilled); - gpu::FramebufferPointer _primaryWithoutDepthBuffer; - glm::ivec2 _frameBufferSize {0, 0}; - gpu::PipelinePointer _pipeline; - gpu::PipelinePointer _pipelineFilled; + static gpu::PipelinePointer _pipeline; + static gpu::PipelinePointer _pipelineFilled; OutlineConfigurationBuffer _configuration; glm::vec3 _color; float _size; @@ -132,6 +135,44 @@ private: float _fillOpacityUnoccluded; float _fillOpacityOccluded; float _threshold; + bool _hasConfigurationChanged{ true }; +}; + +class DrawOutlineTask { +public: + + using Groups = render::VaryingArray; + using Inputs = render::VaryingSet4; + using Config = render::Task::Config; + using JobModel = render::Task::ModelI; + + DrawOutlineTask(); + + void configure(const Config& config); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); + +private: + + static void initMaskPipelines(render::ShapePlumber& plumber, gpu::StatePointer state); +}; + +class DrawOutlineMask { +public: + + using Groups = render::VaryingArray; + using Inputs = render::VaryingSet2; + // Output will contain outlined objects only z-depth texture and the input primary buffer but without the primary depth buffer + using Outputs = OutlineRessourcesPointer; + using JobModel = render::Job::ModelIO; + + DrawOutlineMask(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output); + +protected: + + render::ShapePlumberPointer _shapePlumber; + OutlineRessourcesPointer _outlineRessources; }; class DebugOutlineConfig : public render::Job::Config { @@ -171,27 +212,6 @@ private: void initializePipelines(); }; -class DrawOutlineTask { -public: - - enum { - MAX_GROUP_COUNT = 7 - }; - - using Groups = render::VaryingArray; - using Inputs = render::VaryingSet4; - using Config = render::Task::Config; - using JobModel = render::Task::ModelI; - - DrawOutlineTask(); - - void configure(const Config& config); - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); - -private: - - static void initMaskPipelines(render::ShapePlumber& plumber, gpu::StatePointer state); -}; #endif // hifi_render_utils_OutlineEffect_h diff --git a/libraries/render-utils/src/Outline_shared.slh b/libraries/render-utils/src/Outline_shared.slh index 902bbd20ad..5ce21c85b4 100644 --- a/libraries/render-utils/src/Outline_shared.slh +++ b/libraries/render-utils/src/Outline_shared.slh @@ -2,9 +2,11 @@ #ifdef __cplusplus # define VEC2 glm::vec2 # define VEC3 glm::vec3 +# define VEC4 glm::vec4 #else # define VEC2 vec2 # define VEC3 vec3 +# define VEC4 vec4 #endif struct OutlineParameters @@ -16,6 +18,8 @@ struct OutlineParameters float _fillOpacityUnoccluded; float _fillOpacityOccluded; + VEC4 _idColor; + float _threshold; int _blurKernelSize; float padding2; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index c20eb10cf6..73d8b93c07 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -181,7 +181,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto selectedItems = addSelectItemJobs(task, selectionBaseName, metas, opaques, transparents); DrawOutlineTask::Groups outlineGroups; outlineGroups[0] = selectedItems; - for (auto i = 1; i < DrawOutlineTask::MAX_GROUP_COUNT; i++) { + for (auto i = 1; i < DrawOutline::MAX_GROUP_COUNT; i++) { std::ostringstream selectionName; selectionName << selectionBaseName; selectionName << i; From 70f892e67d47847c60268cfd0469b04f147fc986 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 16 Oct 2017 17:02:13 +0200 Subject: [PATCH 10/62] Ready to plug multiple selections javascript side --- libraries/render-utils/src/Outline.slh | 131 +++++++++++------- libraries/render-utils/src/OutlineEffect.cpp | 32 +++-- libraries/render-utils/src/OutlineEffect.h | 14 +- libraries/render-utils/src/Outline_shared.slh | 6 +- 4 files changed, 112 insertions(+), 71 deletions(-) diff --git a/libraries/render-utils/src/Outline.slh b/libraries/render-utils/src/Outline.slh index 4e71116f79..c58d1c7689 100644 --- a/libraries/render-utils/src/Outline.slh +++ b/libraries/render-utils/src/Outline.slh @@ -16,23 +16,85 @@ <@include Outline_shared.slh@> uniform outlineParamsBuffer { - OutlineParameters params; + OutlineParameters groups[GROUP_COUNT]; }; uniform sampler2D sceneDepthMap; uniform sampler2D outlinedDepthMap; uniform sampler2D outlinedIdMap; +uniform int enabledGroupsMask; in vec2 varTexCoord0; out vec4 outFragColor; const float FAR_Z = 1.0; -const float ID_THRESHOLD = 1.f/64.f; const float LINEAR_DEPTH_BIAS = 5e-3; const float OPACITY_EPSILON = 5e-3; <@func main(IS_FILLED)@> +int getGroupIndexFromColor(vec4 color) { + ivec4 id = ivec4(color * GROUP_ID_COLOR_COMPONENT_MAX) << ivec4(0, GROUP_ID_COLOR_COMPONENT_BITS, GROUP_ID_COLOR_COMPONENT_BITS*2, GROUP_ID_COLOR_COMPONENT_BITS*3); + return (id.r | id.g | id.b | id.a) - 1; +} + +vec4 computeGroupOutline(int centerGroupId, float centerDepth, int groupId, vec2 texCoord) { + float intensity = 0.0; + + if (centerGroupId==groupId && centerDepth < FAR_Z) { + // We're on the outlined object, thus no outline to do! +<@if IS_FILLED@> + OutlineParameters groupParams = groups[groupId]; + + // But we need to fill the interior + float sceneDepth = texture(sceneDepthMap, texCoord).x; + // Transform to linear depth for better precision + centerDepth = -evalZeyeFromZdb(centerDepth); + sceneDepth = -evalZeyeFromZdb(sceneDepth); + + // Are we occluded? + intensity = (sceneDepth < (centerDepth-LINEAR_DEPTH_BIAS)) ? groupParams._fillOpacityOccluded : groupParams._fillOpacityUnoccluded; + return vec4(groupParams._color.rgb, intensity); +<@else@> + return vec4(0,0,0,0); +<@endif@> + } else { + OutlineParameters groupParams = groups[groupId]; + float weight = 0.0; + vec2 deltaUv = groupParams._size / groupParams._blurKernelSize; + vec2 lineStartUv = texCoord - groupParams._size / 2.0; + vec2 uv; + int x; + int y; + + for (y=0 ; y=0.0 && uv.y<=1.0) { + for (x=0 ; x=0.0 && uv.x<=1.0) + { + vec4 outlinedIdColor = texture(outlinedIdMap, uv); + float outlinedDepth = texture(outlinedDepthMap, uv).x; + int outlinedId = getGroupIndexFromColor(outlinedIdColor); + intensity += (outlinedDepth - // But we need to fill the interior - float sceneDepth = texture(sceneDepthMap, texCoord0).x; - // Transform to linear depth for better precision - outlinedDepth = -evalZeyeFromZdb(outlinedDepth); - sceneDepth = -evalZeyeFromZdb(sceneDepth); - - // Are we occluded? - if (sceneDepth < (outlinedDepth-LINEAR_DEPTH_BIAS)) { - intensity = params._fillOpacityOccluded; - } else { - intensity = params._fillOpacityUnoccluded; + vec4 finalColor = vec4(0,0,0,0); + int groupMask = 1; + for (int i=0 ; i - discard; -<@endif@> - } else { - float weight = 0.0; - vec2 deltaUv = params._size / params._blurKernelSize; - vec2 lineStartUv = texCoord0 - params._size / 2.0; - vec2 uv; - int x; - int y; - - for (y=0 ; y=0.0 && uv.y<=1.0) { - for (x=0 ; x=0.0 && uv.x<=1.0) - { - outlinedDepth = texture(outlinedDepthMap, uv).x; - outlinedIdColor = texture(outlinedIdMap, uv); - intensity += (outlinedDepth < FAR_Z && distance(outlinedIdColor, params._idColor) < ID_THRESHOLD) ? 1.0 : 0.0; - weight += 1.f; - } - uv.x += deltaUv.x; - } - } - } - - intensity /= weight; - if (intensity < OPACITY_EPSILON) { - discard; - } - - intensity = min(1.0, intensity / params._threshold) * params._intensity; + groupMask <<= 1; } - outFragColor = vec4(params._color.rgb, intensity); + if (finalColor.a < OPACITY_EPSILON) { + discard; + } + outFragColor = finalColor; } <@endfunc@> diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index 0ffd2581cf..2cb19826f4 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -94,6 +94,8 @@ glm::vec4 encodeIdToColor(unsigned int id) { unsigned char id; } groupId; + static_assert(GROUP_ID_COLOR_COMPONENT_BITS == 2, "Assuming two bits per component contrary to GLSL shader code. See Outline_shared.slh"); + assert(id < 254); groupId.id = id+1; @@ -101,7 +103,7 @@ glm::vec4 encodeIdToColor(unsigned int id) { // Normalize. Since we put 2 bits into each color component, each component has a maximum // value of 3. - idColor /= 3.f; + idColor /= GROUP_ID_COLOR_COMPONENT_MAX; return idColor; } @@ -250,15 +252,19 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I if (_hasConfigurationChanged) { auto& configuration = _configuration.edit(); - configuration._color = _color; - configuration._intensity = _intensity; - configuration._fillOpacityUnoccluded = _fillOpacityUnoccluded; - configuration._fillOpacityOccluded = _fillOpacityOccluded; - configuration._threshold = _threshold; - configuration._blurKernelSize = _blurKernelSize; - configuration._size.x = (_size * framebufferSize.y) / framebufferSize.x; - configuration._size.y = _size; - configuration._idColor = encodeIdToColor(0); + + for (auto groupId = 0; groupId < MAX_GROUP_COUNT; groupId++) { + auto& groupConfig = configuration._groups[groupId]; + + groupConfig._color = _color; + groupConfig._intensity = _intensity; + groupConfig._fillOpacityUnoccluded = _fillOpacityUnoccluded; + groupConfig._fillOpacityOccluded = _fillOpacityOccluded; + groupConfig._threshold = _threshold; + groupConfig._blurKernelSize = _blurKernelSize; + groupConfig._size.x = (_size * framebufferSize.y) / framebufferSize.x; + groupConfig._size.y = _size; + } _hasConfigurationChanged = false; } @@ -272,15 +278,15 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport)); batch.setPipeline(pipeline); + auto enabledGroupsLoc = pipeline->getProgram()->getUniforms().findLocation("enabledGroupsMask"); + batch.setUniformBuffer(OUTLINE_PARAMS_SLOT, _configuration); batch.setUniformBuffer(FRAME_TRANSFORM_SLOT, frameTransform->getFrameTransformBuffer()); batch.setResourceTexture(SCENE_DEPTH_SLOT, sceneDepthBuffer->getPrimaryDepthTexture()); batch.setResourceTexture(OUTLINED_DEPTH_SLOT, outlinedDepthTexture); batch.setResourceTexture(OUTLINED_ID_SLOT, outlinedIdTexture); + batch._glUniform1i(enabledGroupsLoc, 1); batch.draw(gpu::TRIANGLE_STRIP, 4); - - // Restore previous frame buffer - batch.setFramebuffer(destinationFrameBuffer); }); } } diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index ce0b2917a4..6e4c0a18a8 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -94,9 +94,13 @@ signals: }; class DrawOutline { +private: + +#include "Outline_shared.slh" + public: enum { - MAX_GROUP_COUNT = 7 + MAX_GROUP_COUNT = GROUP_COUNT }; using Inputs = render::VaryingSet4; @@ -116,12 +120,14 @@ private: OUTLINED_ID_SLOT, OUTLINE_PARAMS_SLOT = 0, - FRAME_TRANSFORM_SLOT + FRAME_TRANSFORM_SLOT, }; -#include "Outline_shared.slh" + struct OutlineConfiguration { + OutlineParameters _groups[MAX_GROUP_COUNT]; + }; - using OutlineConfigurationBuffer = gpu::StructBuffer; + using OutlineConfigurationBuffer = gpu::StructBuffer; static const gpu::PipelinePointer& getPipeline(bool isFilled); diff --git a/libraries/render-utils/src/Outline_shared.slh b/libraries/render-utils/src/Outline_shared.slh index 5ce21c85b4..98d803c28b 100644 --- a/libraries/render-utils/src/Outline_shared.slh +++ b/libraries/render-utils/src/Outline_shared.slh @@ -9,6 +9,10 @@ # define VEC4 vec4 #endif +#define GROUP_COUNT 7 +#define GROUP_ID_COLOR_COMPONENT_BITS 2 +#define GROUP_ID_COLOR_COMPONENT_MAX 3 + struct OutlineParameters { VEC3 _color; @@ -18,8 +22,6 @@ struct OutlineParameters float _fillOpacityUnoccluded; float _fillOpacityOccluded; - VEC4 _idColor; - float _threshold; int _blurKernelSize; float padding2; From 9ed5185a3ea86e8be67dc3c5d698103694d1808d Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 17 Oct 2017 10:01:06 +0200 Subject: [PATCH 11/62] Multiple outline group config working --- libraries/render-utils/src/Outline.slh | 2 +- libraries/render-utils/src/OutlineEffect.cpp | 71 ++++--- libraries/render-utils/src/OutlineEffect.h | 121 ++++++----- libraries/render-utils/src/Outline_shared.slh | 4 +- .../developer/utilities/render/outline.qml | 188 ++++++++++-------- 5 files changed, 234 insertions(+), 152 deletions(-) diff --git a/libraries/render-utils/src/Outline.slh b/libraries/render-utils/src/Outline.slh index c58d1c7689..bc71cabf1e 100644 --- a/libraries/render-utils/src/Outline.slh +++ b/libraries/render-utils/src/Outline.slh @@ -53,7 +53,7 @@ vec4 computeGroupOutline(int centerGroupId, float centerDepth, int groupId, vec2 sceneDepth = -evalZeyeFromZdb(sceneDepth); // Are we occluded? - intensity = (sceneDepth < (centerDepth-LINEAR_DEPTH_BIAS)) ? groupParams._fillOpacityOccluded : groupParams._fillOpacityUnoccluded; + intensity = (sceneDepth < (centerDepth-LINEAR_DEPTH_BIAS)) ? groupParams._occludedFillOpacity : groupParams._unoccludedFillOpacity; return vec4(groupParams._color.rgb, intensity); <@else@> return vec4(0,0,0,0); diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index 2cb19826f4..74a29e8237 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -215,6 +215,17 @@ void PrepareDrawOutline::run(const render::RenderContextPointer& renderContext, outputs = _primaryWithoutDepthBuffer; } +int DrawOutlineConfig::getGroupCount() const { + return Outline::MAX_GROUP_COUNT; +} + +void DrawOutlineConfig::setGroup(int value) { + assert(value >= 0 && value < Outline::MAX_GROUP_COUNT); + group = std::min(value, Outline::MAX_GROUP_COUNT); + group = std::max(group, 0); + emit dirty(); +} + gpu::PipelinePointer DrawOutline::_pipeline; gpu::PipelinePointer DrawOutline::_pipelineFilled; @@ -222,15 +233,34 @@ DrawOutline::DrawOutline() { } void DrawOutline::configure(const Config& config) { - _color = config.color; - _blurKernelSize = std::min(10, std::max(2, (int)floorf(config.width*2 + 0.5f))); - // Size is in normalized screen height. We decide that for outline width = 1, this is equal to 1/400. - _size = config.width / 400.f; - _fillOpacityUnoccluded = config.fillOpacityUnoccluded; - _fillOpacityOccluded = config.fillOpacityOccluded; - _threshold = config.glow ? 1.f : 1e-3f; - _intensity = config.intensity * (config.glow ? 2.f : 1.f); - _hasConfigurationChanged = true; + auto& configuration = _configuration.edit(); + const auto OPACITY_EPSILON = 5e-3f; + + bool someFilled = false; + bool isFilled; + + for (auto groupId = 0; groupId < MAX_GROUP_COUNT; groupId++) { + auto& dstGroupConfig = configuration._groups[groupId]; + auto& srcGroupConfig = config.groupParameters[groupId]; + + dstGroupConfig._color = srcGroupConfig.color; + dstGroupConfig._intensity = srcGroupConfig.intensity * (srcGroupConfig.glow ? 2.f : 1.f); + dstGroupConfig._unoccludedFillOpacity = srcGroupConfig.unoccludedFillOpacity; + dstGroupConfig._occludedFillOpacity = srcGroupConfig.occludedFillOpacity; + dstGroupConfig._threshold = srcGroupConfig.glow ? 1.f : 1e-3f; + dstGroupConfig._blurKernelSize = std::min(10, std::max(2, (int)floorf(srcGroupConfig.width * 2 + 0.5f))); + // Size is in normalized screen height. We decide that for outline width = 1, this is equal to 1/400. + _sizes[groupId] = srcGroupConfig.width / 400.0f; + + isFilled = (srcGroupConfig.unoccludedFillOpacity > OPACITY_EPSILON || srcGroupConfig.occludedFillOpacity > OPACITY_EPSILON); + someFilled = someFilled || isFilled; + } + + if (someFilled) { + _mode = M_SOME_FILLED; + } else { + _mode = M_ALL_UNFILLED; + } } void DrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { @@ -245,27 +275,20 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I auto framebufferSize = glm::ivec2(outlinedDepthTexture->getDimensions()); if (sceneDepthBuffer) { - const auto OPACITY_EPSILON = 5e-3f; - auto pipeline = getPipeline(_fillOpacityUnoccluded>OPACITY_EPSILON || _fillOpacityOccluded>OPACITY_EPSILON); + auto pipeline = getPipeline(); auto args = renderContext->args; - if (_hasConfigurationChanged) + if (_framebufferSize != framebufferSize) { auto& configuration = _configuration.edit(); for (auto groupId = 0; groupId < MAX_GROUP_COUNT; groupId++) { auto& groupConfig = configuration._groups[groupId]; - groupConfig._color = _color; - groupConfig._intensity = _intensity; - groupConfig._fillOpacityUnoccluded = _fillOpacityUnoccluded; - groupConfig._fillOpacityOccluded = _fillOpacityOccluded; - groupConfig._threshold = _threshold; - groupConfig._blurKernelSize = _blurKernelSize; - groupConfig._size.x = (_size * framebufferSize.y) / framebufferSize.x; - groupConfig._size.y = _size; + groupConfig._size.x = (_sizes[groupId] * framebufferSize.y) / framebufferSize.x; + groupConfig._size.y = _sizes[groupId]; } - _hasConfigurationChanged = false; + _framebufferSize = framebufferSize; } gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { @@ -292,7 +315,7 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I } } -const gpu::PipelinePointer& DrawOutline::getPipeline(bool isFilled) { +const gpu::PipelinePointer& DrawOutline::getPipeline() { if (!_pipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); auto ps = gpu::Shader::createPixel(std::string(Outline_frag)); @@ -316,7 +339,7 @@ const gpu::PipelinePointer& DrawOutline::getPipeline(bool isFilled) { gpu::Shader::makeProgram(*program, slotBindings); _pipelineFilled = gpu::Pipeline::create(program, state); } - return isFilled ? _pipelineFilled : _pipeline; + return _mode == M_SOME_FILLED ? _pipelineFilled : _pipeline; } DebugOutline::DebugOutline() { @@ -480,7 +503,7 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende } DrawOutlineMask::Groups sortedBounds; - for (auto i = 0; i < DrawOutline::MAX_GROUP_COUNT; i++) { + for (auto i = 0; i < Outline::MAX_GROUP_COUNT; i++) { const auto groupItems = groups[i]; const auto outlinedItemIDs = task.addJob("OutlineMetaToSubItemIDs", groupItems); const auto outlinedItems = task.addJob("OutlineMetaToSubItems", outlinedItemIDs, true); diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index 6e4c0a18a8..bc10fc5e41 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -60,41 +60,8 @@ private: }; -class DrawOutlineConfig : public render::Job::Config { - Q_OBJECT - Q_PROPERTY(bool glow MEMBER glow NOTIFY dirty) - Q_PROPERTY(float width MEMBER width NOTIFY dirty) - Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty) - Q_PROPERTY(float colorR READ getColorR WRITE setColorR NOTIFY dirty) - Q_PROPERTY(float colorG READ getColorG WRITE setColorG NOTIFY dirty) - Q_PROPERTY(float colorB READ getColorB WRITE setColorB NOTIFY dirty) - Q_PROPERTY(float fillOpacityUnoccluded MEMBER fillOpacityUnoccluded NOTIFY dirty) - Q_PROPERTY(float fillOpacityOccluded MEMBER fillOpacityOccluded NOTIFY dirty) - -public: - - void setColorR(float value) { color.r = value; emit dirty(); } - float getColorR() const { return color.r; } - - void setColorG(float value) { color.g = value; emit dirty(); } - float getColorG() const { return color.g; } - - void setColorB(float value) { color.b = value; emit dirty(); } - float getColorB() const { return color.b; } - - glm::vec3 color{ 1.f, 0.7f, 0.2f }; - float width{ 2.0f }; - float intensity{ 0.9f }; - float fillOpacityUnoccluded{ 0.0f }; - float fillOpacityOccluded{ 0.0f }; - bool glow{ false }; - -signals: - void dirty(); -}; - -class DrawOutline { -private: +class Outline { +protected: #include "Outline_shared.slh" @@ -102,6 +69,68 @@ public: enum { MAX_GROUP_COUNT = GROUP_COUNT }; +}; + +class DrawOutlineConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(int group MEMBER group WRITE setGroup NOTIFY dirty); + Q_PROPERTY(bool glow READ isGlow WRITE setGlow NOTIFY dirty) + Q_PROPERTY(float width READ getWidth WRITE setWidth NOTIFY dirty) + Q_PROPERTY(float intensity READ getIntensity WRITE setIntensity NOTIFY dirty) + Q_PROPERTY(float colorR READ getColorR WRITE setColorR NOTIFY dirty) + Q_PROPERTY(float colorG READ getColorG WRITE setColorG NOTIFY dirty) + Q_PROPERTY(float colorB READ getColorB WRITE setColorB NOTIFY dirty) + Q_PROPERTY(float unoccludedFillOpacity READ getUnoccludedFillOpacity WRITE setUnoccludedFillOpacity NOTIFY dirty) + Q_PROPERTY(float occludedFillOpacity READ getOccludedFillOpacity WRITE setOccludedFillOpacity NOTIFY dirty) + +public: + + struct GroupParameters { + glm::vec3 color{ 1.f, 0.7f, 0.2f }; + float width{ 2.0f }; + float intensity{ 0.9f }; + float unoccludedFillOpacity{ 0.0f }; + float occludedFillOpacity{ 0.0f }; + bool glow{ false }; + }; + + int getGroupCount() const; + + void setColorR(float value) { groupParameters[group].color.r = value; emit dirty(); } + float getColorR() const { return groupParameters[group].color.r; } + + void setColorG(float value) { groupParameters[group].color.g = value; emit dirty(); } + float getColorG() const { return groupParameters[group].color.g; } + + void setColorB(float value) { groupParameters[group].color.b = value; emit dirty(); } + float getColorB() const { return groupParameters[group].color.b; } + + void setGlow(bool value) { groupParameters[group].glow = value; emit dirty(); } + bool isGlow() const { return groupParameters[group].glow; } + + void setWidth(float value) { groupParameters[group].width = value; emit dirty(); } + float getWidth() const { return groupParameters[group].width; } + + void setIntensity(float value) { groupParameters[group].intensity = value; emit dirty(); } + float getIntensity() const { return groupParameters[group].intensity; } + + void setUnoccludedFillOpacity(float value) { groupParameters[group].unoccludedFillOpacity = value; emit dirty(); } + float getUnoccludedFillOpacity() const { return groupParameters[group].unoccludedFillOpacity; } + + void setOccludedFillOpacity(float value) { groupParameters[group].occludedFillOpacity = value; emit dirty(); } + float getOccludedFillOpacity() const { return groupParameters[group].occludedFillOpacity; } + + void setGroup(int value); + + int group{ 0 }; + GroupParameters groupParameters[Outline::MAX_GROUP_COUNT]; + +signals: + void dirty(); +}; + +class DrawOutline : public Outline { +public: using Inputs = render::VaryingSet4; using Config = DrawOutlineConfig; @@ -127,27 +156,27 @@ private: OutlineParameters _groups[MAX_GROUP_COUNT]; }; + enum Mode { + M_ALL_UNFILLED, + M_SOME_FILLED, + }; + using OutlineConfigurationBuffer = gpu::StructBuffer; - static const gpu::PipelinePointer& getPipeline(bool isFilled); + const gpu::PipelinePointer& getPipeline(); static gpu::PipelinePointer _pipeline; static gpu::PipelinePointer _pipelineFilled; OutlineConfigurationBuffer _configuration; - glm::vec3 _color; - float _size; - int _blurKernelSize; - float _intensity; - float _fillOpacityUnoccluded; - float _fillOpacityOccluded; - float _threshold; - bool _hasConfigurationChanged{ true }; + glm::ivec2 _framebufferSize{ 0,0 }; + Mode _mode{ M_ALL_UNFILLED }; + float _sizes[MAX_GROUP_COUNT]; }; class DrawOutlineTask { public: - using Groups = render::VaryingArray; + using Groups = render::VaryingArray; using Inputs = render::VaryingSet4; using Config = render::Task::Config; using JobModel = render::Task::ModelI; @@ -165,7 +194,7 @@ private: class DrawOutlineMask { public: - using Groups = render::VaryingArray; + using Groups = render::VaryingArray; using Inputs = render::VaryingSet2; // Output will contain outlined objects only z-depth texture and the input primary buffer but without the primary depth buffer using Outputs = OutlineRessourcesPointer; diff --git a/libraries/render-utils/src/Outline_shared.slh b/libraries/render-utils/src/Outline_shared.slh index 98d803c28b..bda67c49d6 100644 --- a/libraries/render-utils/src/Outline_shared.slh +++ b/libraries/render-utils/src/Outline_shared.slh @@ -19,8 +19,8 @@ struct OutlineParameters float _intensity; VEC2 _size; - float _fillOpacityUnoccluded; - float _fillOpacityOccluded; + float _unoccludedFillOpacity; + float _occludedFillOpacity; float _threshold; int _blurKernelSize; diff --git a/scripts/developer/utilities/render/outline.qml b/scripts/developer/utilities/render/outline.qml index 1b5cbf6e1c..1270c696d1 100644 --- a/scripts/developer/utilities/render/outline.qml +++ b/scripts/developer/utilities/render/outline.qml @@ -20,6 +20,24 @@ Item { Column { spacing: 8 + ComboBox { + id: groupBox + model: ["Group 0", "Group 1", "Group 2", "Group 3", "Group 4"] + Timer { + id: postpone + interval: 100; running: false; repeat: false + onTriggered: { paramWidgetLoader.sourceComponent = paramWidgets } + } + onCurrentIndexChanged: { + // This is a hack to be sure the widgets below properly reflect the change of category: delete the Component + // by setting the loader source to Null and then recreate it 100ms later + root.drawConfig["group"] = currentIndex + paramWidgetLoader.sourceComponent = undefined; + postpone.interval = 100 + postpone.start() + } + } + CheckBox { text: "View Mask" checked: root.debugConfig["viewMask"] @@ -27,93 +45,105 @@ Item { root.debugConfig["viewMask"] = checked; } } - CheckBox { - text: "Glow" - checked: root.drawConfig["glow"] - onCheckedChanged: { - root.drawConfig["glow"] = checked; - } - } - ConfigSlider { - label: "Width" - integral: false - config: root.drawConfig - property: "width" - max: 15.0 - min: 0.0 - width: 280 - } - ConfigSlider { - label: "Intensity" - integral: false - config: root.drawConfig - property: "intensity" - max: 1.0 - min: 0.0 - width: 280 - } - GroupBox { - title: "Color" - width: 280 + Component { + id: paramWidgets Column { spacing: 8 + CheckBox { + text: "Glow" + checked: root.drawConfig["glow"] + onCheckedChanged: { + drawConfig["glow"] = checked; + } + } + ConfigSlider { + label: "Width" + integral: false + config: root.drawConfig + property: "width" + max: 15.0 + min: 0.0 + width: 280 + } + ConfigSlider { + label: "Intensity" + integral: false + config: root.drawConfig + property: "intensity" + max: 1.0 + min: 0.0 + width: 280 + } - ConfigSlider { - label: "Red" - integral: false - config: root.drawConfig - property: "colorR" - max: 1.0 - min: 0.0 - width: 270 - } - ConfigSlider { - label: "Green" - integral: false - config: root.drawConfig - property: "colorG" - max: 1.0 - min: 0.0 - width: 270 - } - ConfigSlider { - label: "Blue" - integral: false - config: root.drawConfig - property: "colorB" - max: 1.0 - min: 0.0 - width: 270 + GroupBox { + title: "Color" + width: 280 + Column { + spacing: 8 + + ConfigSlider { + label: "Red" + integral: false + config: root.drawConfig + property: "colorR" + max: 1.0 + min: 0.0 + width: 270 + } + ConfigSlider { + label: "Green" + integral: false + config: root.drawConfig + property: "colorG" + max: 1.0 + min: 0.0 + width: 270 + } + ConfigSlider { + label: "Blue" + integral: false + config: root.drawConfig + property: "colorB" + max: 1.0 + min: 0.0 + width: 270 + } + } + } + + GroupBox { + title: "Fill Opacity" + width: 280 + Column { + spacing: 8 + + ConfigSlider { + label: "Unoccluded" + integral: false + config: root.drawConfig + property: "fillOpacityUnoccluded" + max: 1.0 + min: 0.0 + width: 270 + } + ConfigSlider { + label: "Occluded" + integral: false + config: root.drawConfig + property: "fillOpacityOccluded" + max: 1.0 + min: 0.0 + width: 270 + } + } } } } - GroupBox { - title: "Fill Opacity" - width: 280 - Column { - spacing: 8 - - ConfigSlider { - label: "Unoccluded" - integral: false - config: root.drawConfig - property: "fillOpacityUnoccluded" - max: 1.0 - min: 0.0 - width: 270 - } - ConfigSlider { - label: "Occluded" - integral: false - config: root.drawConfig - property: "fillOpacityOccluded" - max: 1.0 - min: 0.0 - width: 270 - } - } + Loader { + id: paramWidgetLoader + sourceComponent: paramWidgets } } } From 76589316dfef0f308355fc7a387ce6a4b20b7982 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 17 Oct 2017 10:24:05 +0200 Subject: [PATCH 12/62] Working debug script with multiple selection --- .../src/ui/overlays/ContextOverlayInterface.h | 2 +- .../utilities/render/debugOutline.js | 99 ++++++++++++++++++- .../developer/utilities/render/outline.qml | 4 +- 3 files changed, 101 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 2a96ea3d3e..28e3707f99 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -49,7 +49,7 @@ class ContextOverlayInterface : public QObject, public Dependency { public: enum { - MAX_HIGHLIGHT_COUNT = 4 + MAX_HIGHLIGHT_COUNT = 5 }; ContextOverlayInterface(); diff --git a/scripts/developer/utilities/render/debugOutline.js b/scripts/developer/utilities/render/debugOutline.js index e333ab5869..ac75197933 100644 --- a/scripts/developer/utilities/render/debugOutline.js +++ b/scripts/developer/utilities/render/debugOutline.js @@ -17,4 +17,101 @@ var window = new OverlayWindow({ width: 285, height: 370, }); -window.closed.connect(function() { Script.stop(); }); \ No newline at end of file +window.closed.connect(function() { Script.stop(); }); + +"use strict"; + +// Created by Sam Gondelman on 9/7/2017 +// Copyright 2017 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 + +(function() { // BEGIN LOCAL_SCOPE + +var END_DIMENSIONS = { + x: 0.15, + y: 0.15, + z: 0.15 +}; +var COLOR = {red: 97, green: 247, blue: 255}; +var end = { + type: "sphere", + dimensions: END_DIMENSIONS, + color: COLOR, + ignoreRayIntersection: true, + alpha: 1.0, + visible: true +} + +var COLOR2 = {red: 247, green: 97, blue: 255}; +var end2 = { + type: "sphere", + dimensions: END_DIMENSIONS, + color: COLOR2, + ignoreRayIntersection: true, + alpha: 1.0, + visible: true +} + +var renderStates = [{name: "test", end: end}]; +var defaultRenderStates = [{name: "test", distance: 20.0, end: end2}]; + +var ray = LaserPointers.createLaserPointer({ + joint: "Mouse", + filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS | RayPick.PICK_AVATARS | RayPick.PICK_INVISIBLE | RayPick.PICK_NONCOLLIDABLE, + renderStates: renderStates, + defaultRenderStates: defaultRenderStates, + enabled: true +}); + +function cleanup() { + LaserPointers.removeLaserPointer(ray); +} +Script.scriptEnding.connect(cleanup); + +var prevID = 0; +var prevType = ""; +function update() { + // you have to do this repeatedly because there's a bug but I'll fix it + LaserPointers.setRenderState(ray, "test"); + + var result = LaserPointers.getPrevRayPickResult(ray); + var selectionName = "contextOverlayHighlightList" + var outlineGroupIndex = Render.getConfig("RenderMainView.OutlineEffect").group + + if (outlineGroupIndex>0) { + selectionName += outlineGroupIndex + } + + if (result.type != RayPick.INTERSECTED_NONE) { + if (result.objectID != prevID) { + if (prevID != 0) { + Selection.removeFromSelectedItemsList(selectionName, prevType, prevID) + } + + var typeName = "" + if (result.type == RayPick.INTERSECTED_ENTITY) { + typeName = "entity" + } else if (result.type == RayPick.INTERSECTED_OVERLAY) { + typeName = "overlay" + } else if (result.type == RayPick.INTERSECTED_AVATAR) { + typeName = "avatar" + } + + Selection.addToSelectedItemsList(selectionName, typeName, result.objectID) + //print("type: " + result.type + ", id: " + result.objectID); + + prevID = result.objectID; + prevType = typeName; + } + } else { + if (prevID != 0) { + Selection.removeFromSelectedItemsList(selectionName, prevType, prevID) + } + prevID = 0; + } +} +Script.update.connect(update); + +}()); // END LOCAL_SCOPE \ No newline at end of file diff --git a/scripts/developer/utilities/render/outline.qml b/scripts/developer/utilities/render/outline.qml index 1270c696d1..d62e3b8899 100644 --- a/scripts/developer/utilities/render/outline.qml +++ b/scripts/developer/utilities/render/outline.qml @@ -122,7 +122,7 @@ Item { label: "Unoccluded" integral: false config: root.drawConfig - property: "fillOpacityUnoccluded" + property: "unoccludedFillOpacity" max: 1.0 min: 0.0 width: 270 @@ -131,7 +131,7 @@ Item { label: "Occluded" integral: false config: root.drawConfig - property: "fillOpacityOccluded" + property: "occludedFillOpacity" max: 1.0 min: 0.0 width: 270 From 25b3549e042f9e21438d0236c4610a641c4c5bb3 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 18 Oct 2017 15:00:53 +0200 Subject: [PATCH 13/62] Working multiple outlines except debugging scripts which applies config to all outlines and graphics bug when filled --- .../ui/overlays/ContextOverlayInterface.cpp | 2 +- .../src/ui/overlays/ContextOverlayInterface.h | 6 +- libraries/render-utils/src/Outline.slh | 81 +--- libraries/render-utils/src/OutlineEffect.cpp | 423 ++++++------------ libraries/render-utils/src/OutlineEffect.h | 178 +++----- libraries/render-utils/src/Outline_shared.slh | 4 - .../render-utils/src/RenderDeferredTask.cpp | 2 +- libraries/render-utils/src/model_outline.slf | 21 - .../render-utils/src/model_outline_fade.slf | 31 -- libraries/render/src/render/Scene.h | 5 + .../utilities/render/debugOutline.js | 12 +- .../developer/utilities/render/outline.qml | 12 +- 12 files changed, 251 insertions(+), 526 deletions(-) delete mode 100644 libraries/render-utils/src/model_outline.slf delete mode 100644 libraries/render-utils/src/model_outline_fade.slf diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index f677dc6550..3a3026d5ae 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -36,7 +36,7 @@ ContextOverlayInterface::ContextOverlayInterface() { _selectionToSceneHandlers[0].initialize("contextOverlayHighlightList"); connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandlers[0], &SelectionToSceneHandler::selectedItemsListChanged); - for (auto i = 1; i < MAX_HIGHLIGHT_COUNT; i++) { + for (auto i = 1; i < render::Scene::MAX_OUTLINE_COUNT ; i++) { _selectionToSceneHandlers[i].initialize(QString("contextOverlayHighlightList")+QString::number(i)); connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandlers[i], &SelectionToSceneHandler::selectedItemsListChanged); } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 28e3707f99..3d33d2a802 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -48,10 +48,6 @@ class ContextOverlayInterface : public QObject, public Dependency { std::shared_ptr _contextOverlay { nullptr }; public: - enum { - MAX_HIGHLIGHT_COUNT = 5 - }; - ContextOverlayInterface(); Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } @@ -91,7 +87,7 @@ private: void deletingEntity(const EntityItemID& entityItemID); - SelectionToSceneHandler _selectionToSceneHandlers[MAX_HIGHLIGHT_COUNT]; + SelectionToSceneHandler _selectionToSceneHandlers[render::Scene::MAX_OUTLINE_COUNT]; }; #endif // hifi_ContextOverlayInterface_h diff --git a/libraries/render-utils/src/Outline.slh b/libraries/render-utils/src/Outline.slh index bc71cabf1e..fe9594cc12 100644 --- a/libraries/render-utils/src/Outline.slh +++ b/libraries/render-utils/src/Outline.slh @@ -16,13 +16,11 @@ <@include Outline_shared.slh@> uniform outlineParamsBuffer { - OutlineParameters groups[GROUP_COUNT]; + OutlineParameters params; }; uniform sampler2D sceneDepthMap; uniform sampler2D outlinedDepthMap; -uniform sampler2D outlinedIdMap; -uniform int enabledGroupsMask; in vec2 varTexCoord0; out vec4 outFragColor; @@ -33,52 +31,47 @@ const float OPACITY_EPSILON = 5e-3; <@func main(IS_FILLED)@> -int getGroupIndexFromColor(vec4 color) { - ivec4 id = ivec4(color * GROUP_ID_COLOR_COMPONENT_MAX) << ivec4(0, GROUP_ID_COLOR_COMPONENT_BITS, GROUP_ID_COLOR_COMPONENT_BITS*2, GROUP_ID_COLOR_COMPONENT_BITS*3); - return (id.r | id.g | id.b | id.a) - 1; -} - -vec4 computeGroupOutline(int centerGroupId, float centerDepth, int groupId, vec2 texCoord) { +void main(void) { + // We offset by half a texel to be centered on the depth sample. If we don't do this + // the blur will have a different width between the left / right sides and top / bottom + // sides of the silhouette + vec2 halfTexel = getInvWidthHeight() / 2; + vec2 texCoord0 = varTexCoord0+halfTexel; + float outlinedDepth = texture(outlinedDepthMap, texCoord0).x; float intensity = 0.0; - if (centerGroupId==groupId && centerDepth < FAR_Z) { - // We're on the outlined object, thus no outline to do! + if (outlinedDepth < FAR_Z) { + // We're not on the far plane so we are on the outlined object, thus no outline to do! <@if IS_FILLED@> - OutlineParameters groupParams = groups[groupId]; - // But we need to fill the interior - float sceneDepth = texture(sceneDepthMap, texCoord).x; + float sceneDepth = texture(sceneDepthMap, texCoord0).x; // Transform to linear depth for better precision - centerDepth = -evalZeyeFromZdb(centerDepth); + outlinedDepth = -evalZeyeFromZdb(outlinedDepth); sceneDepth = -evalZeyeFromZdb(sceneDepth); // Are we occluded? - intensity = (sceneDepth < (centerDepth-LINEAR_DEPTH_BIAS)) ? groupParams._occludedFillOpacity : groupParams._unoccludedFillOpacity; - return vec4(groupParams._color.rgb, intensity); + intensity = sceneDepth < (outlinedDepth-LINEAR_DEPTH_BIAS) ? params._occludedFillOpacity : params._unoccludedFillOpacity; <@else@> - return vec4(0,0,0,0); + discard; <@endif@> } else { - OutlineParameters groupParams = groups[groupId]; float weight = 0.0; - vec2 deltaUv = groupParams._size / groupParams._blurKernelSize; - vec2 lineStartUv = texCoord - groupParams._size / 2.0; + vec2 deltaUv = params._size / params._blurKernelSize; + vec2 lineStartUv = texCoord0 - params._size / 2.0; vec2 uv; int x; int y; - for (y=0 ; y=0.0 && uv.y<=1.0) { - for (x=0 ; x=0.0 && uv.x<=1.0) { - vec4 outlinedIdColor = texture(outlinedIdMap, uv); - float outlinedDepth = texture(outlinedDepthMap, uv).x; - int outlinedId = getGroupIndexFromColor(outlinedIdColor); - intensity += (outlinedDepth diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index 74a29e8237..d00819d0f2 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -18,6 +18,7 @@ #include "gpu/Context.h" #include "gpu/StandardShaderLib.h" +#include #include "surfaceGeometry_copyDepth_frag.h" #include "debug_deferred_buffer_vert.h" @@ -30,22 +31,29 @@ using namespace render; OutlineRessources::OutlineRessources() { } -void OutlineRessources::update(const gpu::TexturePointer& colorBuffer) { +void OutlineRessources::update(const gpu::FramebufferPointer& primaryFrameBuffer) { + auto newFrameSize = glm::ivec2(primaryFrameBuffer->getSize()); + // If the depth buffer or size changed, we need to delete our FBOs and recreate them at the // new correct dimensions. - if (_depthTexture) { - auto newFrameSize = glm::ivec2(colorBuffer->getDimensions()); + if (_depthFrameBuffer) { if (_frameSize != newFrameSize) { _frameSize = newFrameSize; clear(); } } + if (!_colorFrameBuffer) { + if (_frameSize != newFrameSize) { + _frameSize = newFrameSize; + // Failing to recreate this frame buffer when the screen has been resized creates a bug on Mac + _colorFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithoutDepth")); + _colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0)); + } + } } void OutlineRessources::clear() { - _frameBuffer.reset(); - _depthTexture.reset(); - _idTexture.reset(); + _depthFrameBuffer.reset(); } void OutlineRessources::allocate() { @@ -53,177 +61,95 @@ void OutlineRessources::allocate() { auto width = _frameSize.x; auto height = _frameSize.y; auto depthFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); - - _idTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_2, width, height)); - _depthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, width, height)); + auto depthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, width, height)); - _frameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("outlineDepth")); - _frameBuffer->setDepthStencilBuffer(_depthTexture, depthFormat); - _frameBuffer->setRenderBuffer(0, _idTexture); + _depthFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("outlineDepth")); + _depthFrameBuffer->setDepthStencilBuffer(depthTexture, depthFormat); } -gpu::FramebufferPointer OutlineRessources::getFramebuffer() { - if (!_frameBuffer) { +gpu::FramebufferPointer OutlineRessources::getDepthFramebuffer() { + if (!_depthFrameBuffer) { allocate(); } - return _frameBuffer; + return _depthFrameBuffer; } gpu::TexturePointer OutlineRessources::getDepthTexture() { - if (!_depthTexture) { - allocate(); - } - return _depthTexture; -} - -gpu::TexturePointer OutlineRessources::getIdTexture() { - if (!_idTexture) { - allocate(); - } - return _idTexture; -} - -glm::vec4 encodeIdToColor(unsigned int id) { - union { - struct { - unsigned int r : 2; - unsigned int g : 2; - unsigned int b : 2; - unsigned int a : 2; - }; - unsigned char id; - } groupId; - - static_assert(GROUP_ID_COLOR_COMPONENT_BITS == 2, "Assuming two bits per component contrary to GLSL shader code. See Outline_shared.slh"); - - assert(id < 254); - groupId.id = id+1; - - glm::vec4 idColor{ groupId.r, groupId.g, groupId.b, groupId.a }; - - // Normalize. Since we put 2 bits into each color component, each component has a maximum - // value of 3. - idColor /= GROUP_ID_COLOR_COMPONENT_MAX; - return idColor; -} - -void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output) { - assert(renderContext->args); - assert(renderContext->args->hasViewFrustum()); - auto& groups = inputs.get0(); - auto& deferredFrameBuffer = inputs.get1(); - - RenderArgs* args = renderContext->args; - ShapeKey::Builder defaultKeyBuilder; - auto hasOutline = false; - - if (!_outlineRessources) { - _outlineRessources = std::make_shared(); - } - _outlineRessources->update(deferredFrameBuffer->getDeferredColorTexture()); - - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { - args->_batch = &batch; - - auto maskPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder); - auto maskSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned()); - auto colorLoc = maskPipeline.get()->pipeline->getProgram()->getUniforms().findLocation("color"); - auto skinnedColorLoc = maskSkinnedPipeline.get()->pipeline->getProgram()->getUniforms().findLocation("color"); - unsigned int groupId = 0; - - for (auto& inShapeBounds : groups) { - if (!inShapeBounds.isNull()) { - auto& inShapes = inShapeBounds.get(); - - if (!inShapes.empty()) { - if (!hasOutline) { - batch.setFramebuffer(_outlineRessources->getFramebuffer()); - // Clear it only if it hasn't been done before - batch.clearFramebuffer( - gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH, - vec4(0.0f, 0.0f, 0.0f, 0.0f), 1.0f, 0, false); - - // Setup camera, projection and viewport for all items - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - glm::mat4 projMat; - Transform viewMat; - args->getViewFrustum().evalProjectionMatrix(projMat); - args->getViewFrustum().evalViewTransform(viewMat); - - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - hasOutline = true; - } - - std::vector skinnedShapeKeys{}; - // Encode group id in quantized color - glm::vec4 idColor = encodeIdToColor(groupId); - - // Iterate through all inShapes and render the unskinned - args->_shapePipeline = maskPipeline; - batch.setPipeline(maskPipeline->pipeline); - batch._glUniform4f(colorLoc, idColor.r, idColor.g, idColor.b, idColor.a); - for (auto items : inShapes) { - if (items.first.isSkinned()) { - skinnedShapeKeys.push_back(items.first); - } else { - renderItems(renderContext, items.second); - } - } - - // Reiterate to render the skinned - args->_shapePipeline = maskSkinnedPipeline; - batch.setPipeline(maskSkinnedPipeline->pipeline); - batch._glUniform4f(skinnedColorLoc, idColor.r, idColor.g, idColor.b, idColor.a); - for (const auto& key : skinnedShapeKeys) { - renderItems(renderContext, inShapes.at(key)); - } - } - } - - ++groupId; - } - - args->_shapePipeline = nullptr; - args->_batch = nullptr; - }); - - if (hasOutline) { - output = _outlineRessources; - } else { - output = nullptr; - } + return getDepthFramebuffer()->getDepthStencilBuffer(); } PrepareDrawOutline::PrepareDrawOutline() { - + _ressources = std::make_shared(); } void PrepareDrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { auto destinationFrameBuffer = inputs; - auto framebufferSize = destinationFrameBuffer->getSize(); - if (!_primaryWithoutDepthBuffer || framebufferSize != _frameBufferSize) { - // Failing to recreate this frame buffer when the screen has been resized creates a bug on Mac - _primaryWithoutDepthBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithoutDepth")); - _primaryWithoutDepthBuffer->setRenderBuffer(0, destinationFrameBuffer->getRenderBuffer(0)); - _frameBufferSize = framebufferSize; + _ressources->update(destinationFrameBuffer); + outputs = _ressources; +} + +void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + auto& inShapes = inputs.get0(); + + if (!inShapes.empty()) { + auto ressources = inputs.get1(); + + RenderArgs* args = renderContext->args; + ShapeKey::Builder defaultKeyBuilder; + + outputs = args->_viewport; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + + auto maskPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder); + auto maskSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned()); + + batch.setFramebuffer(ressources->getDepthFramebuffer()); + batch.clearDepthFramebuffer(1.0f); + + // Setup camera, projection and viewport for all items + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); + + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + std::vector skinnedShapeKeys{}; + + // Iterate through all inShapes and render the unskinned + args->_shapePipeline = maskPipeline; + batch.setPipeline(maskPipeline->pipeline); + for (auto items : inShapes) { + if (items.first.isSkinned()) { + skinnedShapeKeys.push_back(items.first); + } else { + renderItems(renderContext, items.second); + } + } + + // Reiterate to render the skinned + args->_shapePipeline = maskSkinnedPipeline; + batch.setPipeline(maskSkinnedPipeline->pipeline); + for (const auto& key : skinnedShapeKeys) { + renderItems(renderContext, inShapes.at(key)); + } + + args->_shapePipeline = nullptr; + args->_batch = nullptr; + }); + } else { + // Outline rect should be null as there are no outlined shapes + outputs = glm::ivec4(0, 0, 0, 0); } - - outputs = _primaryWithoutDepthBuffer; -} - -int DrawOutlineConfig::getGroupCount() const { - return Outline::MAX_GROUP_COUNT; -} - -void DrawOutlineConfig::setGroup(int value) { - assert(value >= 0 && value < Outline::MAX_GROUP_COUNT); - group = std::min(value, Outline::MAX_GROUP_COUNT); - group = std::max(group, 0); - emit dirty(); } gpu::PipelinePointer DrawOutline::_pipeline; @@ -236,41 +162,28 @@ void DrawOutline::configure(const Config& config) { auto& configuration = _configuration.edit(); const auto OPACITY_EPSILON = 5e-3f; - bool someFilled = false; - bool isFilled; + configuration._color = config.color; + configuration._intensity = config.intensity * (config.glow ? 2.f : 1.f); + configuration._unoccludedFillOpacity = config.unoccludedFillOpacity; + configuration._occludedFillOpacity = config.occludedFillOpacity; + configuration._threshold = config.glow ? 1.f : 1e-3f; + configuration._blurKernelSize = std::min(10, std::max(2, (int)floorf(config.width * 3 + 0.5f))); + // Size is in normalized screen height. We decide that for outline width = 1, this is equal to 1/400. + _size = config.width / 400.0f; + configuration._size.x = (_size * _framebufferSize.y) / _framebufferSize.x; + configuration._size.y = _size; - for (auto groupId = 0; groupId < MAX_GROUP_COUNT; groupId++) { - auto& dstGroupConfig = configuration._groups[groupId]; - auto& srcGroupConfig = config.groupParameters[groupId]; - - dstGroupConfig._color = srcGroupConfig.color; - dstGroupConfig._intensity = srcGroupConfig.intensity * (srcGroupConfig.glow ? 2.f : 1.f); - dstGroupConfig._unoccludedFillOpacity = srcGroupConfig.unoccludedFillOpacity; - dstGroupConfig._occludedFillOpacity = srcGroupConfig.occludedFillOpacity; - dstGroupConfig._threshold = srcGroupConfig.glow ? 1.f : 1e-3f; - dstGroupConfig._blurKernelSize = std::min(10, std::max(2, (int)floorf(srcGroupConfig.width * 2 + 0.5f))); - // Size is in normalized screen height. We decide that for outline width = 1, this is equal to 1/400. - _sizes[groupId] = srcGroupConfig.width / 400.0f; - - isFilled = (srcGroupConfig.unoccludedFillOpacity > OPACITY_EPSILON || srcGroupConfig.occludedFillOpacity > OPACITY_EPSILON); - someFilled = someFilled || isFilled; - } - - if (someFilled) { - _mode = M_SOME_FILLED; - } else { - _mode = M_ALL_UNFILLED; - } + _isFilled = (config.unoccludedFillOpacity > OPACITY_EPSILON || config.occludedFillOpacity > OPACITY_EPSILON); } void DrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { auto outlineFrameBuffer = inputs.get1(); + auto outlineRect = inputs.get4(); - if (outlineFrameBuffer) { + if (outlineFrameBuffer && outlineRect.z>0 && outlineRect.w>0) { auto sceneDepthBuffer = inputs.get2(); const auto frameTransform = inputs.get0(); auto outlinedDepthTexture = outlineFrameBuffer->getDepthTexture(); - auto outlinedIdTexture = outlineFrameBuffer->getIdTexture(); auto destinationFrameBuffer = inputs.get3(); auto framebufferSize = glm::ivec2(outlinedDepthTexture->getDimensions()); @@ -281,13 +194,8 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I if (_framebufferSize != framebufferSize) { auto& configuration = _configuration.edit(); - - for (auto groupId = 0; groupId < MAX_GROUP_COUNT; groupId++) { - auto& groupConfig = configuration._groups[groupId]; - - groupConfig._size.x = (_sizes[groupId] * framebufferSize.y) / framebufferSize.x; - groupConfig._size.y = _sizes[groupId]; - } + configuration._size.x = (_size * framebufferSize.y) / framebufferSize.x; + configuration._size.y = _size; _framebufferSize = framebufferSize; } @@ -301,14 +209,10 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport)); batch.setPipeline(pipeline); - auto enabledGroupsLoc = pipeline->getProgram()->getUniforms().findLocation("enabledGroupsMask"); - batch.setUniformBuffer(OUTLINE_PARAMS_SLOT, _configuration); batch.setUniformBuffer(FRAME_TRANSFORM_SLOT, frameTransform->getFrameTransformBuffer()); batch.setResourceTexture(SCENE_DEPTH_SLOT, sceneDepthBuffer->getPrimaryDepthTexture()); batch.setResourceTexture(OUTLINED_DEPTH_SLOT, outlinedDepthTexture); - batch.setResourceTexture(OUTLINED_ID_SLOT, outlinedIdTexture); - batch._glUniform1i(enabledGroupsLoc, 1); batch.draw(gpu::TRIANGLE_STRIP, 4); }); } @@ -326,7 +230,6 @@ const gpu::PipelinePointer& DrawOutline::getPipeline() { slotBindings.insert(gpu::Shader::Binding("deferredFrameTransformBuffer", FRAME_TRANSFORM_SLOT)); slotBindings.insert(gpu::Shader::Binding("sceneDepthMap", SCENE_DEPTH_SLOT)); slotBindings.insert(gpu::Shader::Binding("outlinedDepthMap", OUTLINED_DEPTH_SLOT)); - slotBindings.insert(gpu::Shader::Binding("outlinedIdMap", OUTLINED_ID_SLOT)); gpu::Shader::makeProgram(*program, slotBindings); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); @@ -339,19 +242,17 @@ const gpu::PipelinePointer& DrawOutline::getPipeline() { gpu::Shader::makeProgram(*program, slotBindings); _pipelineFilled = gpu::Pipeline::create(program, state); } - return _mode == M_SOME_FILLED ? _pipelineFilled : _pipeline; + return _isFilled ? _pipelineFilled : _pipeline; } DebugOutline::DebugOutline() { _geometryDepthId = DependencyManager::get()->allocateID(); - _geometryColorId = DependencyManager::get()->allocateID(); } DebugOutline::~DebugOutline() { auto geometryCache = DependencyManager::get(); if (geometryCache) { geometryCache->releaseID(_geometryDepthId); - geometryCache->releaseID(_geometryColorId); } } @@ -360,9 +261,9 @@ void DebugOutline::configure(const Config& config) { } void DebugOutline::run(const render::RenderContextPointer& renderContext, const Inputs& input) { - const auto outlineFramebuffer = input; + const auto outlineRessources = input; - if (_isDisplayEnabled && outlineFramebuffer) { + if (_isDisplayEnabled && outlineRessources) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); RenderArgs* args = renderContext->args; @@ -384,20 +285,10 @@ void DebugOutline::run(const render::RenderContextPointer& renderContext, const const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); batch.setPipeline(getDepthPipeline()); - batch.setResourceTexture(0, outlineFramebuffer->getDepthTexture()); - { - const glm::vec2 bottomLeft(-1.0f, -1.0f); - const glm::vec2 topRight(0.0f, 1.0f); - geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryDepthId); - } - - batch.setPipeline(getIdPipeline()); - batch.setResourceTexture(0, outlineFramebuffer->getIdTexture()); - { - const glm::vec2 bottomLeft(0.0f, -1.0f); - const glm::vec2 topRight(1.0f, 1.0f); - geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryColorId); - } + batch.setResourceTexture(0, outlineRessources->getDepthTexture()); + const glm::vec2 bottomLeft(-1.0f, -1.0f); + const glm::vec2 topRight(1.0f, 1.0f); + geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryDepthId); batch.setResourceTexture(0, nullptr); }); @@ -439,27 +330,6 @@ void DebugOutline::initializePipelines() { _depthPipeline = gpu::Pipeline::create(program, state); } - - // ID shader - { - static const std::string ID_SHADER{ - "vec4 getFragmentColor() {" - " return texelFetch(albedoMap, ivec2(gl_FragCoord.xy), 0); " - "}" - }; - - auto fragmentShader = FRAGMENT_SHADER; - fragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), ID_SHADER); - - const auto ps = gpu::Shader::createPixel(fragmentShader); - const auto program = gpu::Shader::createProgram(vs, ps); - - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding("albedoMap", 0)); - gpu::Shader::makeProgram(*program, slotBindings); - - _idPipeline = gpu::Pipeline::create(program, state); - } } const gpu::PipelinePointer& DebugOutline::getDepthPipeline() { @@ -470,14 +340,6 @@ const gpu::PipelinePointer& DebugOutline::getDepthPipeline() { return _depthPipeline; } -const gpu::PipelinePointer& DebugOutline::getIdPipeline() { - if (!_idPipeline) { - initializePipelines(); - } - - return _idPipeline; -} - DrawOutlineTask::DrawOutlineTask() { } @@ -496,70 +358,63 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende ShapePlumberPointer shapePlumber = std::make_shared(); { auto state = std::make_shared(); - state->setDepthTest(true, true, gpu::LESS_EQUAL); - state->setColorWriteMask(true, true, true, true); - + state->setDepthTest(true, true, gpu::LESS); + state->setColorWriteMask(false, false, false, false); initMaskPipelines(*shapePlumber, state); } - DrawOutlineMask::Groups sortedBounds; - for (auto i = 0; i < Outline::MAX_GROUP_COUNT; i++) { + // Prepare for outline group rendering. + const auto outlineRessources = task.addJob("PrepareOutline", primaryFramebuffer); + + for (auto i = 0; i < render::Scene::MAX_OUTLINE_COUNT; i++) { const auto groupItems = groups[i]; const auto outlinedItemIDs = task.addJob("OutlineMetaToSubItemIDs", groupItems); const auto outlinedItems = task.addJob("OutlineMetaToSubItems", outlinedItemIDs, true); // Sort const auto sortedPipelines = task.addJob("OutlinePipelineSort", outlinedItems); - sortedBounds[i] = task.addJob("OutlineDepthSort", sortedPipelines); + const auto sortedBounds = task.addJob("OutlineDepthSort", sortedPipelines); + + // Draw depth of outlined objects in separate buffer + std::string name; + { + std::ostringstream stream; + stream << "OutlineMask" << i; + name = stream.str(); + } + const auto drawMaskInputs = DrawOutlineMask::Inputs(sortedBounds, outlineRessources).asVarying(); + const auto outlinedRect = task.addJob(name, drawMaskInputs, shapePlumber); + + // Draw outline + { + std::ostringstream stream; + stream << "OutlineEffect" << i; + name = stream.str(); + } + const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlineRessources, sceneFrameBuffer, primaryFramebuffer, outlinedRect).asVarying(); + task.addJob(name, drawOutlineInputs); } - // Draw depth of outlined objects in separate buffer - const auto drawMaskInputs = DrawOutlineMask::Inputs(sortedBounds, sceneFrameBuffer).asVarying(); - const auto outlinedFrameBuffer = task.addJob("OutlineMask", drawMaskInputs, shapePlumber); - - // Prepare for outline group rendering. - const auto destFrameBuffer = task.addJob("PrepareOutline", primaryFramebuffer); - - // Draw outline - const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlinedFrameBuffer, sceneFrameBuffer, destFrameBuffer).asVarying(); - task.addJob("OutlineEffect", drawOutlineInputs); - // Debug outline - task.addJob("OutlineDebug", outlinedFrameBuffer); + task.addJob("OutlineDebug", outlineRessources); } #include "model_shadow_vert.h" -#include "model_shadow_fade_vert.h" #include "skin_model_shadow_vert.h" -#include "skin_model_shadow_fade_vert.h" -#include "model_outline_frag.h" -#include "model_outline_fade_frag.h" +#include "model_shadow_frag.h" void DrawOutlineTask::initMaskPipelines(render::ShapePlumber& shapePlumber, gpu::StatePointer state) { auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); - auto modelPixel = gpu::Shader::createPixel(std::string(model_outline_frag)); + auto modelPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(modelVertex, modelPixel); shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withoutSkinned().withoutFade(), + ShapeKey::Filter::Builder().withoutSkinned(), modelProgram, state); auto skinVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert)); gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(skinVertex, modelPixel); shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withSkinned().withoutFade(), + ShapeKey::Filter::Builder().withSkinned(), skinProgram, state); - - auto modelFadeVertex = gpu::Shader::createVertex(std::string(model_shadow_fade_vert)); - auto modelFadePixel = gpu::Shader::createPixel(std::string(model_outline_fade_frag)); - gpu::ShaderPointer modelFadeProgram = gpu::Shader::createProgram(modelFadeVertex, modelFadePixel); - shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withoutSkinned().withFade(), - modelFadeProgram, state); - - auto skinFadeVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_fade_vert)); - gpu::ShaderPointer skinFadeProgram = gpu::Shader::createProgram(skinFadeVertex, modelFadePixel); - shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withSkinned().withFade(), - skinFadeProgram, state); } diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index bc10fc5e41..b1c2e3c3dd 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -20,12 +20,13 @@ class OutlineRessources { public: OutlineRessources(); - gpu::FramebufferPointer getFramebuffer(); - gpu::TexturePointer getIdTexture(); + gpu::FramebufferPointer getDepthFramebuffer(); gpu::TexturePointer getDepthTexture(); + gpu::FramebufferPointer getColorFramebuffer() { return _colorFrameBuffer; } + // Update the source framebuffer size which will drive the allocation of all the other resources. - void update(const gpu::TexturePointer& colorBuffer); + void update(const gpu::FramebufferPointer& primaryFrameBuffer); const glm::ivec2& getSourceFrameSize() const { return _frameSize; } protected: @@ -33,9 +34,8 @@ protected: void clear(); void allocate(); - gpu::FramebufferPointer _frameBuffer; - gpu::TexturePointer _depthTexture; - gpu::TexturePointer _idTexture; + gpu::FramebufferPointer _depthFrameBuffer; + gpu::FramebufferPointer _colorFrameBuffer; glm::ivec2 _frameSize; }; @@ -45,9 +45,8 @@ using OutlineRessourcesPointer = std::shared_ptr; class PrepareDrawOutline { public: using Inputs = gpu::FramebufferPointer; - using Outputs = gpu::FramebufferPointer; - using Config = render::Job::Config; - using JobModel = render::Job::ModelIO; + using Outputs = OutlineRessourcesPointer; + using JobModel = render::Job::ModelIO; PrepareDrawOutline(); @@ -55,84 +54,63 @@ public: private: - gpu::FramebufferPointer _primaryWithoutDepthBuffer; - gpu::Vec2u _frameBufferSize{ 0, 0 }; + OutlineRessourcesPointer _ressources; }; -class Outline { +class DrawOutlineMask { +public: + + using Inputs = render::VaryingSet2; + using Outputs = glm::ivec4; + using JobModel = render::Job::ModelIO; + + DrawOutlineMask(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); + protected: -#include "Outline_shared.slh" - -public: - enum { - MAX_GROUP_COUNT = GROUP_COUNT - }; + render::ShapePlumberPointer _shapePlumber; }; class DrawOutlineConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(int group MEMBER group WRITE setGroup NOTIFY dirty); - Q_PROPERTY(bool glow READ isGlow WRITE setGlow NOTIFY dirty) - Q_PROPERTY(float width READ getWidth WRITE setWidth NOTIFY dirty) - Q_PROPERTY(float intensity READ getIntensity WRITE setIntensity NOTIFY dirty) + Q_PROPERTY(bool glow MEMBER glow NOTIFY dirty) + Q_PROPERTY(float width MEMBER width NOTIFY dirty) + Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty) Q_PROPERTY(float colorR READ getColorR WRITE setColorR NOTIFY dirty) Q_PROPERTY(float colorG READ getColorG WRITE setColorG NOTIFY dirty) Q_PROPERTY(float colorB READ getColorB WRITE setColorB NOTIFY dirty) - Q_PROPERTY(float unoccludedFillOpacity READ getUnoccludedFillOpacity WRITE setUnoccludedFillOpacity NOTIFY dirty) - Q_PROPERTY(float occludedFillOpacity READ getOccludedFillOpacity WRITE setOccludedFillOpacity NOTIFY dirty) + Q_PROPERTY(float unoccludedFillOpacity MEMBER unoccludedFillOpacity NOTIFY dirty) + Q_PROPERTY(float occludedFillOpacity MEMBER occludedFillOpacity NOTIFY dirty) public: - struct GroupParameters { - glm::vec3 color{ 1.f, 0.7f, 0.2f }; - float width{ 2.0f }; - float intensity{ 0.9f }; - float unoccludedFillOpacity{ 0.0f }; - float occludedFillOpacity{ 0.0f }; - bool glow{ false }; - }; + void setColorR(float value) { color.r = value; emit dirty(); } + float getColorR() const { return color.r; } - int getGroupCount() const; + void setColorG(float value) { color.g = value; emit dirty(); } + float getColorG() const { return color.g; } - void setColorR(float value) { groupParameters[group].color.r = value; emit dirty(); } - float getColorR() const { return groupParameters[group].color.r; } + void setColorB(float value) { color.b = value; emit dirty(); } + float getColorB() const { return color.b; } - void setColorG(float value) { groupParameters[group].color.g = value; emit dirty(); } - float getColorG() const { return groupParameters[group].color.g; } - - void setColorB(float value) { groupParameters[group].color.b = value; emit dirty(); } - float getColorB() const { return groupParameters[group].color.b; } - - void setGlow(bool value) { groupParameters[group].glow = value; emit dirty(); } - bool isGlow() const { return groupParameters[group].glow; } - - void setWidth(float value) { groupParameters[group].width = value; emit dirty(); } - float getWidth() const { return groupParameters[group].width; } - - void setIntensity(float value) { groupParameters[group].intensity = value; emit dirty(); } - float getIntensity() const { return groupParameters[group].intensity; } - - void setUnoccludedFillOpacity(float value) { groupParameters[group].unoccludedFillOpacity = value; emit dirty(); } - float getUnoccludedFillOpacity() const { return groupParameters[group].unoccludedFillOpacity; } - - void setOccludedFillOpacity(float value) { groupParameters[group].occludedFillOpacity = value; emit dirty(); } - float getOccludedFillOpacity() const { return groupParameters[group].occludedFillOpacity; } - - void setGroup(int value); - - int group{ 0 }; - GroupParameters groupParameters[Outline::MAX_GROUP_COUNT]; + glm::vec3 color{ 1.f, 0.7f, 0.2f }; + float width{ 2.0f }; + float intensity{ 0.9f }; + float unoccludedFillOpacity{ 0.0f }; + float occludedFillOpacity{ 0.0f }; + bool glow{ false }; signals: void dirty(); }; -class DrawOutline : public Outline { +class DrawOutline { public: - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet5; using Config = DrawOutlineConfig; using JobModel = render::Job::ModelI; @@ -143,71 +121,27 @@ public: private: +#include "Outline_shared.slh" + enum { SCENE_DEPTH_SLOT = 0, OUTLINED_DEPTH_SLOT, - OUTLINED_ID_SLOT, OUTLINE_PARAMS_SLOT = 0, FRAME_TRANSFORM_SLOT, }; - struct OutlineConfiguration { - OutlineParameters _groups[MAX_GROUP_COUNT]; - }; - - enum Mode { - M_ALL_UNFILLED, - M_SOME_FILLED, - }; - - using OutlineConfigurationBuffer = gpu::StructBuffer; + using OutlineConfigurationBuffer = gpu::StructBuffer; const gpu::PipelinePointer& getPipeline(); static gpu::PipelinePointer _pipeline; static gpu::PipelinePointer _pipelineFilled; + OutlineConfigurationBuffer _configuration; glm::ivec2 _framebufferSize{ 0,0 }; - Mode _mode{ M_ALL_UNFILLED }; - float _sizes[MAX_GROUP_COUNT]; -}; - -class DrawOutlineTask { -public: - - using Groups = render::VaryingArray; - using Inputs = render::VaryingSet4; - using Config = render::Task::Config; - using JobModel = render::Task::ModelI; - - DrawOutlineTask(); - - void configure(const Config& config); - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); - -private: - - static void initMaskPipelines(render::ShapePlumber& plumber, gpu::StatePointer state); -}; - -class DrawOutlineMask { -public: - - using Groups = render::VaryingArray; - using Inputs = render::VaryingSet2; - // Output will contain outlined objects only z-depth texture and the input primary buffer but without the primary depth buffer - using Outputs = OutlineRessourcesPointer; - using JobModel = render::Job::ModelIO; - - DrawOutlineMask(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} - - void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output); - -protected: - - render::ShapePlumberPointer _shapePlumber; - OutlineRessourcesPointer _outlineRessources; + bool _isFilled{ false }; + float _size; }; class DebugOutlineConfig : public render::Job::Config { @@ -237,16 +171,30 @@ public: private: gpu::PipelinePointer _depthPipeline; - gpu::PipelinePointer _idPipeline; int _geometryDepthId{ 0 }; - int _geometryColorId{ 0 }; bool _isDisplayEnabled{ false }; const gpu::PipelinePointer& getDepthPipeline(); - const gpu::PipelinePointer& getIdPipeline(); void initializePipelines(); }; +class DrawOutlineTask { +public: + + using Groups = render::VaryingArray; + using Inputs = render::VaryingSet4; + using Config = render::Task::Config; + using JobModel = render::Task::ModelI; + + DrawOutlineTask(); + + void configure(const Config& config); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); + +private: + + static void initMaskPipelines(render::ShapePlumber& plumber, gpu::StatePointer state); +}; #endif // hifi_render_utils_OutlineEffect_h diff --git a/libraries/render-utils/src/Outline_shared.slh b/libraries/render-utils/src/Outline_shared.slh index bda67c49d6..2ebece8903 100644 --- a/libraries/render-utils/src/Outline_shared.slh +++ b/libraries/render-utils/src/Outline_shared.slh @@ -9,10 +9,6 @@ # define VEC4 vec4 #endif -#define GROUP_COUNT 7 -#define GROUP_ID_COLOR_COMPONENT_BITS 2 -#define GROUP_ID_COLOR_COMPONENT_MAX 3 - struct OutlineParameters { VEC3 _color; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 73d8b93c07..54c2248d8b 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -181,7 +181,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto selectedItems = addSelectItemJobs(task, selectionBaseName, metas, opaques, transparents); DrawOutlineTask::Groups outlineGroups; outlineGroups[0] = selectedItems; - for (auto i = 1; i < DrawOutline::MAX_GROUP_COUNT; i++) { + for (auto i = 1; i < render::Scene::MAX_OUTLINE_COUNT; i++) { std::ostringstream selectionName; selectionName << selectionBaseName; selectionName << i; diff --git a/libraries/render-utils/src/model_outline.slf b/libraries/render-utils/src/model_outline.slf deleted file mode 100644 index 8c11b2b295..0000000000 --- a/libraries/render-utils/src/model_outline.slf +++ /dev/null @@ -1,21 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// model_outline.frag -// fragment shader -// -// Created by Olivier Prat on 10/13/17. -// Copyright 2017 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 -// - -layout(location = 0) out vec4 _fragColor; - -uniform vec4 color; - -void main(void) { - _fragColor = color; -} diff --git a/libraries/render-utils/src/model_outline_fade.slf b/libraries/render-utils/src/model_outline_fade.slf deleted file mode 100644 index 69ff54dea6..0000000000 --- a/libraries/render-utils/src/model_outline_fade.slf +++ /dev/null @@ -1,31 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// model_outline_fade.frag -// fragment shader -// -// Created by Olivier Prat on 10/13/17. -// Copyright 2017 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 Fade.slh@> -<$declareFadeFragment()$> - -layout(location = 0) out vec4 _fragColor; - -uniform vec4 color; - -in vec4 _worldPosition; - -void main(void) { - FadeObjectParams fadeParams; - - <$fetchFadeObjectParams(fadeParams)$> - applyFadeClip(fadeParams, _worldPosition.xyz); - - _fragColor = color; -} diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index fef2077897..04a285bcd1 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -102,6 +102,11 @@ typedef std::queue TransactionQueue; // Items are notified accordingly on any update message happening class Scene { public: + + enum { + MAX_OUTLINE_COUNT = 16 + }; + Scene(glm::vec3 origin, float size); ~Scene(); diff --git a/scripts/developer/utilities/render/debugOutline.js b/scripts/developer/utilities/render/debugOutline.js index ac75197933..8e8a00f912 100644 --- a/scripts/developer/utilities/render/debugOutline.js +++ b/scripts/developer/utilities/render/debugOutline.js @@ -54,6 +54,15 @@ var end2 = { visible: true } +var outlineGroupIndex = 0 + +function setOutlineGroupIndex(index) { + print("Switching to outline group "+index) + outlineGroupIndex = index +} + +window.fromQml.connect(setOutlineGroupIndex); + var renderStates = [{name: "test", end: end}]; var defaultRenderStates = [{name: "test", distance: 20.0, end: end2}]; @@ -78,7 +87,6 @@ function update() { var result = LaserPointers.getPrevRayPickResult(ray); var selectionName = "contextOverlayHighlightList" - var outlineGroupIndex = Render.getConfig("RenderMainView.OutlineEffect").group if (outlineGroupIndex>0) { selectionName += outlineGroupIndex @@ -100,7 +108,7 @@ function update() { } Selection.addToSelectedItemsList(selectionName, typeName, result.objectID) - //print("type: " + result.type + ", id: " + result.objectID); + print("OUTLINE " + outlineGroupIndex + " picked type: " + result.type + ", id: " + result.objectID); prevID = result.objectID; prevType = typeName; diff --git a/scripts/developer/utilities/render/outline.qml b/scripts/developer/utilities/render/outline.qml index d62e3b8899..1e19b2cac9 100644 --- a/scripts/developer/utilities/render/outline.qml +++ b/scripts/developer/utilities/render/outline.qml @@ -15,7 +15,8 @@ import "configSlider" Item { id: root property var debugConfig: Render.getConfig("RenderMainView.OutlineDebug") - property var drawConfig: Render.getConfig("RenderMainView.OutlineEffect") + property var drawConfig: Render.getConfig("RenderMainView.OutlineEffect0") + signal sendToScript(var message); Column { spacing: 8 @@ -26,12 +27,15 @@ Item { Timer { id: postpone interval: 100; running: false; repeat: false - onTriggered: { paramWidgetLoader.sourceComponent = paramWidgets } + onTriggered: { + paramWidgetLoader.sourceComponent = paramWidgets; + sendToScript(currentIndex) + } } onCurrentIndexChanged: { - // This is a hack to be sure the widgets below properly reflect the change of category: delete the Component + // This is a hack to be sure the widgets below properly reflect the change of index: delete the Component // by setting the loader source to Null and then recreate it 100ms later - root.drawConfig["group"] = currentIndex + root.drawConfig = Render.getConfig("RenderMainView.OutlineEffect"+currentIndex) paramWidgetLoader.sourceComponent = undefined; postpone.interval = 100 postpone.start() From 8cabd1c9530c2395053df294ea76546a180c0837 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 18 Oct 2017 15:06:54 +0200 Subject: [PATCH 14/62] Fixed graphics bug with filled --- libraries/render-utils/src/OutlineEffect.cpp | 6 +++--- libraries/render-utils/src/OutlineEffect.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index d00819d0f2..d028e028d4 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -178,13 +178,13 @@ void DrawOutline::configure(const Config& config) { void DrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { auto outlineFrameBuffer = inputs.get1(); - auto outlineRect = inputs.get4(); + auto outlineRect = inputs.get3(); if (outlineFrameBuffer && outlineRect.z>0 && outlineRect.w>0) { auto sceneDepthBuffer = inputs.get2(); const auto frameTransform = inputs.get0(); auto outlinedDepthTexture = outlineFrameBuffer->getDepthTexture(); - auto destinationFrameBuffer = inputs.get3(); + auto destinationFrameBuffer = outlineFrameBuffer->getColorFramebuffer(); auto framebufferSize = glm::ivec2(outlinedDepthTexture->getDimensions()); if (sceneDepthBuffer) { @@ -391,7 +391,7 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende stream << "OutlineEffect" << i; name = stream.str(); } - const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlineRessources, sceneFrameBuffer, primaryFramebuffer, outlinedRect).asVarying(); + const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlineRessources, sceneFrameBuffer, outlinedRect).asVarying(); task.addJob(name, drawOutlineInputs); } diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index b1c2e3c3dd..a099166267 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -110,7 +110,7 @@ signals: class DrawOutline { public: - using Inputs = render::VaryingSet5; + using Inputs = render::VaryingSet4; using Config = DrawOutlineConfig; using JobModel = render::Job::ModelI; From f63b4a64b050ded997e33b577565903cfbd019bc Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 18 Oct 2017 16:02:38 +0200 Subject: [PATCH 15/62] Working debugging script with tabs --- .../utilities/render/debugOutline.js | 6 +- .../developer/utilities/render/outline.qml | 211 +++++++++--------- 2 files changed, 106 insertions(+), 111 deletions(-) diff --git a/scripts/developer/utilities/render/debugOutline.js b/scripts/developer/utilities/render/debugOutline.js index 8e8a00f912..5ac7bcd6aa 100644 --- a/scripts/developer/utilities/render/debugOutline.js +++ b/scripts/developer/utilities/render/debugOutline.js @@ -14,8 +14,8 @@ var qml = Script.resolvePath('outline.qml'); var window = new OverlayWindow({ title: 'Outline', source: qml, - width: 285, - height: 370, + width: 400, + height: 400, }); window.closed.connect(function() { Script.stop(); }); @@ -108,7 +108,7 @@ function update() { } Selection.addToSelectedItemsList(selectionName, typeName, result.objectID) - print("OUTLINE " + outlineGroupIndex + " picked type: " + result.type + ", id: " + result.objectID); + //print("OUTLINE " + outlineGroupIndex + " picked type: " + result.type + ", id: " + result.objectID); prevID = result.objectID; prevType = typeName; diff --git a/scripts/developer/utilities/render/outline.qml b/scripts/developer/utilities/render/outline.qml index 1e19b2cac9..c686af33c6 100644 --- a/scripts/developer/utilities/render/outline.qml +++ b/scripts/developer/utilities/render/outline.qml @@ -15,34 +15,14 @@ import "configSlider" Item { id: root property var debugConfig: Render.getConfig("RenderMainView.OutlineDebug") - property var drawConfig: Render.getConfig("RenderMainView.OutlineEffect0") signal sendToScript(var message); Column { spacing: 8 - - ComboBox { - id: groupBox - model: ["Group 0", "Group 1", "Group 2", "Group 3", "Group 4"] - Timer { - id: postpone - interval: 100; running: false; repeat: false - onTriggered: { - paramWidgetLoader.sourceComponent = paramWidgets; - sendToScript(currentIndex) - } - } - onCurrentIndexChanged: { - // This is a hack to be sure the widgets below properly reflect the change of index: delete the Component - // by setting the loader source to Null and then recreate it 100ms later - root.drawConfig = Render.getConfig("RenderMainView.OutlineEffect"+currentIndex) - paramWidgetLoader.sourceComponent = undefined; - postpone.interval = 100 - postpone.start() - } - } + anchors.fill: parent CheckBox { + id: debug text: "View Mask" checked: root.debugConfig["viewMask"] onCheckedChanged: { @@ -50,104 +30,119 @@ Item { } } - Component { - id: paramWidgets - Column { - spacing: 8 - CheckBox { - text: "Glow" - checked: root.drawConfig["glow"] - onCheckedChanged: { - drawConfig["glow"] = checked; - } - } - ConfigSlider { - label: "Width" - integral: false - config: root.drawConfig - property: "width" - max: 15.0 - min: 0.0 - width: 280 - } - ConfigSlider { - label: "Intensity" - integral: false - config: root.drawConfig - property: "intensity" - max: 1.0 - min: 0.0 - width: 280 - } + TabView { + id: tabs + width: 384 + height: 400 - GroupBox { - title: "Color" - width: 280 - Column { - spacing: 8 + onCurrentIndexChanged: { + sendToScript(currentIndex) + } - ConfigSlider { - label: "Red" - integral: false - config: root.drawConfig - property: "colorR" - max: 1.0 - min: 0.0 - width: 270 - } - ConfigSlider { - label: "Green" - integral: false - config: root.drawConfig - property: "colorG" - max: 1.0 - min: 0.0 - width: 270 - } - ConfigSlider { - label: "Blue" - integral: false - config: root.drawConfig - property: "colorB" - max: 1.0 - min: 0.0 - width: 270 + Component { + id: paramWidgets + + Column { + spacing: 8 + + CheckBox { + id: glow + text: "Glow" + checked: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex)["glow"] + onCheckedChanged: { + Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex)["glow"] = checked; } } - } + ConfigSlider { + label: "Width" + integral: false + config: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex) + property: "width" + max: 15.0 + min: 0.0 + width: 280 + } + ConfigSlider { + label: "Intensity" + integral: false + config: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex) + property: "intensity" + max: 1.0 + min: 0.0 + width: 280 + } - GroupBox { - title: "Fill Opacity" - width: 280 - Column { - spacing: 8 + GroupBox { + title: "Color" + width: 280 + Column { + spacing: 8 - ConfigSlider { - label: "Unoccluded" - integral: false - config: root.drawConfig - property: "unoccludedFillOpacity" - max: 1.0 - min: 0.0 - width: 270 - } - ConfigSlider { - label: "Occluded" - integral: false - config: root.drawConfig - property: "occludedFillOpacity" - max: 1.0 - min: 0.0 - width: 270 + ConfigSlider { + label: "Red" + integral: false + config: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex) + property: "colorR" + max: 1.0 + min: 0.0 + width: 270 + } + ConfigSlider { + label: "Green" + integral: false + config: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex) + property: "colorG" + max: 1.0 + min: 0.0 + width: 270 + } + ConfigSlider { + label: "Blue" + integral: false + config: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex) + property: "colorB" + max: 1.0 + min: 0.0 + width: 270 + } + } + } + + GroupBox { + title: "Fill Opacity" + width: 280 + Column { + spacing: 8 + + ConfigSlider { + label: "Unoccluded" + integral: false + config: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex) + property: "unoccludedFillOpacity" + max: 1.0 + min: 0.0 + width: 270 + } + ConfigSlider { + label: "Occluded" + integral: false + config: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex) + property: "occludedFillOpacity" + max: 1.0 + min: 0.0 + width: 270 + } } } } } } + } - Loader { - id: paramWidgetLoader - sourceComponent: paramWidgets + Component.onCompleted: { + for (var i=0 ; i<4 ; i++) { + var outlinePage = tabs.addTab("Outl. "+i, paramWidgets) + outlinePage.active = true } } } From f890c5bb8cac7e5cd28726088058791b85a55131 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 18 Oct 2017 19:40:03 +0200 Subject: [PATCH 16/62] Added project rect and scissor test. Need to increase bounds to take into account blur width --- libraries/render-utils/src/OutlineEffect.cpp | 83 +++++++++++++++++--- libraries/render-utils/src/OutlineEffect.h | 4 +- libraries/shared/src/ViewFrustum.cpp | 55 +++++++------ libraries/shared/src/ViewFrustum.h | 5 ++ 4 files changed, 112 insertions(+), 35 deletions(-) diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index d028e028d4..36faee8119 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -11,6 +11,9 @@ #include "OutlineEffect.h" #include "GeometryCache.h" +#include "RenderUtilsLogging.h" + +#include "CubeProjectedPolygon.h" #include #include @@ -99,8 +102,13 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con RenderArgs* args = renderContext->args; ShapeKey::Builder defaultKeyBuilder; + auto framebufferSize = ressources->getSourceFrameSize(); - outputs = args->_viewport; + // First thing we do is determine the projected bounding rect of all the outlined items + auto outlinedRect = computeOutlineRect(inShapes, args->getViewFrustum(), framebufferSize); + qCDebug(renderutils) << "Outline rect is " << outlinedRect.x << ' ' << outlinedRect.y << ' ' << outlinedRect.z << ' ' << outlinedRect.w; + + outputs = outlinedRect; gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; @@ -108,18 +116,17 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con auto maskPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder); auto maskSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned()); - batch.setFramebuffer(ressources->getDepthFramebuffer()); - batch.clearDepthFramebuffer(1.0f); - - // Setup camera, projection and viewport for all items - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - glm::mat4 projMat; Transform viewMat; args->getViewFrustum().evalProjectionMatrix(projMat); args->getViewFrustum().evalViewTransform(viewMat); + batch.setStateScissorRect(outlinedRect); + batch.setFramebuffer(ressources->getDepthFramebuffer()); + batch.clearDepthFramebuffer(1.0f, true); + + // Setup camera, projection and viewport for all items + batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); @@ -152,6 +159,49 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con } } +glm::ivec4 DrawOutlineMask::computeOutlineRect(const render::ShapeBounds& shapes, + const ViewFrustum& viewFrustum, glm::ivec2 frameSize) { + glm::vec4 minMaxBounds{ + std::numeric_limits::max(), + std::numeric_limits::max(), + -std::numeric_limits::max(), + -std::numeric_limits::max(), + }; + + for (const auto& keyShapes : shapes) { + const auto& items = keyShapes.second; + + for (const auto& item : items) { + const auto& aabb = item.bound; + const auto projectedCube = viewFrustum.getProjectedPolygon(aabb); + + if (projectedCube.getAnyInView()) { + minMaxBounds.x = std::min(minMaxBounds.x, projectedCube.getMinX()); + minMaxBounds.y = std::min(minMaxBounds.y, projectedCube.getMinY()); + minMaxBounds.z = std::max(minMaxBounds.z, projectedCube.getMaxX()); + minMaxBounds.w = std::max(minMaxBounds.w, projectedCube.getMaxY()); + } + } + } + + if (minMaxBounds.x != std::numeric_limits::max()) { + const glm::vec2 halfFrameSize{ frameSize.x*0.5f, frameSize.y*0.5f }; + glm::ivec4 rect; + + minMaxBounds += 1.0f; + rect.x = glm::clamp((int)floorf(minMaxBounds.x * halfFrameSize.x), 0, frameSize.x); + rect.y = glm::clamp((int)floorf(minMaxBounds.y * halfFrameSize.y), 0, frameSize.y); + rect.z = glm::clamp((int)ceilf(minMaxBounds.z * halfFrameSize.x), 0, frameSize.x); + rect.w = glm::clamp((int)ceilf(minMaxBounds.w * halfFrameSize.y), 0, frameSize.y); + + rect.z -= rect.x; + rect.w -= rect.y; + return rect; + } else { + return glm::ivec4(0, 0, 0, 0); + } +} + gpu::PipelinePointer DrawOutline::_pipeline; gpu::PipelinePointer DrawOutline::_pipelineFilled; @@ -203,6 +253,7 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I batch.enableStereo(false); batch.setFramebuffer(destinationFrameBuffer); + batch.setStateScissorRect(outlineRect); batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); @@ -235,6 +286,7 @@ const gpu::PipelinePointer& DrawOutline::getPipeline() { gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setDepthTest(gpu::State::DepthTest(false, false)); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); + state->setScissorEnable(true); _pipeline = gpu::Pipeline::create(program, state); ps = gpu::Shader::createPixel(std::string(Outline_filled_frag)); @@ -261,7 +313,8 @@ void DebugOutline::configure(const Config& config) { } void DebugOutline::run(const render::RenderContextPointer& renderContext, const Inputs& input) { - const auto outlineRessources = input; + const auto outlineRessources = input.get0(); + const auto outlineRect = input.get1(); if (_isDisplayEnabled && outlineRessources) { assert(renderContext->args); @@ -271,6 +324,7 @@ void DebugOutline::run(const render::RenderContextPointer& renderContext, const gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(outlineRect); const auto geometryBuffer = DependencyManager::get(); @@ -305,6 +359,7 @@ void DebugOutline::initializePipelines() { auto state = std::make_shared(); state->setDepthTest(gpu::State::DepthTest(false)); + state->setScissorEnable(true); const auto vs = gpu::Shader::createVertex(VERTEX_SHADER); @@ -360,16 +415,18 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende auto state = std::make_shared(); state->setDepthTest(true, true, gpu::LESS); state->setColorWriteMask(false, false, false, false); + state->setScissorEnable(true); initMaskPipelines(*shapePlumber, state); } // Prepare for outline group rendering. const auto outlineRessources = task.addJob("PrepareOutline", primaryFramebuffer); + render::Varying outline0Rect; for (auto i = 0; i < render::Scene::MAX_OUTLINE_COUNT; i++) { const auto groupItems = groups[i]; const auto outlinedItemIDs = task.addJob("OutlineMetaToSubItemIDs", groupItems); - const auto outlinedItems = task.addJob("OutlineMetaToSubItems", outlinedItemIDs, true); + const auto outlinedItems = task.addJob("OutlineMetaToSubItems", outlinedItemIDs); // Sort const auto sortedPipelines = task.addJob("OutlinePipelineSort", outlinedItems); @@ -384,6 +441,9 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende } const auto drawMaskInputs = DrawOutlineMask::Inputs(sortedBounds, outlineRessources).asVarying(); const auto outlinedRect = task.addJob(name, drawMaskInputs, shapePlumber); + if (i == 0) { + outline0Rect = outlinedRect; + } // Draw outline { @@ -396,7 +456,8 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende } // Debug outline - task.addJob("OutlineDebug", outlineRessources); + const auto debugInputs = DebugOutline::Inputs(outlineRessources, const_cast(outline0Rect)).asVarying(); + task.addJob("OutlineDebug", debugInputs); } #include "model_shadow_vert.h" diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index a099166267..878bdf2c04 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -72,6 +72,8 @@ public: protected: render::ShapePlumberPointer _shapePlumber; + + static glm::ivec4 computeOutlineRect(const render::ShapeBounds& shapes, const ViewFrustum& viewFrustum, glm::ivec2 frameSize); }; class DrawOutlineConfig : public render::Job::Config { @@ -158,7 +160,7 @@ signals: class DebugOutline { public: - using Inputs = OutlineRessourcesPointer; + using Inputs = render::VaryingSet2; using Config = DebugOutlineConfig; using JobModel = render::Job::ModelI; diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index 978221e167..f08c85a38a 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -504,16 +504,17 @@ const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_PROJECTED_POLYGON_VERT {6, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, // back, top, left }; -CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AACube& box) const { +template +CubeProjectedPolygon ViewFrustum::computeProjectedPolygon(const TBOX& box) const { const glm::vec3& bottomNearRight = box.getCorner(); glm::vec3 topFarLeft = box.calcTopFarLeft(); - int lookUp = ((_position.x < bottomNearRight.x) ) // 1 = right | compute 6-bit - + ((_position.x > topFarLeft.x ) << 1) // 2 = left | code to - + ((_position.y < bottomNearRight.y) << 2) // 4 = bottom | classify camera - + ((_position.y > topFarLeft.y ) << 3) // 8 = top | with respect to - + ((_position.z < bottomNearRight.z) << 4) // 16 = front/near | the 6 defining - + ((_position.z > topFarLeft.z ) << 5); // 32 = back/far | planes + int lookUp = ((_position.x < bottomNearRight.x)) // 1 = right | compute 6-bit + + ((_position.x > topFarLeft.x) << 1) // 2 = left | code to + + ((_position.y < bottomNearRight.y) << 2) // 4 = bottom | classify camera + + ((_position.y > topFarLeft.y) << 3) // 8 = top | with respect to + + ((_position.z < bottomNearRight.z) << 4) // 16 = front/near | the 6 defining + + ((_position.z > topFarLeft.z) << 5); // 32 = back/far | planes int vertexCount = hullVertexLookup[lookUp][0]; //look up number of vertices @@ -524,8 +525,8 @@ CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AACube& box) const { bool anyPointsInView = false; // assume the worst! if (vertexCount) { allPointsInView = true; // assume the best! - for(int i = 0; i < vertexCount; i++) { - int vertexNum = hullVertexLookup[lookUp][i+1]; + for (int i = 0; i < vertexCount; i++) { + int vertexNum = hullVertexLookup[lookUp][i + 1]; glm::vec3 point = box.getVertex((BoxVertex)vertexNum); glm::vec2 projectedPoint = projectPoint(point, pointInView); allPointsInView = allPointsInView && pointInView; @@ -538,24 +539,24 @@ CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AACube& box) const { // NOTE: This clipping does not improve our overall performance. It basically causes more polygons to // end up in the same quad/half and so the polygon lists get longer, and that's more calls to polygon.occludes() if ( (projectedPolygon.getMaxX() > PolygonClip::RIGHT_OF_CLIPPING_WINDOW ) || - (projectedPolygon.getMaxY() > PolygonClip::TOP_OF_CLIPPING_WINDOW ) || - (projectedPolygon.getMaxX() < PolygonClip::LEFT_OF_CLIPPING_WINDOW ) || - (projectedPolygon.getMaxY() < PolygonClip::BOTTOM_OF_CLIPPING_WINDOW) ) { + (projectedPolygon.getMaxY() > PolygonClip::TOP_OF_CLIPPING_WINDOW ) || + (projectedPolygon.getMaxX() < PolygonClip::LEFT_OF_CLIPPING_WINDOW ) || + (projectedPolygon.getMaxY() < PolygonClip::BOTTOM_OF_CLIPPING_WINDOW) ) { - CoverageRegion::_clippedPolygons++; + CoverageRegion::_clippedPolygons++; - glm::vec2* clippedVertices; - int clippedVertexCount; - PolygonClip::clipToScreen(projectedPolygon.getVertices(), vertexCount, clippedVertices, clippedVertexCount); + glm::vec2* clippedVertices; + int clippedVertexCount; + PolygonClip::clipToScreen(projectedPolygon.getVertices(), vertexCount, clippedVertices, clippedVertexCount); - // Now reset the vertices of our projectedPolygon object - projectedPolygon.setVertexCount(clippedVertexCount); - for(int i = 0; i < clippedVertexCount; i++) { - projectedPolygon.setVertex(i, clippedVertices[i]); - } - delete[] clippedVertices; + // Now reset the vertices of our projectedPolygon object + projectedPolygon.setVertexCount(clippedVertexCount); + for(int i = 0; i < clippedVertexCount; i++) { + projectedPolygon.setVertex(i, clippedVertices[i]); + } + delete[] clippedVertices; - lookUp += PROJECTION_CLIPPED; + lookUp += PROJECTION_CLIPPED; } ***/ } @@ -568,6 +569,14 @@ CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AACube& box) const { return projectedPolygon; } +CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AACube& box) const { + return computeProjectedPolygon(box); +} + +CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { + return computeProjectedPolygon(box); +} + // Similar strategy to getProjectedPolygon() we use the knowledge of camera position relative to the // axis-aligned voxels to determine which of the voxels vertices must be the furthest. No need for // squares and square-roots. Just compares. diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index d1b88fb2a5..c428e83397 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -119,6 +119,7 @@ public: glm::vec2 projectPoint(glm::vec3 point, bool& pointInView) const; CubeProjectedPolygon getProjectedPolygon(const AACube& box) const; + CubeProjectedPolygon getProjectedPolygon(const AABox& box) const; void getFurthestPointFromCamera(const AACube& box, glm::vec3& furthestPoint) const; float distanceToCamera(const glm::vec3& point) const; @@ -169,6 +170,10 @@ private: // Used to project points glm::mat4 _ourModelViewProjectionMatrix; + + template + CubeProjectedPolygon computeProjectedPolygon(const TBOX& box) const; + }; using ViewFrustumPointer = std::shared_ptr; From cc30c0b841dd5a6baef7fe61411870d3b509f8c8 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 19 Oct 2017 11:35:26 +0200 Subject: [PATCH 17/62] Expanded scissor rect with outline blur width --- libraries/render-utils/src/OutlineEffect.cpp | 48 +++++++++++++++++--- libraries/render-utils/src/OutlineEffect.h | 19 +++++++- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index 36faee8119..918f769277 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -81,6 +81,10 @@ gpu::TexturePointer OutlineRessources::getDepthTexture() { return getDepthFramebuffer()->getDepthStencilBuffer(); } +OutlineSharedParameters::OutlineSharedParameters() { + std::fill(_blurPixelWidths.begin(), _blurPixelWidths.end(), 0); +} + PrepareDrawOutline::PrepareDrawOutline() { _ressources = std::make_shared(); } @@ -92,6 +96,13 @@ void PrepareDrawOutline::run(const render::RenderContextPointer& renderContext, outputs = _ressources; } +DrawOutlineMask::DrawOutlineMask(unsigned int outlineIndex, + render::ShapePlumberPointer shapePlumber, OutlineSharedParametersPointer parameters) : + _outlineIndex{ outlineIndex }, + _shapePlumber { shapePlumber }, + _parameters{ parameters } { +} + void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); @@ -106,9 +117,12 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con // First thing we do is determine the projected bounding rect of all the outlined items auto outlinedRect = computeOutlineRect(inShapes, args->getViewFrustum(), framebufferSize); + auto blurPixelWidth = _parameters->_blurPixelWidths[_outlineIndex]; qCDebug(renderutils) << "Outline rect is " << outlinedRect.x << ' ' << outlinedRect.y << ' ' << outlinedRect.z << ' ' << outlinedRect.w; - outputs = outlinedRect; + // Add 1 pixel of extra margin to be on the safe side + outputs = expandRect(outlinedRect, blurPixelWidth+1, framebufferSize); + outlinedRect = expandRect(outputs, blurPixelWidth+1, framebufferSize); gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; @@ -202,10 +216,28 @@ glm::ivec4 DrawOutlineMask::computeOutlineRect(const render::ShapeBounds& shapes } } +glm::ivec4 DrawOutlineMask::expandRect(glm::ivec4 rect, int amount, glm::ivec2 frameSize) { + // Go bo back to min max values + rect.z += rect.x; + rect.w += rect.y; + + rect.x = std::max(0, rect.x - amount); + rect.y = std::max(0, rect.y - amount); + rect.z = std::min(frameSize.x, rect.z + amount); + rect.w = std::min(frameSize.y, rect.w + amount); + + // Back to width height + rect.z -= rect.x; + rect.w -= rect.y; + return rect; +} + gpu::PipelinePointer DrawOutline::_pipeline; gpu::PipelinePointer DrawOutline::_pipelineFilled; -DrawOutline::DrawOutline() { +DrawOutline::DrawOutline(unsigned int outlineIndex, OutlineSharedParametersPointer parameters) : + _outlineIndex{ outlineIndex }, + _parameters{ parameters } { } void DrawOutline::configure(const Config& config) { @@ -222,7 +254,7 @@ void DrawOutline::configure(const Config& config) { _size = config.width / 400.0f; configuration._size.x = (_size * _framebufferSize.y) / _framebufferSize.x; configuration._size.y = _size; - + _parameters->_blurPixelWidths[_outlineIndex] = (int)ceilf(_size * _framebufferSize.y); _isFilled = (config.unoccludedFillOpacity > OPACITY_EPSILON || config.occludedFillOpacity > OPACITY_EPSILON); } @@ -247,18 +279,19 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I configuration._size.x = (_size * framebufferSize.y) / framebufferSize.x; configuration._size.y = _size; _framebufferSize = framebufferSize; + _parameters->_blurPixelWidths[_outlineIndex] = (int)ceilf(_size * _framebufferSize.y); } gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setFramebuffer(destinationFrameBuffer); - batch.setStateScissorRect(outlineRect); batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport)); batch.setPipeline(pipeline); + batch.setStateScissorRect(outlineRect); batch.setUniformBuffer(OUTLINE_PARAMS_SLOT, _configuration); batch.setUniformBuffer(FRAME_TRANSFORM_SLOT, frameTransform->getFrameTransformBuffer()); @@ -410,7 +443,7 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende const auto deferredFrameTransform = inputs.getN(3); // Prepare the ShapePipeline - ShapePlumberPointer shapePlumber = std::make_shared(); + auto shapePlumber = std::make_shared(); { auto state = std::make_shared(); state->setDepthTest(true, true, gpu::LESS); @@ -418,6 +451,7 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende state->setScissorEnable(true); initMaskPipelines(*shapePlumber, state); } + auto sharedParameters = std::make_shared(); // Prepare for outline group rendering. const auto outlineRessources = task.addJob("PrepareOutline", primaryFramebuffer); @@ -440,7 +474,7 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende name = stream.str(); } const auto drawMaskInputs = DrawOutlineMask::Inputs(sortedBounds, outlineRessources).asVarying(); - const auto outlinedRect = task.addJob(name, drawMaskInputs, shapePlumber); + const auto outlinedRect = task.addJob(name, drawMaskInputs, i, shapePlumber, sharedParameters); if (i == 0) { outline0Rect = outlinedRect; } @@ -452,7 +486,7 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende name = stream.str(); } const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlineRessources, sceneFrameBuffer, outlinedRect).asVarying(); - task.addJob(name, drawOutlineInputs); + task.addJob(name, drawOutlineInputs, i, sharedParameters); } // Debug outline diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index 878bdf2c04..ab603ecab6 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -42,6 +42,16 @@ protected: using OutlineRessourcesPointer = std::shared_ptr; +class OutlineSharedParameters { +public: + + OutlineSharedParameters(); + + std::array _blurPixelWidths; +}; + +using OutlineSharedParametersPointer = std::shared_ptr; + class PrepareDrawOutline { public: using Inputs = gpu::FramebufferPointer; @@ -65,15 +75,18 @@ public: using Outputs = glm::ivec4; using JobModel = render::Job::ModelIO; - DrawOutlineMask(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + DrawOutlineMask(unsigned int outlineIndex, render::ShapePlumberPointer shapePlumber, OutlineSharedParametersPointer parameters); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); protected: + unsigned int _outlineIndex; render::ShapePlumberPointer _shapePlumber; + OutlineSharedParametersPointer _parameters; static glm::ivec4 computeOutlineRect(const render::ShapeBounds& shapes, const ViewFrustum& viewFrustum, glm::ivec2 frameSize); + static glm::ivec4 expandRect(glm::ivec4 rect, int amount, glm::ivec2 frameSize); }; class DrawOutlineConfig : public render::Job::Config { @@ -116,7 +129,7 @@ public: using Config = DrawOutlineConfig; using JobModel = render::Job::ModelI; - DrawOutline(); + DrawOutline(unsigned int outlineIndex, OutlineSharedParametersPointer parameters); void configure(const Config& config); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); @@ -140,6 +153,8 @@ private: static gpu::PipelinePointer _pipeline; static gpu::PipelinePointer _pipelineFilled; + unsigned int _outlineIndex; + OutlineSharedParametersPointer _parameters; OutlineConfigurationBuffer _configuration; glm::ivec2 _framebufferSize{ 0,0 }; bool _isFilled{ false }; From 111966b987bc5115628a83a623475b8c2117456d Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 19 Oct 2017 12:39:34 +0200 Subject: [PATCH 18/62] Fixed potential bug with outline frame buffer allocations. Still problems with avatar outline rect --- libraries/render-utils/src/OutlineEffect.cpp | 53 ++++++++++---------- libraries/render-utils/src/OutlineEffect.h | 8 +-- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index 918f769277..6e313d262a 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -37,43 +37,36 @@ OutlineRessources::OutlineRessources() { void OutlineRessources::update(const gpu::FramebufferPointer& primaryFrameBuffer) { auto newFrameSize = glm::ivec2(primaryFrameBuffer->getSize()); - // If the depth buffer or size changed, we need to delete our FBOs and recreate them at the + // If the buffer size changed, we need to delete our FBOs and recreate them at the // new correct dimensions. - if (_depthFrameBuffer) { - if (_frameSize != newFrameSize) { - _frameSize = newFrameSize; - clear(); + if (_frameSize != newFrameSize) { + _frameSize = newFrameSize; + allocateDepthBuffer(primaryFrameBuffer); + allocateColorBuffer(primaryFrameBuffer); + } else { + if (!_depthFrameBuffer) { + allocateDepthBuffer(primaryFrameBuffer); } - } - if (!_colorFrameBuffer) { - if (_frameSize != newFrameSize) { - _frameSize = newFrameSize; - // Failing to recreate this frame buffer when the screen has been resized creates a bug on Mac - _colorFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithoutDepth")); - _colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0)); + if (!_colorFrameBuffer) { + allocateColorBuffer(primaryFrameBuffer); } } } -void OutlineRessources::clear() { - _depthFrameBuffer.reset(); +void OutlineRessources::allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { + _colorFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithoutDepth")); + _colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0)); } -void OutlineRessources::allocate() { - - auto width = _frameSize.x; - auto height = _frameSize.y; +void OutlineRessources::allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { auto depthFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); - auto depthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, width, height)); - + auto depthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, _frameSize.x, _frameSize.y)); _depthFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("outlineDepth")); _depthFrameBuffer->setDepthStencilBuffer(depthTexture, depthFormat); } gpu::FramebufferPointer OutlineRessources::getDepthFramebuffer() { - if (!_depthFrameBuffer) { - allocate(); - } + assert(_depthFrameBuffer); return _depthFrameBuffer; } @@ -81,6 +74,11 @@ gpu::TexturePointer OutlineRessources::getDepthTexture() { return getDepthFramebuffer()->getDepthStencilBuffer(); } +gpu::FramebufferPointer OutlineRessources::getColorFramebuffer() { + assert(_colorFrameBuffer); + return _colorFrameBuffer; +} + OutlineSharedParameters::OutlineSharedParameters() { std::fill(_blurPixelWidths.begin(), _blurPixelWidths.end(), 0); } @@ -305,6 +303,11 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I const gpu::PipelinePointer& DrawOutline::getPipeline() { if (!_pipeline) { + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false, false)); + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); + state->setScissorEnable(true); + auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); auto ps = gpu::Shader::createPixel(std::string(Outline_frag)); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); @@ -316,10 +319,6 @@ const gpu::PipelinePointer& DrawOutline::getPipeline() { slotBindings.insert(gpu::Shader::Binding("outlinedDepthMap", OUTLINED_DEPTH_SLOT)); gpu::Shader::makeProgram(*program, slotBindings); - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setDepthTest(gpu::State::DepthTest(false, false)); - state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); - state->setScissorEnable(true); _pipeline = gpu::Pipeline::create(program, state); ps = gpu::Shader::createPixel(std::string(Outline_filled_frag)); diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index ab603ecab6..b4be75484e 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -23,7 +23,7 @@ public: gpu::FramebufferPointer getDepthFramebuffer(); gpu::TexturePointer getDepthTexture(); - gpu::FramebufferPointer getColorFramebuffer() { return _colorFrameBuffer; } + gpu::FramebufferPointer getColorFramebuffer(); // Update the source framebuffer size which will drive the allocation of all the other resources. void update(const gpu::FramebufferPointer& primaryFrameBuffer); @@ -31,13 +31,13 @@ public: protected: - void clear(); - void allocate(); - gpu::FramebufferPointer _depthFrameBuffer; gpu::FramebufferPointer _colorFrameBuffer; glm::ivec2 _frameSize; + + void allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer); + void allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer); }; using OutlineRessourcesPointer = std::shared_ptr; From 382262da3db5d47d0d21c03e8e9ee8d96633c19c Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 19 Oct 2017 16:23:13 +0200 Subject: [PATCH 19/62] Working scissor with correct projected bounding box rect --- libraries/render-utils/src/OutlineEffect.cpp | 13 +-- libraries/shared/src/ViewFrustum.cpp | 96 ++++++++++++++++++-- libraries/shared/src/ViewFrustum.h | 1 + 3 files changed, 98 insertions(+), 12 deletions(-) diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index 6e313d262a..19d3847e50 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -185,13 +185,14 @@ glm::ivec4 DrawOutlineMask::computeOutlineRect(const render::ShapeBounds& shapes for (const auto& item : items) { const auto& aabb = item.bound; - const auto projectedCube = viewFrustum.getProjectedPolygon(aabb); + glm::vec2 bottomLeft; + glm::vec2 topRight; - if (projectedCube.getAnyInView()) { - minMaxBounds.x = std::min(minMaxBounds.x, projectedCube.getMinX()); - minMaxBounds.y = std::min(minMaxBounds.y, projectedCube.getMinY()); - minMaxBounds.z = std::max(minMaxBounds.z, projectedCube.getMaxX()); - minMaxBounds.w = std::max(minMaxBounds.w, projectedCube.getMaxY()); + if (viewFrustum.getProjectedRect(aabb, bottomLeft, topRight)) { + minMaxBounds.x = std::min(minMaxBounds.x, bottomLeft.x); + minMaxBounds.y = std::min(minMaxBounds.y, bottomLeft.y); + minMaxBounds.z = std::max(minMaxBounds.z, topRight.x); + minMaxBounds.w = std::max(minMaxBounds.w, topRight.y); } } } diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index f08c85a38a..747b39f79e 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include @@ -509,12 +510,12 @@ CubeProjectedPolygon ViewFrustum::computeProjectedPolygon(const TBOX& box) const const glm::vec3& bottomNearRight = box.getCorner(); glm::vec3 topFarLeft = box.calcTopFarLeft(); - int lookUp = ((_position.x < bottomNearRight.x)) // 1 = right | compute 6-bit - + ((_position.x > topFarLeft.x) << 1) // 2 = left | code to - + ((_position.y < bottomNearRight.y) << 2) // 4 = bottom | classify camera - + ((_position.y > topFarLeft.y) << 3) // 8 = top | with respect to - + ((_position.z < bottomNearRight.z) << 4) // 16 = front/near | the 6 defining - + ((_position.z > topFarLeft.z) << 5); // 32 = back/far | planes + int lookUp = ((_position.x < bottomNearRight.x)) // 1 = right | compute 6-bit + + ((_position.x > topFarLeft.x) << 1) // 2 = left | code to + + ((_position.y < bottomNearRight.y) << 2) // 4 = bottom | classify camera + + ((_position.y > topFarLeft.y) << 3) // 8 = top | with respect to + + ((_position.z < bottomNearRight.z) << 4) // 16 = front/near | the 6 defining + + ((_position.z > topFarLeft.z) << 5); // 32 = back/far | planes int vertexCount = hullVertexLookup[lookUp][0]; //look up number of vertices @@ -577,6 +578,89 @@ CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { return computeProjectedPolygon(box); } +bool ViewFrustum::getProjectedRect(const AABox& box, glm::vec2& bottomLeft, glm::vec2& topRight) const { + using Edge = std::pair; + + const int VERTEX_COUNT = 8; + const int EDGE_COUNT = 12; + // In theory, after clipping a box with a plane, only 4 new vertices at max + // should be created but due to potential imprecisions (edge almost parallel to + // near plane for instance) there might be more + const int MAX_VERTEX_COUNT = VERTEX_COUNT + 4 + 2; + + std::array vertices; + std::array boxEdges{ + Edge(BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR), + Edge(TOP_LEFT_NEAR, TOP_RIGHT_NEAR), + Edge(BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR), + Edge(TOP_LEFT_FAR, TOP_RIGHT_FAR), + Edge(BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR), + Edge(BOTTOM_LEFT_FAR, TOP_LEFT_FAR), + Edge(BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR), + Edge(BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR), + Edge(BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR), + Edge(TOP_LEFT_NEAR, TOP_LEFT_FAR), + Edge(BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR), + Edge(TOP_RIGHT_NEAR, TOP_RIGHT_FAR) + }; + std::array distancesToNearPlane; + std::bitset areVerticesInside; + int vertexCount = VERTEX_COUNT; + int i; + + // Clip the hull with the near plane. + const auto& nearPlane = _planes[NEAR_PLANE]; + + for (i = 0; i < VERTEX_COUNT; i++) { + vertices[i] = box.getVertex(static_cast(i)); + distancesToNearPlane[i] = nearPlane.distance(vertices[i]); + } + + for (i = 0; i < EDGE_COUNT; i++) { + const auto& edgeVertexIndices = boxEdges[i]; + const auto& startVertex = vertices[edgeVertexIndices.first]; + const auto& endVertex = vertices[edgeVertexIndices.second]; + float startVertexDistance = distancesToNearPlane[edgeVertexIndices.first]; + float endVertexDistance = distancesToNearPlane[edgeVertexIndices.second]; + bool isStartPointInside = startVertexDistance >= 0.0f; + bool isEndPointInside = endVertexDistance >= 0.0f; + + areVerticesInside.set(edgeVertexIndices.first, isStartPointInside); + areVerticesInside.set(edgeVertexIndices.second, isEndPointInside); + + if (isStartPointInside != isEndPointInside) { + // One of the two vertices is behind the near plane so add a new clipped vertex + // add tag it as projectable. + vertices[vertexCount] = startVertex + (endVertex - startVertex) * (startVertexDistance / (startVertexDistance - endVertexDistance)); + areVerticesInside.set(vertexCount); + vertexCount++; + } + } + + // Project points that are inside + bottomLeft.x = std::numeric_limits::max(); + bottomLeft.y = std::numeric_limits::max(); + topRight.x = -std::numeric_limits::max(); + topRight.y = -std::numeric_limits::max(); + for (i = 0; i < vertexCount; i++) { + if (areVerticesInside[i]) { + bool isPointInside; + auto projectedPoint = projectPoint(vertices[i], isPointInside); + bottomLeft.x = std::min(bottomLeft.x, projectedPoint.x); + bottomLeft.y = std::min(bottomLeft.y, projectedPoint.y); + topRight.x = std::max(topRight.x, projectedPoint.x); + topRight.y = std::max(topRight.y, projectedPoint.y); + } + } + + bottomLeft.x = glm::clamp(bottomLeft.x, -1.0f, 1.0f); + bottomLeft.y = glm::clamp(bottomLeft.y, -1.0f, 1.0f); + topRight.x = glm::clamp(topRight.x, -1.0f, 1.0f); + topRight.y = glm::clamp(topRight.y, -1.0f, 1.0f); + + return areVerticesInside.any(); +} + // Similar strategy to getProjectedPolygon() we use the knowledge of camera position relative to the // axis-aligned voxels to determine which of the voxels vertices must be the furthest. No need for // squares and square-roots. Just compares. diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index c428e83397..98f666d666 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -120,6 +120,7 @@ public: glm::vec2 projectPoint(glm::vec3 point, bool& pointInView) const; CubeProjectedPolygon getProjectedPolygon(const AACube& box) const; CubeProjectedPolygon getProjectedPolygon(const AABox& box) const; + bool getProjectedRect(const AABox& box, glm::vec2& bottomLeft, glm::vec2& topRight) const; void getFurthestPointFromCamera(const AACube& box, glm::vec3& furthestPoint) const; float distanceToCamera(const glm::vec3& point) const; From fc66dcfdea154849606a90354806f156da05fea4 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 19 Oct 2017 16:52:29 +0200 Subject: [PATCH 20/62] Added outline to statsGPU script --- libraries/render-utils/src/RenderDeferredTask.cpp | 4 ++-- scripts/developer/utilities/render/statsGPU.qml | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 54c2248d8b..dc4bdd14cc 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -176,6 +176,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying(); task.addJob("ToneMapping", toneMappingInputs); + const auto outlineRangeTimer = task.addJob("BeginOutlineRangeTimer", "Outline"); // Select items that need to be outlined const auto selectionBaseName = "contextOverlayHighlightList"; const auto selectedItems = addSelectItemJobs(task, selectionBaseName, metas, opaques, transparents); @@ -187,12 +188,11 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren selectionName << i; outlineGroups[i] = addSelectItemJobs(task, selectionName.str().c_str(), metas, opaques, transparents); } - const auto outlineRangeTimer = task.addJob("BeginOutlineRangeTimer", "Outline"); const auto outlineInputs = DrawOutlineTask::Inputs(outlineGroups, deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying(); task.addJob("DrawOutline", outlineInputs); - task.addJob("EndOutlineRangeTimer", outlineRangeTimer); + task.addJob("OutlineRangeTimer", outlineRangeTimer); { // DEbug the bounds of the rendered items, still look at the zbuffer task.addJob("DrawMetaBounds", metas); diff --git a/scripts/developer/utilities/render/statsGPU.qml b/scripts/developer/utilities/render/statsGPU.qml index 6b80f00af3..8d284c11ca 100644 --- a/scripts/developer/utilities/render/statsGPU.qml +++ b/scripts/developer/utilities/render/statsGPU.qml @@ -65,6 +65,13 @@ Item { label: "tone and post", color: "#FF0000" } + , + { + object: Render.getConfig("RenderMainView.OutlineRangeTimer"), + prop: "gpuRunTime", + label: "outline", + color: "#FFFF00" + } ] } PlotPerf { @@ -105,6 +112,13 @@ Item { label: "tone and post", color: "#FF0000" } + , + { + object: Render.getConfig("RenderMainView.OutlineRangeTimer"), + prop: "batchRunTime", + label: "outline", + color: "#FFFF00" + } ] } } From 410b1904cdb230c9f4bc079ced67f674ef1793d3 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 19 Oct 2017 17:36:16 +0200 Subject: [PATCH 21/62] Fixed bug in outline.qml that associated outline tab to wrong outline job config --- libraries/render-utils/src/OutlineEffect.cpp | 36 ++--- libraries/render-utils/src/OutlineEffect.h | 5 +- libraries/render/src/render/Scene.h | 2 +- .../developer/utilities/render/outline.qml | 126 ++++-------------- .../render/outlinePage/OutlinePage.qml | 113 ++++++++++++++++ .../utilities/render/outlinePage/qmldir | 1 + 6 files changed, 159 insertions(+), 124 deletions(-) create mode 100644 scripts/developer/utilities/render/outlinePage/OutlinePage.qml create mode 100644 scripts/developer/utilities/render/outlinePage/qmldir diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index 19d3847e50..a4286474f9 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -98,7 +98,7 @@ DrawOutlineMask::DrawOutlineMask(unsigned int outlineIndex, render::ShapePlumberPointer shapePlumber, OutlineSharedParametersPointer parameters) : _outlineIndex{ outlineIndex }, _shapePlumber { shapePlumber }, - _parameters{ parameters } { + _sharedParameters{ parameters } { } void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { @@ -115,8 +115,8 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con // First thing we do is determine the projected bounding rect of all the outlined items auto outlinedRect = computeOutlineRect(inShapes, args->getViewFrustum(), framebufferSize); - auto blurPixelWidth = _parameters->_blurPixelWidths[_outlineIndex]; - qCDebug(renderutils) << "Outline rect is " << outlinedRect.x << ' ' << outlinedRect.y << ' ' << outlinedRect.z << ' ' << outlinedRect.w; + auto blurPixelWidth = _sharedParameters->_blurPixelWidths[_outlineIndex]; + //qCDebug(renderutils) << "Outline rect is " << outlinedRect.x << ' ' << outlinedRect.y << ' ' << outlinedRect.z << ' ' << outlinedRect.w; // Add 1 pixel of extra margin to be on the safe side outputs = expandRect(outlinedRect, blurPixelWidth+1, framebufferSize); @@ -236,25 +236,25 @@ gpu::PipelinePointer DrawOutline::_pipelineFilled; DrawOutline::DrawOutline(unsigned int outlineIndex, OutlineSharedParametersPointer parameters) : _outlineIndex{ outlineIndex }, - _parameters{ parameters } { + _sharedParameters{ parameters } { } void DrawOutline::configure(const Config& config) { - auto& configuration = _configuration.edit(); const auto OPACITY_EPSILON = 5e-3f; - configuration._color = config.color; - configuration._intensity = config.intensity * (config.glow ? 2.f : 1.f); - configuration._unoccludedFillOpacity = config.unoccludedFillOpacity; - configuration._occludedFillOpacity = config.occludedFillOpacity; - configuration._threshold = config.glow ? 1.f : 1e-3f; - configuration._blurKernelSize = std::min(10, std::max(2, (int)floorf(config.width * 3 + 0.5f))); + _parameters._color = config.color; + _parameters._intensity = config.intensity * (config.glow ? 2.0f : 1.0f); + _parameters._unoccludedFillOpacity = config.unoccludedFillOpacity; + _parameters._occludedFillOpacity = config.occludedFillOpacity; + _parameters._threshold = config.glow ? 1.0f : 1e-3f; + _parameters._blurKernelSize = std::min(10, std::max(2, (int)floorf(config.width * 3 + 0.5f))); // Size is in normalized screen height. We decide that for outline width = 1, this is equal to 1/400. _size = config.width / 400.0f; - configuration._size.x = (_size * _framebufferSize.y) / _framebufferSize.x; - configuration._size.y = _size; - _parameters->_blurPixelWidths[_outlineIndex] = (int)ceilf(_size * _framebufferSize.y); + _parameters._size.x = (_size * _framebufferSize.y) / _framebufferSize.x; + _parameters._size.y = _size; + _sharedParameters->_blurPixelWidths[_outlineIndex] = (int)ceilf(_size * _framebufferSize.y); _isFilled = (config.unoccludedFillOpacity > OPACITY_EPSILON || config.occludedFillOpacity > OPACITY_EPSILON); + _configuration.edit() = _parameters; } void DrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { @@ -274,11 +274,11 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I if (_framebufferSize != framebufferSize) { - auto& configuration = _configuration.edit(); - configuration._size.x = (_size * framebufferSize.y) / framebufferSize.x; - configuration._size.y = _size; + _parameters._size.x = (_size * framebufferSize.y) / framebufferSize.x; + _parameters._size.y = _size; _framebufferSize = framebufferSize; - _parameters->_blurPixelWidths[_outlineIndex] = (int)ceilf(_size * _framebufferSize.y); + _sharedParameters->_blurPixelWidths[_outlineIndex] = (int)ceilf(_size * _framebufferSize.y); + _configuration.edit() = _parameters; } gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index b4be75484e..ee5e503de9 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -83,7 +83,7 @@ protected: unsigned int _outlineIndex; render::ShapePlumberPointer _shapePlumber; - OutlineSharedParametersPointer _parameters; + OutlineSharedParametersPointer _sharedParameters; static glm::ivec4 computeOutlineRect(const render::ShapeBounds& shapes, const ViewFrustum& viewFrustum, glm::ivec2 frameSize); static glm::ivec4 expandRect(glm::ivec4 rect, int amount, glm::ivec2 frameSize); @@ -154,7 +154,8 @@ private: static gpu::PipelinePointer _pipelineFilled; unsigned int _outlineIndex; - OutlineSharedParametersPointer _parameters; + OutlineParameters _parameters; + OutlineSharedParametersPointer _sharedParameters; OutlineConfigurationBuffer _configuration; glm::ivec2 _framebufferSize{ 0,0 }; bool _isFilled{ false }; diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 04a285bcd1..1f3b9a72c3 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -104,7 +104,7 @@ class Scene { public: enum { - MAX_OUTLINE_COUNT = 16 + MAX_OUTLINE_COUNT = 8 }; Scene(glm::vec3 origin, float size); diff --git a/scripts/developer/utilities/render/outline.qml b/scripts/developer/utilities/render/outline.qml index c686af33c6..578f0857dc 100644 --- a/scripts/developer/utilities/render/outline.qml +++ b/scripts/developer/utilities/render/outline.qml @@ -10,7 +10,7 @@ // import QtQuick 2.5 import QtQuick.Controls 1.4 -import "configSlider" +import "outlinePage" Item { id: root @@ -39,110 +39,30 @@ Item { sendToScript(currentIndex) } - Component { - id: paramWidgets - - Column { - spacing: 8 - - CheckBox { - id: glow - text: "Glow" - checked: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex)["glow"] - onCheckedChanged: { - Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex)["glow"] = checked; - } - } - ConfigSlider { - label: "Width" - integral: false - config: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex) - property: "width" - max: 15.0 - min: 0.0 - width: 280 - } - ConfigSlider { - label: "Intensity" - integral: false - config: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex) - property: "intensity" - max: 1.0 - min: 0.0 - width: 280 - } - - GroupBox { - title: "Color" - width: 280 - Column { - spacing: 8 - - ConfigSlider { - label: "Red" - integral: false - config: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex) - property: "colorR" - max: 1.0 - min: 0.0 - width: 270 - } - ConfigSlider { - label: "Green" - integral: false - config: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex) - property: "colorG" - max: 1.0 - min: 0.0 - width: 270 - } - ConfigSlider { - label: "Blue" - integral: false - config: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex) - property: "colorB" - max: 1.0 - min: 0.0 - width: 270 - } - } - } - - GroupBox { - title: "Fill Opacity" - width: 280 - Column { - spacing: 8 - - ConfigSlider { - label: "Unoccluded" - integral: false - config: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex) - property: "unoccludedFillOpacity" - max: 1.0 - min: 0.0 - width: 270 - } - ConfigSlider { - label: "Occluded" - integral: false - config: Render.getConfig("RenderMainView.OutlineEffect"+tabs.currentIndex) - property: "occludedFillOpacity" - max: 1.0 - min: 0.0 - width: 270 - } - } - } + Tab { + title: "Outl.0" + OutlinePage { + outlineIndex: 0 + } + } + Tab { + title: "Outl.1" + OutlinePage { + outlineIndex: 1 + } + } + Tab { + title: "Outl.2" + OutlinePage { + outlineIndex: 2 + } + } + Tab { + title: "Outl.3" + OutlinePage { + outlineIndex: 3 } } } } - - Component.onCompleted: { - for (var i=0 ; i<4 ; i++) { - var outlinePage = tabs.addTab("Outl. "+i, paramWidgets) - outlinePage.active = true - } - } } diff --git a/scripts/developer/utilities/render/outlinePage/OutlinePage.qml b/scripts/developer/utilities/render/outlinePage/OutlinePage.qml new file mode 100644 index 0000000000..849f71bdb4 --- /dev/null +++ b/scripts/developer/utilities/render/outlinePage/OutlinePage.qml @@ -0,0 +1,113 @@ +// +// outlinePage.qml +// developer/utilities/render +// +// Olivier Prat, created on 08/08/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import "../configSlider" + +Item { + id: root + property var outlineIndex: 0 + property var drawConfig: Render.getConfig("RenderMainView.OutlineEffect"+outlineIndex) + + Column { + spacing: 8 + + CheckBox { + id: glow + text: "Glow" + checked: root.drawConfig["glow"] + onCheckedChanged: { + paramWidgets.drawConfig["glow"] = checked; + } + } + ConfigSlider { + label: "Width" + integral: false + config: root.drawConfig + property: "width" + max: 15.0 + min: 0.0 + width: 280 + } + ConfigSlider { + label: "Intensity" + integral: false + config: root.drawConfig + property: "intensity" + max: 1.0 + min: 0.0 + width: 280 + } + + GroupBox { + title: "Color" + width: 280 + Column { + spacing: 8 + + ConfigSlider { + label: "Red" + integral: false + config: root.drawConfig + property: "colorR" + max: 1.0 + min: 0.0 + width: 270 + } + ConfigSlider { + label: "Green" + integral: false + config: root.drawConfig + property: "colorG" + max: 1.0 + min: 0.0 + width: 270 + } + ConfigSlider { + label: "Blue" + integral: false + config: root.drawConfig + property: "colorB" + max: 1.0 + min: 0.0 + width: 270 + } + } + } + + GroupBox { + title: "Fill Opacity" + width: 280 + Column { + spacing: 8 + + ConfigSlider { + label: "Unoccluded" + integral: false + config: root.drawConfig + property: "unoccludedFillOpacity" + max: 1.0 + min: 0.0 + width: 270 + } + ConfigSlider { + label: "Occluded" + integral: false + config: root.drawConfig + property: "occludedFillOpacity" + max: 1.0 + min: 0.0 + width: 270 + } + } + } + } +} diff --git a/scripts/developer/utilities/render/outlinePage/qmldir b/scripts/developer/utilities/render/outlinePage/qmldir new file mode 100644 index 0000000000..56f5d45414 --- /dev/null +++ b/scripts/developer/utilities/render/outlinePage/qmldir @@ -0,0 +1 @@ +OutlinePage 1.0 OutlinePage.qml \ No newline at end of file From 6acff216d919ab1c7976cb1b17367cb525128f5a Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 19 Oct 2017 19:00:09 +0200 Subject: [PATCH 22/62] Reduced blur tap for slightly better performance --- libraries/render-utils/src/OutlineEffect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index a4286474f9..8822bcc649 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -247,7 +247,7 @@ void DrawOutline::configure(const Config& config) { _parameters._unoccludedFillOpacity = config.unoccludedFillOpacity; _parameters._occludedFillOpacity = config.occludedFillOpacity; _parameters._threshold = config.glow ? 1.0f : 1e-3f; - _parameters._blurKernelSize = std::min(10, std::max(2, (int)floorf(config.width * 3 + 0.5f))); + _parameters._blurKernelSize = std::min(7, std::max(2, (int)floorf(config.width * 3 + 0.5f))); // Size is in normalized screen height. We decide that for outline width = 1, this is equal to 1/400. _size = config.width / 400.0f; _parameters._size.x = (_size * _framebufferSize.y) / _framebufferSize.x; From 3227b9d64ad37e557ad38da4bccba2143f867327 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 20 Oct 2017 09:50:37 +0200 Subject: [PATCH 23/62] Fixed potential bug when item doesn't have a payload --- libraries/render/src/render/FilterTask.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libraries/render/src/render/FilterTask.cpp b/libraries/render/src/render/FilterTask.cpp index 49a9ada91e..f60f5895b7 100644 --- a/libraries/render/src/render/FilterTask.cpp +++ b/libraries/render/src/render/FilterTask.cpp @@ -118,8 +118,9 @@ void MetaToSubItems::run(const RenderContextPointer& renderContext, const ItemBo for (auto idBound : inItems) { auto& item = scene->getItem(idBound.id); - - item.fetchMetaSubItems(outItems); + if (item.exist()) { + item.fetchMetaSubItems(outItems); + } } } @@ -132,8 +133,9 @@ void IDsToBounds::run(const RenderContextPointer& renderContext, const ItemIDs& if (!_disableAABBs) { for (auto id : inItems) { auto& item = scene->getItem(id); - - outItems.emplace_back(ItemBound{ id, item.getBound() }); + if (item.exist()) { + outItems.emplace_back(ItemBound{ id, item.getBound() }); + } } } else { for (auto id : inItems) { From 732fe3b8dbc1e4f23cede8a319c2360f2b4d6151 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 20 Oct 2017 11:32:43 +0200 Subject: [PATCH 24/62] Improved outline debugging script for easier test plan --- .../scripting/SelectionScriptingInterface.cpp | 6 ++ .../scripting/SelectionScriptingInterface.h | 1 + .../utilities/render/debugOutline.js | 85 +++++++++++++------ .../developer/utilities/render/outline.qml | 31 +++++-- 4 files changed, 90 insertions(+), 33 deletions(-) diff --git a/interface/src/scripting/SelectionScriptingInterface.cpp b/interface/src/scripting/SelectionScriptingInterface.cpp index 808396c901..1adf5650dd 100644 --- a/interface/src/scripting/SelectionScriptingInterface.cpp +++ b/interface/src/scripting/SelectionScriptingInterface.cpp @@ -71,6 +71,12 @@ bool SelectionScriptingInterface::removeFromSelectedItemsList(const QString& lis return false; } +bool SelectionScriptingInterface::clearSelectedItemsList(const QString& listName) { + _selectedItemsListMap.insert(listName, GameplayObjects()); + emit selectedItemsListChanged(listName); + return true; +} + template bool SelectionScriptingInterface::addToGameplayObjects(const QString& listName, T idToAdd) { GameplayObjects currentList = _selectedItemsListMap.value(listName); currentList.addToGameplayObjects(idToAdd); diff --git a/interface/src/scripting/SelectionScriptingInterface.h b/interface/src/scripting/SelectionScriptingInterface.h index d1a372c5c4..28c1713050 100644 --- a/interface/src/scripting/SelectionScriptingInterface.h +++ b/interface/src/scripting/SelectionScriptingInterface.h @@ -61,6 +61,7 @@ public: Q_INVOKABLE bool addToSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id); Q_INVOKABLE bool removeFromSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id); + Q_INVOKABLE bool clearSelectedItemsList(const QString& listName); signals: void selectedItemsListChanged(const QString& listName); diff --git a/scripts/developer/utilities/render/debugOutline.js b/scripts/developer/utilities/render/debugOutline.js index 5ac7bcd6aa..c01fbd3c76 100644 --- a/scripts/developer/utilities/render/debugOutline.js +++ b/scripts/developer/utilities/render/debugOutline.js @@ -55,14 +55,7 @@ var end2 = { } var outlineGroupIndex = 0 - -function setOutlineGroupIndex(index) { - print("Switching to outline group "+index) - outlineGroupIndex = index -} - -window.fromQml.connect(setOutlineGroupIndex); - +var isSelectionAddEnabled = false var renderStates = [{name: "test", end: end}]; var defaultRenderStates = [{name: "test", distance: 20.0, end: end2}]; @@ -71,33 +64,64 @@ var ray = LaserPointers.createLaserPointer({ filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS | RayPick.PICK_AVATARS | RayPick.PICK_INVISIBLE | RayPick.PICK_NONCOLLIDABLE, renderStates: renderStates, defaultRenderStates: defaultRenderStates, - enabled: true + enabled: false }); +function getSelectionName() { + var selectionName = "contextOverlayHighlightList" + + if (outlineGroupIndex>0) { + selectionName += outlineGroupIndex + } + return selectionName +} + +function fromQml(message) { + tokens = message.split(' ') + print("Received '"+message+"' from outline.qml") + if (tokens[0]=="outline") { + outlineGroupIndex = parseInt(tokens[1]) + print("Switching to outline group "+outlineGroupIndex) + } else if (tokens[0]=="pick") { + var isPickingEnabled = tokens[1]=='true' + print("Ray picking set to "+isPickingEnabled.toString()) + if (isPickingEnabled) { + LaserPointers.enableLaserPointer(ray) + } else { + LaserPointers.disableLaserPointer(ray) + } + } else if (tokens[0]=="add") { + isSelectionAddEnabled = tokens[1]=='true' + print("Add to selection set to "+isSelectionAddEnabled.toString()) + if (!isSelectionAddEnabled) { + Selection.clearSelectedItemsList(getSelectionName()) + } + } +} + +window.fromQml.connect(fromQml); + function cleanup() { LaserPointers.removeLaserPointer(ray); } Script.scriptEnding.connect(cleanup); -var prevID = 0; -var prevType = ""; -function update() { +var prevID = 0 +var prevType = "" +var selectedID = 0 +var selectedType = "" +var time = 0 +function update(deltaTime) { + // you have to do this repeatedly because there's a bug but I'll fix it LaserPointers.setRenderState(ray, "test"); var result = LaserPointers.getPrevRayPickResult(ray); - var selectionName = "contextOverlayHighlightList" - - if (outlineGroupIndex>0) { - selectionName += outlineGroupIndex - } + var selectionName = getSelectionName() if (result.type != RayPick.INTERSECTED_NONE) { + time += deltaTime if (result.objectID != prevID) { - if (prevID != 0) { - Selection.removeFromSelectedItemsList(selectionName, prevType, prevID) - } - var typeName = "" if (result.type == RayPick.INTERSECTED_ENTITY) { typeName = "entity" @@ -107,19 +131,28 @@ function update() { typeName = "avatar" } - Selection.addToSelectedItemsList(selectionName, typeName, result.objectID) - //print("OUTLINE " + outlineGroupIndex + " picked type: " + result.type + ", id: " + result.objectID); - prevID = result.objectID; prevType = typeName; + time = 0 + } else if (time>1.0 && prevID!=selectedID) { + if (prevID != 0 && !isSelectionAddEnabled) { + Selection.removeFromSelectedItemsList(selectionName, selectedType, selectedID) + } + selectedID = prevID + selectedType = prevType + Selection.addToSelectedItemsList(selectionName, selectedType, selectedID) + //print("OUTLINE " + outlineGroupIndex + " picked type: " + result.type + ", id: " + result.objectID); } } else { - if (prevID != 0) { + if (prevID != 0 && !isSelectionAddEnabled) { Selection.removeFromSelectedItemsList(selectionName, prevType, prevID) } - prevID = 0; + prevID = 0 + selectedID = 0 + time = 0 } } + Script.update.connect(update); }()); // END LOCAL_SCOPE \ No newline at end of file diff --git a/scripts/developer/utilities/render/outline.qml b/scripts/developer/utilities/render/outline.qml index 578f0857dc..39acd854ac 100644 --- a/scripts/developer/utilities/render/outline.qml +++ b/scripts/developer/utilities/render/outline.qml @@ -21,12 +21,29 @@ Item { spacing: 8 anchors.fill: parent - CheckBox { - id: debug - text: "View Mask" - checked: root.debugConfig["viewMask"] - onCheckedChanged: { - root.debugConfig["viewMask"] = checked; + Row { + spacing: 8 + CheckBox { + id: debug + text: "View Mask" + checked: root.debugConfig["viewMask"] + onCheckedChanged: { + root.debugConfig["viewMask"] = checked; + } + } + CheckBox { + text: "Hover select" + checked: false + onCheckedChanged: { + sendToScript("pick "+checked.toString()) + } + } + CheckBox { + text: "Add to selection" + checked: false + onCheckedChanged: { + sendToScript("add "+checked.toString()) + } } } @@ -36,7 +53,7 @@ Item { height: 400 onCurrentIndexChanged: { - sendToScript(currentIndex) + sendToScript("outline "+currentIndex) } Tab { From 2d49cc1a7561db478730e131243c8aa9f11cd479 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 20 Oct 2017 12:04:20 +0200 Subject: [PATCH 25/62] Fixed bug in debugOutline script --- scripts/developer/utilities/render/debugOutline.js | 12 +++++++----- .../utilities/render/outlinePage/OutlinePage.qml | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/scripts/developer/utilities/render/debugOutline.js b/scripts/developer/utilities/render/debugOutline.js index c01fbd3c76..72585e07fb 100644 --- a/scripts/developer/utilities/render/debugOutline.js +++ b/scripts/developer/utilities/render/debugOutline.js @@ -56,8 +56,10 @@ var end2 = { var outlineGroupIndex = 0 var isSelectionAddEnabled = false +var isSelectionEnabled = false var renderStates = [{name: "test", end: end}]; var defaultRenderStates = [{name: "test", distance: 20.0, end: end2}]; +var time = 0 var ray = LaserPointers.createLaserPointer({ joint: "Mouse", @@ -83,13 +85,14 @@ function fromQml(message) { outlineGroupIndex = parseInt(tokens[1]) print("Switching to outline group "+outlineGroupIndex) } else if (tokens[0]=="pick") { - var isPickingEnabled = tokens[1]=='true' - print("Ray picking set to "+isPickingEnabled.toString()) - if (isPickingEnabled) { + isSelectionEnabled = tokens[1]=='true' + print("Ray picking set to "+isSelectionEnabled.toString()) + if (isSelectionEnabled) { LaserPointers.enableLaserPointer(ray) } else { LaserPointers.disableLaserPointer(ray) } + time = 0 } else if (tokens[0]=="add") { isSelectionAddEnabled = tokens[1]=='true' print("Add to selection set to "+isSelectionAddEnabled.toString()) @@ -110,7 +113,6 @@ var prevID = 0 var prevType = "" var selectedID = 0 var selectedType = "" -var time = 0 function update(deltaTime) { // you have to do this repeatedly because there's a bug but I'll fix it @@ -119,7 +121,7 @@ function update(deltaTime) { var result = LaserPointers.getPrevRayPickResult(ray); var selectionName = getSelectionName() - if (result.type != RayPick.INTERSECTED_NONE) { + if (isSelectionEnabled && result.type != RayPick.INTERSECTED_NONE) { time += deltaTime if (result.objectID != prevID) { var typeName = "" diff --git a/scripts/developer/utilities/render/outlinePage/OutlinePage.qml b/scripts/developer/utilities/render/outlinePage/OutlinePage.qml index 849f71bdb4..f8976162ab 100644 --- a/scripts/developer/utilities/render/outlinePage/OutlinePage.qml +++ b/scripts/developer/utilities/render/outlinePage/OutlinePage.qml @@ -25,7 +25,7 @@ Item { text: "Glow" checked: root.drawConfig["glow"] onCheckedChanged: { - paramWidgets.drawConfig["glow"] = checked; + root.drawConfig["glow"] = checked; } } ConfigSlider { From 43f9db5b70ea2aa025948ee1e9e693984f09d2e4 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 23 Oct 2017 11:08:13 +0200 Subject: [PATCH 26/62] Fixed small bug due to half texel offset in outline shader --- libraries/render-utils/src/Outline.slh | 10 +++++----- libraries/render-utils/src/OutlineEffect.cpp | 2 +- .../utilities/render/outlinePage/OutlinePage.qml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/render-utils/src/Outline.slh b/libraries/render-utils/src/Outline.slh index fe9594cc12..aeaf20a24a 100644 --- a/libraries/render-utils/src/Outline.slh +++ b/libraries/render-utils/src/Outline.slh @@ -35,16 +35,14 @@ void main(void) { // We offset by half a texel to be centered on the depth sample. If we don't do this // the blur will have a different width between the left / right sides and top / bottom // sides of the silhouette - vec2 halfTexel = getInvWidthHeight() / 2; - vec2 texCoord0 = varTexCoord0+halfTexel; - float outlinedDepth = texture(outlinedDepthMap, texCoord0).x; + float outlinedDepth = texture(outlinedDepthMap, varTexCoord0).x; float intensity = 0.0; if (outlinedDepth < FAR_Z) { // We're not on the far plane so we are on the outlined object, thus no outline to do! <@if IS_FILLED@> // But we need to fill the interior - float sceneDepth = texture(sceneDepthMap, texCoord0).x; + float sceneDepth = texture(sceneDepthMap, varTexCoord0).x; // Transform to linear depth for better precision outlinedDepth = -evalZeyeFromZdb(outlinedDepth); sceneDepth = -evalZeyeFromZdb(sceneDepth); @@ -55,6 +53,8 @@ void main(void) { discard; <@endif@> } else { + vec2 halfTexel = getInvWidthHeight() / 2; + vec2 texCoord0 = varTexCoord0+halfTexel; float weight = 0.0; vec2 deltaUv = params._size / params._blurKernelSize; vec2 lineStartUv = texCoord0 - params._size / 2.0; @@ -72,7 +72,7 @@ void main(void) { { outlinedDepth = texture(outlinedDepthMap, uv).x; intensity += (outlinedDepth < FAR_Z) ? 1.0 : 0.0; - weight += 1.f; + weight += 1.0; } uv.x += deltaUv.x; } diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index 8822bcc649..ca67347ecf 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -446,7 +446,7 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende auto shapePlumber = std::make_shared(); { auto state = std::make_shared(); - state->setDepthTest(true, true, gpu::LESS); + state->setDepthTest(true, true, gpu::LESS_EQUAL); state->setColorWriteMask(false, false, false, false); state->setScissorEnable(true); initMaskPipelines(*shapePlumber, state); diff --git a/scripts/developer/utilities/render/outlinePage/OutlinePage.qml b/scripts/developer/utilities/render/outlinePage/OutlinePage.qml index f8976162ab..a78bf02d3e 100644 --- a/scripts/developer/utilities/render/outlinePage/OutlinePage.qml +++ b/scripts/developer/utilities/render/outlinePage/OutlinePage.qml @@ -33,7 +33,7 @@ Item { integral: false config: root.drawConfig property: "width" - max: 15.0 + max: 5.0 min: 0.0 width: 280 } From 7011fd3731ef9bf10217deac219604bf1d2314f6 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 23 Oct 2017 15:46:11 +0200 Subject: [PATCH 27/62] Removed Mac and Ubuntu warnings --- libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp | 2 ++ libraries/shared/src/ViewFrustum.cpp | 24 +++++++++---------- .../utilities/render/debugOutline.js | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp index 27319e1696..528a2b524b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp @@ -501,6 +501,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E break; } case gpu::COMPRESSED: + case gpu::NUINT2: case gpu::NUM_TYPES: { // quiet compiler Q_UNREACHABLE(); } @@ -551,6 +552,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E break; } case gpu::COMPRESSED: + case gpu::NUINT2: case gpu::NUM_TYPES: { // quiet compiler Q_UNREACHABLE(); } diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index 747b39f79e..f3eb165c51 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -590,18 +590,18 @@ bool ViewFrustum::getProjectedRect(const AABox& box, glm::vec2& bottomLeft, glm: std::array vertices; std::array boxEdges{ - Edge(BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR), - Edge(TOP_LEFT_NEAR, TOP_RIGHT_NEAR), - Edge(BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR), - Edge(TOP_LEFT_FAR, TOP_RIGHT_FAR), - Edge(BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR), - Edge(BOTTOM_LEFT_FAR, TOP_LEFT_FAR), - Edge(BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR), - Edge(BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR), - Edge(BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR), - Edge(TOP_LEFT_NEAR, TOP_LEFT_FAR), - Edge(BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR), - Edge(TOP_RIGHT_NEAR, TOP_RIGHT_FAR) + Edge{BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR}, + Edge{TOP_LEFT_NEAR, TOP_RIGHT_NEAR}, + Edge{BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR}, + Edge{TOP_LEFT_FAR, TOP_RIGHT_FAR}, + Edge{BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, + Edge{BOTTOM_LEFT_FAR, TOP_LEFT_FAR}, + Edge{BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR}, + Edge{BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR}, + Edge{BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR}, + Edge{TOP_LEFT_NEAR, TOP_LEFT_FAR}, + Edge{BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR}, + Edge{TOP_RIGHT_NEAR, TOP_RIGHT_FAR} }; std::array distancesToNearPlane; std::bitset areVerticesInside; diff --git a/scripts/developer/utilities/render/debugOutline.js b/scripts/developer/utilities/render/debugOutline.js index 72585e07fb..ce32d61e1b 100644 --- a/scripts/developer/utilities/render/debugOutline.js +++ b/scripts/developer/utilities/render/debugOutline.js @@ -143,7 +143,7 @@ function update(deltaTime) { selectedID = prevID selectedType = prevType Selection.addToSelectedItemsList(selectionName, selectedType, selectedID) - //print("OUTLINE " + outlineGroupIndex + " picked type: " + result.type + ", id: " + result.objectID); + print("OUTLINE " + outlineGroupIndex + " picked type: " + result.type + ", id: " + result.objectID); } } else { if (prevID != 0 && !isSelectionAddEnabled) { From 68ed061ce59ae59a5c88444b7f3349ae02967b40 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 23 Oct 2017 16:49:42 +0200 Subject: [PATCH 28/62] Again, fix for Mac warning --- libraries/shared/src/ViewFrustum.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index f3eb165c51..dcbfd83ec7 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -589,7 +589,7 @@ bool ViewFrustum::getProjectedRect(const AABox& box, glm::vec2& bottomLeft, glm: const int MAX_VERTEX_COUNT = VERTEX_COUNT + 4 + 2; std::array vertices; - std::array boxEdges{ + std::array boxEdges{ { Edge{BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR}, Edge{TOP_LEFT_NEAR, TOP_RIGHT_NEAR}, Edge{BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR}, @@ -602,7 +602,7 @@ bool ViewFrustum::getProjectedRect(const AABox& box, glm::vec2& bottomLeft, glm: Edge{TOP_LEFT_NEAR, TOP_LEFT_FAR}, Edge{BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR}, Edge{TOP_RIGHT_NEAR, TOP_RIGHT_FAR} - }; + } }; std::array distancesToNearPlane; std::bitset areVerticesInside; int vertexCount = VERTEX_COUNT; From 99ec2aec4a8c63a4d59525fe3e718562d9ec6ca5 Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 23 Oct 2017 20:44:13 +0200 Subject: [PATCH 29/62] Prevent scripts log window open several times --- interface/src/Application.cpp | 18 ++++++++++++++---- interface/src/Application.h | 4 ++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0b99ce5004..d03ff8f1da 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5800,6 +5800,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe ClipboardScriptingInterface* clipboardScriptable = new ClipboardScriptingInterface(); scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable); connect(scriptEngine.data(), &ScriptEngine::finished, clipboardScriptable, &ClipboardScriptingInterface::deleteLater); + connect(scriptEngine.data(), &ScriptEngine::finished, this, &Application::cleanupRunningScripts); scriptEngine->registerGlobalObject("Overlays", &_overlays); qScriptRegisterMetaType(scriptEngine.data(), OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue); @@ -6213,10 +6214,15 @@ void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const } void Application::showScriptLogs() { - auto scriptEngines = DependencyManager::get(); - QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); - defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/debugging/debugWindow.js"); - scriptEngines->loadScript(defaultScriptsLoc.toString()); + if (!_runningScripts.contains("debugWindow.js")) { + auto scriptEngines = DependencyManager::get(); + QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); + defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/debugging/debugWindow.js"); + ScriptEnginePointer sePointer = scriptEngines->loadScript(defaultScriptsLoc.toString()); + _runningScripts["debugWindow.js"] = sePointer; + } else { + qWarning() << "Scripts Log already running"; + } } void Application::showAssetServerWidget(QString filePath) { @@ -7311,6 +7317,10 @@ void Application::switchDisplayMode() { _previousHMDWornStatus = currentHMDWornStatus; } +void Application::cleanupRunningScripts(const QString &fileNameString, ScriptEnginePointer) { + _runningScripts.remove(QUrl(fileNameString).fileName()); +} + void Application::startHMDStandBySession() { _autoSwitchDisplayModeSupportedHMDPlugin->startStandBySession(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index b6c09bbd87..65b6dd2c9e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -451,6 +451,8 @@ private slots: void handleSandboxStatus(QNetworkReply* reply); void switchDisplayMode(); + + void cleanupRunningScripts(const QString& fileNameString, ScriptEnginePointer); private: static void initDisplay(); void init(); @@ -720,5 +722,7 @@ private: std::atomic _pendingIdleEvent { false }; std::atomic _pendingRenderEvent { false }; + + QHash _runningScripts; }; #endif // hifi_Application_h From c55f93813237d2a3010237b0e5dcc6ded12f1f82 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 24 Oct 2017 22:09:00 +0200 Subject: [PATCH 30/62] Code style fix --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d03ff8f1da..05ec8a5df1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -7317,7 +7317,7 @@ void Application::switchDisplayMode() { _previousHMDWornStatus = currentHMDWornStatus; } -void Application::cleanupRunningScripts(const QString &fileNameString, ScriptEnginePointer) { +void Application::cleanupRunningScripts(const QString& fileNameString, ScriptEnginePointer) { _runningScripts.remove(QUrl(fileNameString).fileName()); } From 45e571dd02cfed91b8a4c7e954b7693fd1b11617 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 26 Oct 2017 08:16:41 -0700 Subject: [PATCH 31/62] cleanup ShapeInfo::getHash() --- libraries/physics/src/ShapeManager.cpp | 8 +- libraries/physics/src/ShapeManager.h | 10 +- libraries/shared/src/HashKey.cpp | 67 +++++++++++++ libraries/shared/src/HashKey.h | 52 ++++++++++ libraries/shared/src/ShapeInfo.cpp | 119 +++++------------------ libraries/shared/src/ShapeInfo.h | 6 +- tests/physics/src/ShapeInfoTests.cpp | 129 +++++++++---------------- tests/physics/src/ShapeInfoTests.h | 4 - 8 files changed, 202 insertions(+), 193 deletions(-) create mode 100644 libraries/shared/src/HashKey.cpp create mode 100644 libraries/shared/src/HashKey.h diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index 77716f671b..97b9e5dab1 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -32,7 +32,7 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { if (info.getType() == SHAPE_TYPE_NONE) { return nullptr; } - DoubleHashKey key = info.getHash(); + HashKey key = info.getHash(); ShapeReference* shapeRef = _shapeMap.find(key); if (shapeRef) { shapeRef->refCount++; @@ -50,7 +50,7 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { } // private helper method -bool ShapeManager::releaseShapeByKey(const DoubleHashKey& key) { +bool ShapeManager::releaseShapeByKey(const HashKey& key) { ShapeReference* shapeRef = _shapeMap.find(key); if (shapeRef) { if (shapeRef->refCount > 0) { @@ -88,7 +88,7 @@ bool ShapeManager::releaseShape(const btCollisionShape* shape) { void ShapeManager::collectGarbage() { int numShapes = _pendingGarbage.size(); for (int i = 0; i < numShapes; ++i) { - DoubleHashKey& key = _pendingGarbage[i]; + HashKey& key = _pendingGarbage[i]; ShapeReference* shapeRef = _shapeMap.find(key); if (shapeRef && shapeRef->refCount == 0) { ShapeFactory::deleteShape(shapeRef->shape); @@ -99,7 +99,7 @@ void ShapeManager::collectGarbage() { } int ShapeManager::getNumReferences(const ShapeInfo& info) const { - DoubleHashKey key = info.getHash(); + HashKey key = info.getHash(); const ShapeReference* shapeRef = _shapeMap.find(key); if (shapeRef) { return shapeRef->refCount; diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index ed81b5e8f8..6ec3573b53 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -17,7 +17,7 @@ #include -#include "DoubleHashKey.h" +#include "HashKey.h" class ShapeManager { public: @@ -41,18 +41,18 @@ public: bool hasShape(const btCollisionShape* shape) const; private: - bool releaseShapeByKey(const DoubleHashKey& key); + bool releaseShapeByKey(const HashKey& key); class ShapeReference { public: int refCount; const btCollisionShape* shape; - DoubleHashKey key; + HashKey key; ShapeReference() : refCount(0), shape(nullptr) {} }; - btHashMap _shapeMap; - btAlignedObjectArray _pendingGarbage; + btHashMap _shapeMap; + btAlignedObjectArray _pendingGarbage; }; #endif // hifi_ShapeManager_h diff --git a/libraries/shared/src/HashKey.cpp b/libraries/shared/src/HashKey.cpp new file mode 100644 index 0000000000..488eccb1bf --- /dev/null +++ b/libraries/shared/src/HashKey.cpp @@ -0,0 +1,67 @@ +// +// HashKey.cpp +// libraries/shared/src +// +// Created by Andrew Meadows 2017.10.25 +// Copyright 2017 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 "HashKey.h" + +#include "NumericalConstants.h" + + +const uint8_t NUM_PRIMES = 64; +const uint64_t PRIMES[] = { + 4194301UL, 4194287UL, 4194277UL, 4194271UL, 4194247UL, 4194217UL, 4194199UL, 4194191UL, + 4194187UL, 4194181UL, 4194173UL, 4194167UL, 4194143UL, 4194137UL, 4194131UL, 4194107UL, + 4194103UL, 4194023UL, 4194011UL, 4194007UL, 4193977UL, 4193971UL, 4193963UL, 4193957UL, + 4193939UL, 4193929UL, 4193909UL, 4193869UL, 4193807UL, 4193803UL, 4193801UL, 4193789UL, + 4193759UL, 4193753UL, 4193743UL, 4193701UL, 4193663UL, 4193633UL, 4193573UL, 4193569UL, + 4193551UL, 4193549UL, 4193531UL, 4193513UL, 4193507UL, 4193459UL, 4193447UL, 4193443UL, + 4193417UL, 4193411UL, 4193393UL, 4193389UL, 4193381UL, 4193377UL, 4193369UL, 4193359UL, + 4193353UL, 4193327UL, 4193309UL, 4193303UL, 4193297UL, 4193279UL, 4193269UL, 4193263UL +}; + + +// this hash function inspired by Squirrel Eiserloh's GDC2017 talk: "Noise-Based RNG" +uint64_t squirrel3_64(uint64_t data, uint8_t primeIndex) { + constexpr uint64_t BIT_NOISE1 = 2760725261486592643UL; + constexpr uint64_t BIT_NOISE2 = 6774464464027632833UL; + constexpr uint64_t BIT_NOISE3 = 5545331650366059883UL; + + // blend prime numbers into the hash to prevent dupes + // when hashing the same set of numbers in a different order + uint64_t hash = PRIMES[primeIndex % NUM_PRIMES] * data; + hash *= BIT_NOISE1; + hash ^= (hash >> 16); + hash += BIT_NOISE2; + hash ^= (hash << 16); + hash *= BIT_NOISE3; + return hash ^ (hash >> 16); +} + +constexpr float QUANTIZED_VALUES_PER_METER = 250.0f; + +// static +float HashKey::getNumQuantizedValuesPerMeter() { + return QUANTIZED_VALUES_PER_METER; +} + +void HashKey::hashUint64(uint64_t data) { + _hash += squirrel3_64(data, ++_hashCount); +} + +void HashKey::hashFloat(float data) { + _hash += squirrel3_64((uint64_t)((int64_t)(data * QUANTIZED_VALUES_PER_METER)), ++_hashCount); +} + +void HashKey::hashVec3(const glm::vec3& data) { + _hash += squirrel3_64((uint64_t)((int64_t)(data[0] * QUANTIZED_VALUES_PER_METER)), ++_hashCount); + _hash *= squirrel3_64((uint64_t)((int64_t)(data[1] * QUANTIZED_VALUES_PER_METER)), ++_hashCount); + _hash ^= squirrel3_64((uint64_t)((int64_t)(data[2] * QUANTIZED_VALUES_PER_METER)), ++_hashCount); +} + diff --git a/libraries/shared/src/HashKey.h b/libraries/shared/src/HashKey.h new file mode 100644 index 0000000000..5fce182084 --- /dev/null +++ b/libraries/shared/src/HashKey.h @@ -0,0 +1,52 @@ +// +// HashKey.h +// libraries/shared/src +// +// Created by Andrew Meadows 2017.10.25 +// Copyright 2017 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_HashKey_h +#define hifi_HashKey_h + +#include +#include + +#include + + +// HashKey for use with btHashMap which requires a particular API for its keys. In particular it +// requires its Key to implement these methods: +// +// bool Key::equals() +// int32_t Key::getHash() +// +// The important thing about the HashKey implementation is that while getHash() returns 32-bits, +// internally HashKey stores a 64-bit hash which is used for the equals() comparison. This allows +// btHashMap to insert "dupe" 32-bit keys to different "values". + +class HashKey { +public: + static float getNumQuantizedValuesPerMeter(); + + // These two methods are required by btHashMap. + bool equals(const HashKey& other) const { return _hash == other._hash; } + int32_t getHash() const { return (int32_t)((uint32_t)_hash); } + + void clear() { _hash = _hashCount = 0; } + bool isNull() const { return _hash == 0 && _hashCount == 0; } + void hashUint64(uint64_t data); + void hashFloat(float data); + void hashVec3(const glm::vec3& data); + + uint64_t getHash64() const { return _hash; } // for debug/test purposes + +private: + uint64_t _hash { 0 }; + uint8_t _hashCount { 0 }; +}; + +#endif // hifi_HashKey_h diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 36ce38335a..8cdc4bcf14 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -53,7 +53,7 @@ void ShapeInfo::clear() { _triangleIndices.clear(); _halfExtents = glm::vec3(0.0f); _offset = glm::vec3(0.0f); - _doubleHashKey.clear(); + _hashKey.clear(); _type = SHAPE_TYPE_NONE; } @@ -87,14 +87,14 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString default: break; } - _doubleHashKey.clear(); + _hashKey.clear(); } void ShapeInfo::setBox(const glm::vec3& halfExtents) { _url = ""; _type = SHAPE_TYPE_BOX; setHalfExtents(halfExtents); - _doubleHashKey.clear(); + _hashKey.clear(); } void ShapeInfo::setSphere(float radius) { @@ -102,12 +102,12 @@ void ShapeInfo::setSphere(float radius) { _type = SHAPE_TYPE_SPHERE; radius = glm::max(radius, MIN_HALF_EXTENT); _halfExtents = glm::vec3(radius); - _doubleHashKey.clear(); + _hashKey.clear(); } void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) { _pointCollection = pointCollection; - _doubleHashKey.clear(); + _hashKey.clear(); } void ShapeInfo::setCapsuleY(float radius, float halfHeight) { @@ -116,12 +116,12 @@ void ShapeInfo::setCapsuleY(float radius, float halfHeight) { radius = glm::max(radius, MIN_HALF_EXTENT); halfHeight = glm::max(halfHeight, 0.0f); _halfExtents = glm::vec3(radius, halfHeight, radius); - _doubleHashKey.clear(); + _hashKey.clear(); } void ShapeInfo::setOffset(const glm::vec3& offset) { _offset = offset; - _doubleHashKey.clear(); + _hashKey.clear(); } uint32_t ShapeInfo::getNumSubShapes() const { @@ -256,119 +256,46 @@ bool ShapeInfo::contains(const glm::vec3& point) const { } } -const DoubleHashKey& ShapeInfo::getHash() const { +const HashKey& ShapeInfo::getHash() const { // NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance. - if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) { - bool useOffset = glm::length2(_offset) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET; + if (_hashKey.isNull() && _type != SHAPE_TYPE_NONE) { // The key is not yet cached therefore we must compute it. - // compute hash1 - // TODO?: provide lookup table for hash/hash2 of _type rather than recompute? - uint32_t primeIndex = 0; - _doubleHashKey.computeHash((uint32_t)_type, primeIndex++); - + _hashKey.hashUint64((uint64_t)_type); if (_type != SHAPE_TYPE_SIMPLE_HULL) { - // compute hash1 - uint32_t hash = _doubleHashKey.getHash(); - for (int j = 0; j < 3; ++j) { - // NOTE: 0.49f is used to bump the float up almost half a millimeter - // so the cast to int produces a round() effect rather than a floor() - hash ^= DoubleHashKey::hashFunction( - (uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f), - primeIndex++); - if (useOffset) { - hash ^= DoubleHashKey::hashFunction( - (uint32_t)(_offset[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[j]) * 0.49f), - primeIndex++); - } - } - _doubleHashKey.setHash(hash); - - // compute hash2 - hash = _doubleHashKey.getHash2(); - for (int j = 0; j < 3; ++j) { - // NOTE: 0.49f is used to bump the float up almost half a millimeter - // so the cast to int produces a round() effect rather than a floor() - uint32_t floatHash = DoubleHashKey::hashFunction2( - (uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f)); - if (useOffset) { - floatHash ^= DoubleHashKey::hashFunction2( - (uint32_t)(_offset[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[j]) * 0.49f)); - } - hash += ~(floatHash << 17); - hash ^= (floatHash >> 11); - hash += (floatHash << 4); - hash ^= (floatHash >> 7); - hash += ~(floatHash << 10); - hash = (hash << 16) | (hash >> 16); - } - _doubleHashKey.setHash2(hash); + _hashKey.hashVec3(_halfExtents); + _hashKey.hashVec3(_offset); } else { - + // TODO: we could avoid hashing all of these points if we were to supply the ShapeInfo with a unique + // descriptive string. Shapes that are uniquely described by their type and URL could just put their + // url in the description. assert(_pointCollection.size() == (size_t)1); const PointList & points = _pointCollection.back(); const int numPoints = (int)points.size(); - uint32_t hash = _doubleHashKey.getHash(); - uint32_t hash2 = _doubleHashKey.getHash2(); - for (int pointIndex = 0; pointIndex < numPoints; ++pointIndex) { - // compute hash1 & 2 - const glm::vec3 &curPoint = points[pointIndex]; - for (int vecCompIndex = 0; vecCompIndex < 3; ++vecCompIndex) { - - // NOTE: 0.49f is used to bump the float up almost half a millimeter - // so the cast to int produces a round() effect rather than a floor() - uint32_t valueToHash = (uint32_t)(curPoint[vecCompIndex] * MILLIMETERS_PER_METER + copysignf(1.0f, curPoint[vecCompIndex]) * 0.49f); - - hash ^= DoubleHashKey::hashFunction(valueToHash, primeIndex++); - uint32_t floatHash = DoubleHashKey::hashFunction2(valueToHash); - - if (useOffset) { - - const uint32_t offsetValToHash = (uint32_t)(_offset[vecCompIndex] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[vecCompIndex])* 0.49f); - - hash ^= DoubleHashKey::hashFunction(offsetValToHash, primeIndex++); - floatHash ^= DoubleHashKey::hashFunction2(offsetValToHash); - } - - hash2 += ~(floatHash << 17); - hash2 ^= (floatHash >> 11); - hash2 += (floatHash << 4); - hash2 ^= (floatHash >> 7); - hash2 += ~(floatHash << 10); - hash2 = (hash2 << 16) | (hash2 >> 16); - } + for (int i = 0; i < numPoints; ++i) { + _hashKey.hashVec3(points[i]); } - - _doubleHashKey.setHash(hash); - _doubleHashKey.setHash2(hash2); } QString url = _url.toString(); if (!url.isEmpty()) { - // fold the urlHash into both parts QByteArray baUrl = url.toLocal8Bit(); uint32_t urlHash = qChecksum(baUrl.data(), baUrl.size()); - _doubleHashKey.setHash(_doubleHashKey.getHash() ^ urlHash); - _doubleHashKey.setHash2(_doubleHashKey.getHash2() ^ urlHash); + _hashKey.hashUint64((uint64_t)urlHash); } - uint32_t numHulls = 0; if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_SIMPLE_COMPOUND) { - numHulls = (uint32_t)_pointCollection.size(); + uint64_t numHulls = (uint64_t)_pointCollection.size(); + _hashKey.hashUint64(numHulls); } else if (_type == SHAPE_TYPE_SIMPLE_HULL) { - numHulls = 1; - } - if (numHulls > 0) { - uint32_t hash = DoubleHashKey::hashFunction(numHulls, primeIndex++); - _doubleHashKey.setHash(_doubleHashKey.getHash() ^ hash); - hash = DoubleHashKey::hashFunction2(numHulls); - _doubleHashKey.setHash2(_doubleHashKey.getHash2() ^ hash); + _hashKey.hashUint64(1); } } - return _doubleHashKey; + return _hashKey; } void ShapeInfo::setHalfExtents(const glm::vec3& halfExtents) { _halfExtents = glm::max(halfExtents, glm::vec3(MIN_HALF_EXTENT)); + _hashKey.clear(); } diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index d658b936a3..069241e29d 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -18,7 +18,7 @@ #include #include -#include "DoubleHashKey.h" +#include "HashKey.h" const float MIN_SHAPE_OFFSET = 0.001f; // offsets less than 1mm will be ignored @@ -89,7 +89,7 @@ public: /// For compound shapes it will only return whether it is inside the bounding box bool contains(const glm::vec3& point) const; - const DoubleHashKey& getHash() const; + const HashKey& getHash() const; protected: void setHalfExtents(const glm::vec3& halfExtents); @@ -99,7 +99,7 @@ protected: TriangleIndices _triangleIndices; glm::vec3 _halfExtents = glm::vec3(0.0f); glm::vec3 _offset = glm::vec3(0.0f); - mutable DoubleHashKey _doubleHashKey; + mutable HashKey _hashKey; ShapeType _type = SHAPE_TYPE_NONE; }; diff --git a/tests/physics/src/ShapeInfoTests.cpp b/tests/physics/src/ShapeInfoTests.cpp index c6a19084a2..79d0092dc3 100644 --- a/tests/physics/src/ShapeInfoTests.cpp +++ b/tests/physics/src/ShapeInfoTests.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include #include @@ -23,108 +23,78 @@ QTEST_MAIN(ShapeInfoTests) +// Enable this to manually run testHashCollisions +// (NOT a regular unit test; takes ~40 secs to run on an i7) +//#define MANUAL_TEST true + void ShapeInfoTests::testHashFunctions() { #if MANUAL_TEST int maxTests = 10000000; ShapeInfo info; - btHashMap hashes; + btHashMap hashes; - uint32_t bits[32]; - uint32_t masks[32]; - for (int i = 0; i < 32; ++i) { + const int32_t NUM_HASH_BITS = 32; + uint32_t bits[NUM_HASH_BITS]; + uint32_t masks[NUM_HASH_BITS]; + for (int i = 0; i < NUM_HASH_BITS; ++i) { bits[i] = 0; - masks[i] = 1U << i; + masks[i] = 1UL << i; } - float deltaLength = 0.002f; - float endLength = 100.0f; + float deltaLength = 1.0f / (HashKey::getNumQuantizedValuesPerMeter() - 3.0f); + float endLength = 2000.0f * deltaLength; int numSteps = (int)(endLength / deltaLength); int testCount = 0; int numCollisions = 0; btClock timer; - for (int x = 1; x < numSteps && testCount < maxTests; ++x) { - float radiusX = (float)x * deltaLength; + for (int i = 1; i < numSteps && testCount < maxTests; ++i) { + float radiusX = (float)i * deltaLength; + int32_t* hashPtr; // test sphere info.setSphere(radiusX); ++testCount; - DoubleHashKey key = info.getHash(); - uint32_t* hashPtr = hashes.find(key.getHash()); - if (hashPtr && *hashPtr == key.getHash2()) { - std::cout << testCount << " hash collision radiusX = " << radiusX - << " h1 = 0x" << std::hex << key.getHash() - << " h2 = 0x" << std::hex << key.getHash2() - << std::endl; + HashKey key = info.getHash(); + hashPtr = hashes.find(key); + if (hashPtr) { + std::cout << testCount << " hash collision sphere radius = " << radiusX + << " h = 0x" << std::hex << key.getHash() << " : 0x" << *hashPtr + << std::dec << std::endl; ++numCollisions; assert(false); } else { - hashes.insert(key.getHash(), key.getHash2()); + hashes.insert(key, key.getHash()); } - for (int k = 0; k < 32; ++k) { - if (masks[k] & key.getHash2()) { - ++bits[k]; + // track bit distribution counts to evaluate hash function randomness + for (int j = 0; j < NUM_HASH_BITS; ++j) { + if (masks[j] & key.getHash()) { + ++bits[j]; } } for (int y = 1; y < numSteps && testCount < maxTests; ++y) { float radiusY = (float)y * deltaLength; - /* TODO: reimplement Cylinder and Capsule shapes - // test cylinder and capsule - int types[] = { CYLINDER_SHAPE_PROXYTYPE, CAPSULE_SHAPE_PROXYTYPE }; - for (int i = 0; i < 2; ++i) { - switch(types[i]) { - case CYLINDER_SHAPE_PROXYTYPE: { - info.setCylinder(radiusX, radiusY); - break; - } - case CAPSULE_SHAPE_PROXYTYPE: { - info.setCapsuleY(radiusX, radiusY); - break; - } - } - - ++testCount; - key = info.getHash(); - hashPtr = hashes.find(key.getHash()); - if (hashPtr && *hashPtr == key.getHash2()) { - std::cout << testCount << " hash collision radiusX = " << radiusX << " radiusY = " << radiusY - << " h1 = 0x" << std::hex << key.getHash() - << " h2 = 0x" << std::hex << key.getHash2() - << std::endl; - ++numCollisions; - assert(false); - } else { - hashes.insert(key.getHash(), key.getHash2()); - } - for (int k = 0; k < 32; ++k) { - if (masks[k] & key.getHash2()) { - ++bits[k]; - } - } - } - */ - for (int z = 1; z < numSteps && testCount < maxTests; ++z) { float radiusZ = (float)z * deltaLength; // test box info.setBox(glm::vec3(radiusX, radiusY, radiusZ)); ++testCount; - DoubleHashKey key = info.getHash(); - hashPtr = hashes.find(key.getHash()); - if (hashPtr && *hashPtr == key.getHash2()) { - std::cout << testCount << " hash collision radiusX = " << radiusX - << " radiusY = " << radiusY << " radiusZ = " << radiusZ - << " h1 = 0x" << std::hex << key.getHash() - << " h2 = 0x" << std::hex << key.getHash2() - << std::endl; + HashKey key = info.getHash(); + hashPtr = hashes.find(key); + if (hashPtr) { + std::cout << testCount << " hash collision box dimensions = < " << radiusX + << ", " << radiusY << ", " << radiusZ << " >" + << " h = 0x" << std::hex << key.getHash() << " : 0x" << *hashPtr << " : 0x" << key.getHash64() + << std::dec << std::endl; ++numCollisions; assert(false); } else { - hashes.insert(key.getHash(), key.getHash2()); + hashes.insert(key, key.getHash()); } - for (int k = 0; k < 32; ++k) { - if (masks[k] & key.getHash2()) { + // track bit distribution counts to evaluate hash function randomness + for (int k = 0; k < NUM_HASH_BITS; ++k) { + if (masks[k] & key.getHash()) { ++bits[k]; } } @@ -135,7 +105,8 @@ void ShapeInfoTests::testHashFunctions() { std::cout << msec << " msec with " << numCollisions << " collisions out of " << testCount << " hashes" << std::endl; // print out distribution of bits - for (int i = 0; i < 32; ++i) { + // ideally the numbers in each bin will be about the same + for (int i = 0; i < NUM_HASH_BITS; ++i) { std::cout << "bit 0x" << std::hex << masks[i] << std::dec << " = " << bits[i] << std::endl; } QCOMPARE(numCollisions, 0); @@ -146,15 +117,14 @@ void ShapeInfoTests::testBoxShape() { ShapeInfo info; glm::vec3 halfExtents(1.23f, 4.56f, 7.89f); info.setBox(halfExtents); - DoubleHashKey key = info.getHash(); + HashKey key = info.getHash(); const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; - DoubleHashKey otherKey = otherInfo.getHash(); + HashKey otherKey = otherInfo.getHash(); QCOMPARE(key.getHash(), otherKey.getHash()); - QCOMPARE(key.getHash2(), otherKey.getHash2()); delete shape; } @@ -163,15 +133,14 @@ void ShapeInfoTests::testSphereShape() { ShapeInfo info; float radius = 1.23f; info.setSphere(radius); - DoubleHashKey key = info.getHash(); + HashKey key = info.getHash(); const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; - DoubleHashKey otherKey = otherInfo.getHash(); + HashKey otherKey = otherInfo.getHash(); QCOMPARE(key.getHash(), otherKey.getHash()); - QCOMPARE(key.getHash2(), otherKey.getHash2()); delete shape; } @@ -182,15 +151,14 @@ void ShapeInfoTests::testCylinderShape() { float radius = 1.23f; float height = 4.56f; info.setCylinder(radius, height); - DoubleHashKey key = info.getHash(); + HashKey key = info.getHash(); btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; - DoubleHashKey otherKey = otherInfo.getHash(); + HashKey otherKey = otherInfo.getHash(); QCOMPARE(key.getHash(), otherKey.getHash()); - QCOMPARE(key.getHash2(), otherKey.getHash2()); delete shape; */ @@ -202,15 +170,14 @@ void ShapeInfoTests::testCapsuleShape() { float radius = 1.23f; float height = 4.56f; info.setCapsule(radius, height); - DoubleHashKey key = info.getHash(); + HashKey key = info.getHash(); btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; - DoubleHashKey otherKey = otherInfo.getHash(); + HashKey otherKey = otherInfo.getHash(); QCOMPARE(key.getHash(), otherKey.getHash()); - QCOMPARE(key.getHash2(), otherKey.getHash2()); delete shape; */ diff --git a/tests/physics/src/ShapeInfoTests.h b/tests/physics/src/ShapeInfoTests.h index fbd89a13a8..1f6054dd1a 100644 --- a/tests/physics/src/ShapeInfoTests.h +++ b/tests/physics/src/ShapeInfoTests.h @@ -18,10 +18,6 @@ //#include "BulletTestUtils.h" //#include "../QTestExtensions.h" -// Enable this to manually run testHashCollisions -// (NOT a regular unit test; takes ~17 secs to run on an i7) -#define MANUAL_TEST false - class ShapeInfoTests : public QObject { Q_OBJECT private slots: From b00d15834255f9ad1f8d1e6359e104efbfb72698 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 26 Oct 2017 09:00:42 -0700 Subject: [PATCH 32/62] remove DoubleHashKey: it is no longer used --- libraries/shared/src/DoubleHashKey.cpp | 48 ------------------------- libraries/shared/src/DoubleHashKey.h | 49 -------------------------- 2 files changed, 97 deletions(-) delete mode 100644 libraries/shared/src/DoubleHashKey.cpp delete mode 100644 libraries/shared/src/DoubleHashKey.h diff --git a/libraries/shared/src/DoubleHashKey.cpp b/libraries/shared/src/DoubleHashKey.cpp deleted file mode 100644 index ded2f073eb..0000000000 --- a/libraries/shared/src/DoubleHashKey.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// -// DoubleHashKey.cpp -// libraries/shared/src -// -// Created by Andrew Meadows 2014.11.02 -// 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 "DoubleHashKey.h" - -const uint32_t NUM_PRIMES = 64; -const uint32_t PRIMES[] = { - 4194301U, 4194287U, 4194277U, 4194271U, 4194247U, 4194217U, 4194199U, 4194191U, - 4194187U, 4194181U, 4194173U, 4194167U, 4194143U, 4194137U, 4194131U, 4194107U, - 4194103U, 4194023U, 4194011U, 4194007U, 4193977U, 4193971U, 4193963U, 4193957U, - 4193939U, 4193929U, 4193909U, 4193869U, 4193807U, 4193803U, 4193801U, 4193789U, - 4193759U, 4193753U, 4193743U, 4193701U, 4193663U, 4193633U, 4193573U, 4193569U, - 4193551U, 4193549U, 4193531U, 4193513U, 4193507U, 4193459U, 4193447U, 4193443U, - 4193417U, 4193411U, 4193393U, 4193389U, 4193381U, 4193377U, 4193369U, 4193359U, - 4193353U, 4193327U, 4193309U, 4193303U, 4193297U, 4193279U, 4193269U, 4193263U -}; - -uint32_t DoubleHashKey::hashFunction(uint32_t value, uint32_t primeIndex) { - uint32_t hash = PRIMES[primeIndex % NUM_PRIMES] * (value + 1U); - hash += ~(hash << 15); - hash ^= (hash >> 10); - hash += (hash << 3); - hash ^= (hash >> 6); - hash += ~(hash << 11); - return hash ^ (hash >> 16); -} - -uint32_t DoubleHashKey::hashFunction2(uint32_t value) { - uint32_t hash = 0x811c9dc5U; - for (uint32_t i = 0; i < 4; i++ ) { - uint32_t byte = (value << (i * 8)) >> (24 - i * 8); - hash = ( hash ^ byte ) * 0x01000193U; - } - return hash; -} - -void DoubleHashKey::computeHash(uint32_t value, uint32_t primeIndex) { - _hash = DoubleHashKey::hashFunction(value, primeIndex); - _hash2 = DoubleHashKey::hashFunction2(value); -} diff --git a/libraries/shared/src/DoubleHashKey.h b/libraries/shared/src/DoubleHashKey.h deleted file mode 100644 index ca92a7197f..0000000000 --- a/libraries/shared/src/DoubleHashKey.h +++ /dev/null @@ -1,49 +0,0 @@ -// -// DoubleHashKey.h -// libraries/shared/src -// -// Created by Andrew Meadows 2014.11.02 -// 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_DoubleHashKey_h -#define hifi_DoubleHashKey_h - -#include - -// DoubleHashKey for use with btHashMap -class DoubleHashKey { -public: - static uint32_t hashFunction(uint32_t value, uint32_t primeIndex); - static uint32_t hashFunction2(uint32_t value); - - DoubleHashKey() : _hash(0), _hash2(0) { } - - DoubleHashKey(uint32_t value, uint32_t primeIndex = 0) : - _hash(hashFunction(value, primeIndex)), - _hash2(hashFunction2(value)) { - } - - void clear() { _hash = 0; _hash2 = 0; } - bool isNull() const { return _hash == 0 && _hash2 == 0; } - - bool equals(const DoubleHashKey& other) const { - return _hash == other._hash && _hash2 == other._hash2; - } - - void computeHash(uint32_t value, uint32_t primeIndex = 0); - uint32_t getHash() const { return _hash; } - uint32_t getHash2() const { return _hash2; } - - void setHash(uint32_t hash) { _hash = hash; } - void setHash2(uint32_t hash2) { _hash2 = hash2; } - -private: - uint32_t _hash; - uint32_t _hash2; -}; - -#endif // hifi_DoubleHashKey_h From 4cadcd79bfb522e453a75fa46f859ccbcdcd6d27 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 27 Oct 2017 17:07:19 +0200 Subject: [PATCH 33/62] Return full viewport when scissor is disabled and items in outline group --- libraries/render-utils/src/OutlineEffect.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index e3f4ff2b7f..fb2aab95f2 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -113,8 +113,9 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con RenderArgs* args = renderContext->args; ShapeKey::Builder defaultKeyBuilder; - auto framebufferSize = ressources->getSourceFrameSize(); +#if OUTLINE_USE_SCISSOR + auto framebufferSize = ressources->getSourceFrameSize(); // First thing we do is determine the projected bounding rect of all the outlined items auto outlinedRect = computeOutlineRect(inShapes, args->getViewFrustum(), framebufferSize); auto blurPixelWidth = _sharedParameters->_blurPixelWidths[_outlineIndex]; @@ -123,6 +124,10 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con // Add 1 pixel of extra margin to be on the safe side outputs = expandRect(outlinedRect, blurPixelWidth+1, framebufferSize); outlinedRect = expandRect(outputs, blurPixelWidth+1, framebufferSize); +#else + // Render full screen + outputs = args->_viewport; +#endif // Clear the framebuffer without stereo // Needs to be distinct from the other batch because using the clear call From 4cbee72eae60305cc616b53aee5fba092f2090ae Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 27 Oct 2017 17:50:22 +0200 Subject: [PATCH 34/62] Fixed warning on Mac and Ubuntu --- libraries/render-utils/src/OutlineEffect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index fb2aab95f2..f459ddc42c 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -367,7 +367,7 @@ void DebugOutline::run(const render::RenderContextPointer& renderContext, const const auto outlineRessources = input.get0(); const auto outlineRect = input.get1(); - if (_isDisplayEnabled && outlineRessources) { + if (_isDisplayEnabled && outlineRessources && outlineRect.z>0 && outlineRect.w>0) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); RenderArgs* args = renderContext->args; From d45febf1db706a7f8855004818816d9220a9f823 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 27 Oct 2017 08:55:41 -0700 Subject: [PATCH 35/62] add description of ShapeManager in comments --- libraries/physics/src/ShapeManager.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index 6ec3573b53..d75bb1dc4a 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -19,6 +19,28 @@ #include "HashKey.h" +// The ShapeManager handles the ref-counting on shared shapes: +// +// Each object added to the physics simulation gets a corresponding btRigidBody. +// The body has a btCollisionShape that represents the contours of its collision +// surface. Multiple bodies may have the same shape. Rather than create a unique +// btCollisionShape instance for every body with a particular shape we can instead +// use a single shape instance for all of the bodies. This is called "shape +// sharing". +// +// When body needs a new shape a description of ths shape (ShapeInfo) is assembled +// and a request is sent to the ShapeManager for a corresponding btCollisionShape +// pointer. The ShapeManager will compute a hash of the ShapeInfo's data and use +// that to find the shape in its map. If it finds one it increments the ref-count +// and returns the pointer. If not it asks the ShapeFactory to create it, adds an +// entry in the map with a ref-count of 1, and returns the pointer. +// +// When a body stops using a shape the ShapeManager must be informed so it can +// decrement its ref-count. When a ref-count drops to zero the ShapeManager +// doesn't delete it right away. Instead it puts the shape's key on a list delete +// later. When that list grows big enough the ShapeManager will remove any matching +// entries that still have zero ref-count. + class ShapeManager { public: @@ -51,6 +73,7 @@ private: ShapeReference() : refCount(0), shape(nullptr) {} }; + // btHashMap is required because it supports memory alignment of the btCollisionShapes btHashMap _shapeMap; btAlignedObjectArray _pendingGarbage; }; From 137fccbd91b46c20fddc891e52915824bd440f72 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 27 Oct 2017 08:56:26 -0700 Subject: [PATCH 36/62] cleanup ShapeFactory implementation --- libraries/physics/src/ShapeFactory.cpp | 63 ++++++++++++++------------ libraries/physics/src/ShapeFactory.h | 16 +------ 2 files changed, 35 insertions(+), 44 deletions(-) diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index cd0fba848a..f484f32fdf 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -16,6 +16,40 @@ #include "ShapeFactory.h" #include "BulletUtil.h" + +class StaticMeshShape : public btBvhTriangleMeshShape { +public: + StaticMeshShape() = delete; + + StaticMeshShape(btTriangleIndexVertexArray* dataArray) + : btBvhTriangleMeshShape(dataArray, true), _dataArray(dataArray) { + assert(_dataArray); + } + + ~StaticMeshShape() { + assert(_dataArray); + IndexedMeshArray& meshes = _dataArray->getIndexedMeshArray(); + for (int32_t i = 0; i < meshes.size(); ++i) { + btIndexedMesh mesh = meshes[i]; + mesh.m_numTriangles = 0; + delete [] mesh.m_triangleIndexBase; + mesh.m_triangleIndexBase = nullptr; + mesh.m_numVertices = 0; + delete [] mesh.m_vertexBase; + mesh.m_vertexBase = nullptr; + } + meshes.clear(); + delete _dataArray; + _dataArray = nullptr; + } + +private: + // the StaticMeshShape owns its vertex/index data + btTriangleIndexVertexArray* _dataArray; +}; + +// the dataArray must be created before we create the StaticMeshShape + // These are the same normalized directions used by the btShapeHull class. // 12 points for the face centers of a dodecahedron plus another 30 points // for the midpoints the edges, for a total of 42. @@ -230,23 +264,6 @@ btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info) { return dataArray; } -// util method -void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) { - assert(dataArray); - IndexedMeshArray& meshes = dataArray->getIndexedMeshArray(); - for (int32_t i = 0; i < meshes.size(); ++i) { - btIndexedMesh mesh = meshes[i]; - mesh.m_numTriangles = 0; - delete [] mesh.m_triangleIndexBase; - mesh.m_triangleIndexBase = nullptr; - mesh.m_numVertices = 0; - delete [] mesh.m_vertexBase; - mesh.m_vertexBase = nullptr; - } - meshes.clear(); - delete dataArray; -} - const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { btCollisionShape* shape = NULL; int type = info.getType(); @@ -431,7 +448,6 @@ void ShapeFactory::deleteShape(const btCollisionShape* shape) { assert(shape); // ShapeFactory is responsible for deleting all shapes, even the const ones that are stored // in the ShapeManager, so we must cast to non-const here when deleting. - // so we cast to non-const here when deleting memory. btCollisionShape* nonConstShape = const_cast(shape); if (nonConstShape->getShapeType() == (int)COMPOUND_SHAPE_PROXYTYPE) { btCompoundShape* compoundShape = static_cast(nonConstShape); @@ -448,14 +464,3 @@ void ShapeFactory::deleteShape(const btCollisionShape* shape) { } delete nonConstShape; } - -// the dataArray must be created before we create the StaticMeshShape -ShapeFactory::StaticMeshShape::StaticMeshShape(btTriangleIndexVertexArray* dataArray) -: btBvhTriangleMeshShape(dataArray, true), _dataArray(dataArray) { - assert(dataArray); -} - -ShapeFactory::StaticMeshShape::~StaticMeshShape() { - deleteStaticMeshArray(_dataArray); - _dataArray = nullptr; -} diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h index 2bf79f390c..704a7804b3 100644 --- a/libraries/physics/src/ShapeFactory.h +++ b/libraries/physics/src/ShapeFactory.h @@ -17,25 +17,11 @@ #include -// translates between ShapeInfo and btShape +// The ShapeFactory assembles and correctly disassembles btCollisionShapes. namespace ShapeFactory { const btCollisionShape* createShapeFromInfo(const ShapeInfo& info); void deleteShape(const btCollisionShape* shape); - - //btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info); - //void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray); - - class StaticMeshShape : public btBvhTriangleMeshShape { - public: - StaticMeshShape() = delete; - StaticMeshShape(btTriangleIndexVertexArray* dataArray); - ~StaticMeshShape(); - - private: - // the StaticMeshShape owns its vertex/index data - btTriangleIndexVertexArray* _dataArray; - }; }; #endif // hifi_ShapeFactory_h From 3a977f60397077aa2a7f18712c41736c6b354f24 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 27 Oct 2017 22:59:23 +0200 Subject: [PATCH 37/62] Disabled stereo in batch when using full screen outline --- libraries/render-utils/src/OutlineEffect.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index f459ddc42c..ad4c59b35a 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -278,6 +278,9 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I auto outlineFrameBuffer = inputs.get1(); auto outlineRect = inputs.get3(); + // TODO : If scissor isn't possible in stereo, send the AABox in the shader + // and do a raycasting per pixel to determine if we need to do the outline + // This should improve performance. if (outlineFrameBuffer && outlineRect.z>0 && outlineRect.w>0) { auto sceneDepthBuffer = inputs.get2(); const auto frameTransform = inputs.get0(); @@ -299,6 +302,9 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I } gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { +#if !OUTLINE_USE_SCISSOR + batch.enableStereo(false); +#endif batch.setFramebuffer(destinationFrameBuffer); batch.setViewportTransform(args->_viewport); From e9743d4c950a1e4591ec2690b12d9aa1de70561a Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 2 Nov 2017 10:00:30 +0100 Subject: [PATCH 38/62] Added jsdoc to SelectionScriptingInterface --- .../scripting/SelectionScriptingInterface.h | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/interface/src/scripting/SelectionScriptingInterface.h b/interface/src/scripting/SelectionScriptingInterface.h index 28c1713050..d9003c2c32 100644 --- a/interface/src/scripting/SelectionScriptingInterface.h +++ b/interface/src/scripting/SelectionScriptingInterface.h @@ -56,11 +56,44 @@ public: GameplayObjects getList(const QString& listName); + /**jsdoc + * Prints out the list of avatars, entities and overlays stored in a particular selection. + * @function Selection.printList + * @param listName {string} name of the selection + */ Q_INVOKABLE void printList(const QString& listName); + /**jsdoc + * Removes a named selection from the list of selections. + * @function Selection.removeListFromMap + * @param listName {string} name of the selection + * @returns {bool} true if the selection existed and was successfully removed. + */ Q_INVOKABLE bool removeListFromMap(const QString& listName); + /**jsdoc + * Add an item in a selection. + * @function Selection.addToSelectedItemsList + * @param listName {string} name of the selection + * @param itemType {string} the type of the item (one of "avatar", "entity" or "overlay") + * @param id {EntityID} the Id of the item to add to the selection + * @returns {bool} true if the item was successfully added. + */ Q_INVOKABLE bool addToSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id); + /**jsdoc + * Remove an item from a selection. + * @function Selection.removeFromSelectedItemsList + * @param listName {string} name of the selection + * @param itemType {string} the type of the item (one of "avatar", "entity" or "overlay") + * @param id {EntityID} the Id of the item to remove + * @returns {bool} true if the item was successfully removed. + */ Q_INVOKABLE bool removeFromSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id); + /**jsdoc + * Remove all items from a selection. + * @function Selection.clearSelectedItemsList + * @param listName {string} name of the selection + * @returns {bool} true if the item was successfully cleared. + */ Q_INVOKABLE bool clearSelectedItemsList(const QString& listName); signals: From 4b598a6e241e35edaa622856e4a04fade4dfbce5 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 3 Nov 2017 14:26:36 +0100 Subject: [PATCH 39/62] Working stencil mask with object bounding boxes --- libraries/render-utils/src/OutlineEffect.cpp | 189 ++++++++---------- libraries/render-utils/src/OutlineEffect.h | 10 +- libraries/render-utils/src/Outline_aabox.slv | 103 ++++++++++ libraries/render-utils/src/Outline_shared.slh | 16 +- .../render-utils/src/RenderDeferredTask.cpp | 2 + libraries/render/src/render/SortTask.cpp | 8 +- .../developer/utilities/render/outline.qml | 57 +++--- .../render/outlinePage/OutlinePage.qml | 143 ++++++------- 8 files changed, 305 insertions(+), 223 deletions(-) create mode 100644 libraries/render-utils/src/Outline_aabox.slv diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index ad4c59b35a..a42cbfe358 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -11,7 +11,6 @@ #include "OutlineEffect.h" #include "GeometryCache.h" -#include "RenderUtilsLogging.h" #include "CubeProjectedPolygon.h" @@ -28,10 +27,11 @@ #include "debug_deferred_buffer_frag.h" #include "Outline_frag.h" #include "Outline_filled_frag.h" +#include "Outline_aabox_vert.h" using namespace render; -#define OUTLINE_USE_SCISSOR false +#define OUTLINE_STENCIL_MASK 1 OutlineRessources::OutlineRessources() { } @@ -56,15 +56,16 @@ void OutlineRessources::update(const gpu::FramebufferPointer& primaryFrameBuffer } void OutlineRessources::allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { - _colorFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithoutDepth")); + _colorFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithStencil")); _colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0)); + _colorFrameBuffer->setDepthStencilBuffer(_depthStencilTexture, _depthStencilTexture->getTexelFormat()); } void OutlineRessources::allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { - auto depthFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); - auto depthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, _frameSize.x, _frameSize.y)); + auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); + _depthStencilTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, _frameSize.x, _frameSize.y)); _depthFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("outlineDepth")); - _depthFrameBuffer->setDepthStencilBuffer(depthTexture, depthFormat); + _depthFrameBuffer->setDepthStencilBuffer(_depthStencilTexture, depthFormat); } gpu::FramebufferPointer OutlineRessources::getDepthFramebuffer() { @@ -73,10 +74,10 @@ gpu::FramebufferPointer OutlineRessources::getDepthFramebuffer() { } gpu::TexturePointer OutlineRessources::getDepthTexture() { - return getDepthFramebuffer()->getDepthStencilBuffer(); + return _depthStencilTexture; } -gpu::FramebufferPointer OutlineRessources::getColorFramebuffer() { +gpu::FramebufferPointer OutlineRessources::getColorFramebuffer() { assert(_colorFrameBuffer); return _colorFrameBuffer; } @@ -96,6 +97,10 @@ void PrepareDrawOutline::run(const render::RenderContextPointer& renderContext, outputs = _ressources; } +gpu::PipelinePointer DrawOutlineMask::_stencilMaskPipeline; +gpu::PipelinePointer DrawOutlineMask::_stencilMaskFillPipeline; +gpu::BufferPointer DrawOutlineMask::_boundsBuffer; + DrawOutlineMask::DrawOutlineMask(unsigned int outlineIndex, render::ShapePlumberPointer shapePlumber, OutlineSharedParametersPointer parameters) : _outlineIndex{ outlineIndex }, @@ -108,26 +113,42 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con assert(renderContext->args->hasViewFrustum()); auto& inShapes = inputs.get0(); + if (!_stencilMaskPipeline || !_stencilMaskFillPipeline) { + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(true, false, gpu::LESS_EQUAL); + state->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_ZERO, gpu::State::STENCIL_OP_REPLACE)); + state->setColorWriteMask(false, false, false, false); + state->setCullMode(gpu::State::CULL_FRONT); + + gpu::StatePointer fillState = gpu::StatePointer(new gpu::State()); + fillState->setDepthTest(false, false, gpu::LESS_EQUAL); + fillState->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE)); + fillState->setColorWriteMask(false, false, false, false); + fillState->setCullMode(gpu::State::CULL_FRONT); + + auto vs = gpu::Shader::createVertex(std::string(Outline_aabox_vert)); + auto ps = gpu::StandardShaderLib::getDrawWhitePS(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + _stencilMaskPipeline = gpu::Pipeline::create(program, state); + _stencilMaskFillPipeline = gpu::Pipeline::create(program, fillState); + } + + if (!_boundsBuffer) { + _boundsBuffer = std::make_shared(sizeof(render::ItemBound)); + } + if (!inShapes.empty()) { auto ressources = inputs.get1(); RenderArgs* args = renderContext->args; ShapeKey::Builder defaultKeyBuilder; -#if OUTLINE_USE_SCISSOR - auto framebufferSize = ressources->getSourceFrameSize(); - // First thing we do is determine the projected bounding rect of all the outlined items - auto outlinedRect = computeOutlineRect(inShapes, args->getViewFrustum(), framebufferSize); - auto blurPixelWidth = _sharedParameters->_blurPixelWidths[_outlineIndex]; - //qCDebug(renderutils) << "Outline rect is " << outlinedRect.x << ' ' << outlinedRect.y << ' ' << outlinedRect.z << ' ' << outlinedRect.w; - - // Add 1 pixel of extra margin to be on the safe side - outputs = expandRect(outlinedRect, blurPixelWidth+1, framebufferSize); - outlinedRect = expandRect(outputs, blurPixelWidth+1, framebufferSize); -#else // Render full screen outputs = args->_viewport; -#endif // Clear the framebuffer without stereo // Needs to be distinct from the other batch because using the clear call @@ -135,25 +156,22 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setFramebuffer(ressources->getDepthFramebuffer()); - batch.clearDepthFramebuffer(1.0f); + batch.clearDepthStencilFramebuffer(1.0f, 0); }); + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); + + render::ItemBounds itemBounds; + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; auto maskPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder); auto maskSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned()); - glm::mat4 projMat; - Transform viewMat; - args->getViewFrustum().evalProjectionMatrix(projMat); - args->getViewFrustum().evalViewTransform(viewMat); - -#if OUTLINE_USE_SCISSOR - batch.setStateScissorRect(outlinedRect); -#endif - batch.setFramebuffer(ressources->getDepthFramebuffer()); - // Setup camera, projection and viewport for all items batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(projMat); @@ -164,7 +182,8 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con // Iterate through all inShapes and render the unskinned args->_shapePipeline = maskPipeline; batch.setPipeline(maskPipeline->pipeline); - for (auto items : inShapes) { + for (const auto& items : inShapes) { + itemBounds.insert(itemBounds.end(), items.second.begin(), items.second.end()); if (items.first.isSkinned()) { skinnedShapeKeys.push_back(items.first); } else { @@ -182,72 +201,34 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con args->_shapePipeline = nullptr; args->_batch = nullptr; }); + + _boundsBuffer->setData(itemBounds.size() * sizeof(render::ItemBound), (const gpu::Byte*) itemBounds.data()); + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + // Setup camera, projection and viewport for all items + batch.setViewportTransform(args->_viewport); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + // Draw stencil mask with object bounding boxes + const auto outlineWidthLoc = _stencilMaskPipeline->getProgram()->getUniforms().findLocation("outlineWidth"); + const auto sqrt3 = 1.74f; + const float blurPixelWidth = 2.0f * sqrt3 *_sharedParameters->_blurPixelWidths[_outlineIndex]; + const auto framebufferSize = ressources->getSourceFrameSize(); + + auto stencilPipeline = _sharedParameters->_isFilled.test(_outlineIndex) ? _stencilMaskFillPipeline : _stencilMaskPipeline; + batch.setPipeline(stencilPipeline); + batch.setResourceBuffer(0, _boundsBuffer); + batch._glUniform2f(outlineWidthLoc, blurPixelWidth / framebufferSize.x, blurPixelWidth / framebufferSize.y); + static const int NUM_VERTICES_PER_CUBE = 36; + batch.draw(gpu::TRIANGLES, NUM_VERTICES_PER_CUBE * (gpu::uint32) itemBounds.size(), 0); + }); } else { // Outline rect should be null as there are no outlined shapes outputs = glm::ivec4(0, 0, 0, 0); } } -glm::ivec4 DrawOutlineMask::computeOutlineRect(const render::ShapeBounds& shapes, - const ViewFrustum& viewFrustum, glm::ivec2 frameSize) { - glm::vec4 minMaxBounds{ - std::numeric_limits::max(), - std::numeric_limits::max(), - -std::numeric_limits::max(), - -std::numeric_limits::max(), - }; - - for (const auto& keyShapes : shapes) { - const auto& items = keyShapes.second; - - for (const auto& item : items) { - const auto& aabb = item.bound; - glm::vec2 bottomLeft; - glm::vec2 topRight; - - if (viewFrustum.getProjectedRect(aabb, bottomLeft, topRight)) { - minMaxBounds.x = std::min(minMaxBounds.x, bottomLeft.x); - minMaxBounds.y = std::min(minMaxBounds.y, bottomLeft.y); - minMaxBounds.z = std::max(minMaxBounds.z, topRight.x); - minMaxBounds.w = std::max(minMaxBounds.w, topRight.y); - } - } - } - - if (minMaxBounds.x != std::numeric_limits::max()) { - const glm::vec2 halfFrameSize{ frameSize.x*0.5f, frameSize.y*0.5f }; - glm::ivec4 rect; - - minMaxBounds += 1.0f; - rect.x = glm::clamp((int)floorf(minMaxBounds.x * halfFrameSize.x), 0, frameSize.x); - rect.y = glm::clamp((int)floorf(minMaxBounds.y * halfFrameSize.y), 0, frameSize.y); - rect.z = glm::clamp((int)ceilf(minMaxBounds.z * halfFrameSize.x), 0, frameSize.x); - rect.w = glm::clamp((int)ceilf(minMaxBounds.w * halfFrameSize.y), 0, frameSize.y); - - rect.z -= rect.x; - rect.w -= rect.y; - return rect; - } else { - return glm::ivec4(0, 0, 0, 0); - } -} - -glm::ivec4 DrawOutlineMask::expandRect(glm::ivec4 rect, int amount, glm::ivec2 frameSize) { - // Go bo back to min max values - rect.z += rect.x; - rect.w += rect.y; - - rect.x = std::max(0, rect.x - amount); - rect.y = std::max(0, rect.y - amount); - rect.z = std::min(frameSize.x, rect.z + amount); - rect.w = std::min(frameSize.y, rect.w + amount); - - // Back to width height - rect.z -= rect.x; - rect.w -= rect.y; - return rect; -} - gpu::PipelinePointer DrawOutline::_pipeline; gpu::PipelinePointer DrawOutline::_pipelineFilled; @@ -270,7 +251,7 @@ void DrawOutline::configure(const Config& config) { _parameters._size.x = (_size * _framebufferSize.y) / _framebufferSize.x; _parameters._size.y = _size; _sharedParameters->_blurPixelWidths[_outlineIndex] = (int)ceilf(_size * _framebufferSize.y); - _isFilled = (config.unoccludedFillOpacity > OPACITY_EPSILON || config.occludedFillOpacity > OPACITY_EPSILON); + _sharedParameters->_isFilled.set(_outlineIndex, (config.unoccludedFillOpacity > OPACITY_EPSILON || config.occludedFillOpacity > OPACITY_EPSILON)); _configuration.edit() = _parameters; } @@ -278,9 +259,6 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I auto outlineFrameBuffer = inputs.get1(); auto outlineRect = inputs.get3(); - // TODO : If scissor isn't possible in stereo, send the AABox in the shader - // and do a raycasting per pixel to determine if we need to do the outline - // This should improve performance. if (outlineFrameBuffer && outlineRect.z>0 && outlineRect.w>0) { auto sceneDepthBuffer = inputs.get2(); const auto frameTransform = inputs.get0(); @@ -302,9 +280,7 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I } gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { -#if !OUTLINE_USE_SCISSOR batch.enableStereo(false); -#endif batch.setFramebuffer(destinationFrameBuffer); batch.setViewportTransform(args->_viewport); @@ -312,9 +288,6 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I batch.resetViewTransform(); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport)); batch.setPipeline(pipeline); -#if OUTLINE_USE_SCISSOR - batch.setStateScissorRect(outlineRect); -#endif batch.setUniformBuffer(OUTLINE_PARAMS_SLOT, _configuration); batch.setUniformBuffer(FRAME_TRANSFORM_SLOT, frameTransform->getFrameTransformBuffer()); @@ -331,7 +304,7 @@ const gpu::PipelinePointer& DrawOutline::getPipeline() { gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setDepthTest(gpu::State::DepthTest(false, false)); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); - state->setScissorEnable(OUTLINE_USE_SCISSOR); + state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL)); auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); auto ps = gpu::Shader::createPixel(std::string(Outline_frag)); @@ -351,7 +324,7 @@ const gpu::PipelinePointer& DrawOutline::getPipeline() { gpu::Shader::makeProgram(*program, slotBindings); _pipelineFilled = gpu::Pipeline::create(program, state); } - return _isFilled ? _pipelineFilled : _pipeline; + return _sharedParameters->_isFilled.test(_outlineIndex) ? _pipelineFilled : _pipeline; } DebugOutline::DebugOutline() { @@ -380,9 +353,7 @@ void DebugOutline::run(const render::RenderContextPointer& renderContext, const gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); -#if OUTLINE_USE_SCISSOR - batch.setStateScissorRect(outlineRect); -#endif + batch.setFramebuffer(outlineRessources->getColorFramebuffer()); const auto geometryBuffer = DependencyManager::get(); @@ -416,8 +387,8 @@ void DebugOutline::initializePipelines() { "Could not find source placeholder"); auto state = std::make_shared(); - state->setDepthTest(gpu::State::DepthTest(false)); - state->setScissorEnable(OUTLINE_USE_SCISSOR); + state->setDepthTest(gpu::State::DepthTest(false, false)); + state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL)); const auto vs = gpu::Shader::createVertex(VERTEX_SHADER); @@ -473,7 +444,7 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende auto state = std::make_shared(); state->setDepthTest(true, true, gpu::LESS_EQUAL); state->setColorWriteMask(false, false, false, false); - state->setScissorEnable(OUTLINE_USE_SCISSOR); + initMaskPipelines(*shapePlumber, state); } auto sharedParameters = std::make_shared(); diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index ee5e503de9..a2bfc88c81 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -33,6 +33,7 @@ protected: gpu::FramebufferPointer _depthFrameBuffer; gpu::FramebufferPointer _colorFrameBuffer; + gpu::TexturePointer _depthStencilTexture; glm::ivec2 _frameSize; @@ -48,6 +49,7 @@ public: OutlineSharedParameters(); std::array _blurPixelWidths; + std::bitset _isFilled; }; using OutlineSharedParametersPointer = std::shared_ptr; @@ -84,9 +86,10 @@ protected: unsigned int _outlineIndex; render::ShapePlumberPointer _shapePlumber; OutlineSharedParametersPointer _sharedParameters; - - static glm::ivec4 computeOutlineRect(const render::ShapeBounds& shapes, const ViewFrustum& viewFrustum, glm::ivec2 frameSize); - static glm::ivec4 expandRect(glm::ivec4 rect, int amount, glm::ivec2 frameSize); + + static gpu::BufferPointer _boundsBuffer; + static gpu::PipelinePointer _stencilMaskPipeline; + static gpu::PipelinePointer _stencilMaskFillPipeline; }; class DrawOutlineConfig : public render::Job::Config { @@ -158,7 +161,6 @@ private: OutlineSharedParametersPointer _sharedParameters; OutlineConfigurationBuffer _configuration; glm::ivec2 _framebufferSize{ 0,0 }; - bool _isFilled{ false }; float _size; }; diff --git a/libraries/render-utils/src/Outline_aabox.slv b/libraries/render-utils/src/Outline_aabox.slv new file mode 100644 index 0000000000..1a46ccd9c7 --- /dev/null +++ b/libraries/render-utils/src/Outline_aabox.slv @@ -0,0 +1,103 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// Draw and transform the fed vertex position with the standard MVP stack +// and offset the vertices by a certain amount in the vertex direction +// +// Created by Olivier Prat on 11/02/2017 +// Copyright 2017 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()$> + +struct ItemBound { + vec4 id_boundPos; + vec4 boundDim_s; +}; + +#if defined(GPU_GL410) +uniform samplerBuffer ssbo0Buffer; +ItemBound getItemBound(int i) { + int offset = 2 * i; + ItemBound bound; + bound.id_boundPos = texelFetch(ssbo0Buffer, offset); + bound.boundDim_s = texelFetch(ssbo0Buffer, offset + 1); + return bound; +} +#else +layout(std140) buffer ssbo0Buffer { + ItemBound bounds[]; +}; +ItemBound getItemBound(int i) { + ItemBound bound = bounds[i]; + return bound; +} +#endif + +uniform vec2 outlineWidth; + +void main(void) { + const vec3 UNIT_BOX_VERTICES[8] = vec3[8]( + vec3(0.0, 1.0, 0.0), + vec3(1.0, 1.0, 0.0), + vec3(1.0, 0.0, 0.0), + vec3(0.0, 0.0, 0.0), + vec3(0.0, 1.0, 1.0), + vec3(1.0, 1.0, 1.0), + vec3(1.0, 0.0, 1.0), + vec3(0.0, 0.0, 1.0) + ); + const vec3 UNIT_BOX_NORMALS[8] = vec3[8]( + vec3(-1.0, 1.0, -1.0), + vec3(1.0, 1.0, -1.0), + vec3(1.0, -1.0, -1.0), + vec3(-1.0, -1.0, -1.0), + vec3(-1.0, 1.0, 1.0), + vec3(1.0, 1.0, 1.0), + vec3(1.0, -1.0, 1.0), + vec3(-1.0, -1.0, 1.0) + ); + const int NUM_VERTICES_PER_CUBE = 36; + const int UNIT_BOX_TRIANGLE_INDICES[NUM_VERTICES_PER_CUBE] = int[NUM_VERTICES_PER_CUBE]( + 0, 1, 2, + 0, 2, 3, + 3, 2, 6, + 3, 6, 7, + 7, 6, 5, + 7, 5, 4, + 4, 5, 1, + 4, 1, 0, + 1, 5, 6, + 1, 6, 2, + 4, 0, 3, + 4, 3, 7 + ); + + int boundID = gl_VertexID / NUM_VERTICES_PER_CUBE; + int vertexID = gl_VertexID - boundID * NUM_VERTICES_PER_CUBE; + int triangleIndex = UNIT_BOX_TRIANGLE_INDICES[vertexID]; + vec3 cubeVec = UNIT_BOX_VERTICES[triangleIndex]; + + ItemBound bound = getItemBound(boundID); + vec3 boundPos = bound.id_boundPos.yzw; + vec3 boundDim = bound.boundDim_s.xyz; + + vec4 pos = vec4(boundPos + boundDim * cubeVec.xyz, 1.0); + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, pos, gl_Position)$> + + // Offset the vertex to take into account the outline width + pos.xyz += UNIT_BOX_NORMALS[triangleIndex]; + vec4 offsetPosition; + <$transformModelToClipPos(cam, obj, pos, offsetPosition)$> + gl_Position.xy += normalize(offsetPosition.xy-gl_Position.xy) * outlineWidth * gl_Position.w; +} diff --git a/libraries/render-utils/src/Outline_shared.slh b/libraries/render-utils/src/Outline_shared.slh index 2ebece8903..3fd089e2fc 100644 --- a/libraries/render-utils/src/Outline_shared.slh +++ b/libraries/render-utils/src/Outline_shared.slh @@ -1,20 +1,20 @@ // glsl / C++ compatible source as interface for Outline #ifdef __cplusplus -# define VEC2 glm::vec2 -# define VEC3 glm::vec3 -# define VEC4 glm::vec4 +# define TVEC2 glm::vec2 +# define TVEC3 glm::vec3 +# define TVEC4 glm::vec4 #else -# define VEC2 vec2 -# define VEC3 vec3 -# define VEC4 vec4 +# define TVEC2 vec2 +# define TVEC3 vec3 +# define TVEC4 vec4 #endif struct OutlineParameters { - VEC3 _color; + TVEC3 _color; float _intensity; - VEC2 _size; + TVEC2 _size; float _unoccludedFillOpacity; float _occludedFillOpacity; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 63936b0809..16db1281f5 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -35,6 +35,7 @@ #include "TextureCache.h" #include "ZoneRenderer.h" #include "FadeEffect.h" +#include "RenderUtilsLogging.h" #include "AmbientOcclusionEffect.h" #include "AntialiasingEffect.h" @@ -471,6 +472,7 @@ void Blit::run(const RenderContextPointer& renderContext, const gpu::Framebuffer auto blitFbo = renderArgs->_blitFramebuffer; if (!blitFbo) { + qCWarning(renderutils) << "Blit::run - no blit frame buffer."; return; } diff --git a/libraries/render/src/render/SortTask.cpp b/libraries/render/src/render/SortTask.cpp index 5b7ead4b6a..00146e393d 100644 --- a/libraries/render/src/render/SortTask.cpp +++ b/libraries/render/src/render/SortTask.cpp @@ -74,9 +74,13 @@ void render::depthSortItems(const RenderContextPointer& renderContext, bool fron std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), backToFrontSort); } - // Finally once sorted result to a list of itemID + // Finally once sorted result to a list of itemID and keep uniques + render::ItemID previousID = Item::INVALID_ITEM_ID; for (auto& item : itemBoundSorts) { - outItems.emplace_back(ItemBound(item._id, item._bounds)); + if (item._id != previousID) { + outItems.emplace_back(ItemBound(item._id, item._bounds)); + previousID = item._id; + } } } diff --git a/scripts/developer/utilities/render/outline.qml b/scripts/developer/utilities/render/outline.qml index 39acd854ac..8269ea830e 100644 --- a/scripts/developer/utilities/render/outline.qml +++ b/scripts/developer/utilities/render/outline.qml @@ -8,22 +8,34 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 +import QtQuick 2.7 import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 import "outlinePage" +import "qrc:///qml/styles-uit" +import "qrc:///qml/controls-uit" as HifiControls -Item { +Rectangle { id: root + HifiConstants { id: hifi;} + color: hifi.colors.baseGray; + anchors.margins: hifi.dimensions.contentMargin.x + property var debugConfig: Render.getConfig("RenderMainView.OutlineDebug") signal sendToScript(var message); Column { - spacing: 8 - anchors.fill: parent + spacing: 5 + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: hifi.dimensions.contentMargin.x Row { - spacing: 8 - CheckBox { + spacing: 10 + anchors.left: parent.left + anchors.right: parent.right + + HifiControls.CheckBox { id: debug text: "View Mask" checked: root.debugConfig["viewMask"] @@ -31,14 +43,14 @@ Item { root.debugConfig["viewMask"] = checked; } } - CheckBox { + HifiControls.CheckBox { text: "Hover select" checked: false onCheckedChanged: { sendToScript("pick "+checked.toString()) } } - CheckBox { + HifiControls.CheckBox { text: "Add to selection" checked: false onCheckedChanged: { @@ -56,28 +68,13 @@ Item { sendToScript("outline "+currentIndex) } - Tab { - title: "Outl.0" - OutlinePage { - outlineIndex: 0 - } - } - Tab { - title: "Outl.1" - OutlinePage { - outlineIndex: 1 - } - } - Tab { - title: "Outl.2" - OutlinePage { - outlineIndex: 2 - } - } - Tab { - title: "Outl.3" - OutlinePage { - outlineIndex: 3 + Repeater { + model: [ 0, 1, 2, 3 ] + Tab { + title: "Outl."+modelData + OutlinePage { + outlineIndex: modelData + } } } } diff --git a/scripts/developer/utilities/render/outlinePage/OutlinePage.qml b/scripts/developer/utilities/render/outlinePage/OutlinePage.qml index a78bf02d3e..ff1d7fa23b 100644 --- a/scripts/developer/utilities/render/outlinePage/OutlinePage.qml +++ b/scripts/developer/utilities/render/outlinePage/OutlinePage.qml @@ -8,19 +8,28 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 +import QtQuick 2.7 import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 import "../configSlider" +import "qrc:///qml/styles-uit" +import "qrc:///qml/controls-uit" as HifiControls -Item { +Rectangle { id: root property var outlineIndex: 0 property var drawConfig: Render.getConfig("RenderMainView.OutlineEffect"+outlineIndex) - Column { - spacing: 8 + HifiConstants { id: hifi;} + anchors.margins: hifi.dimensions.contentMargin.x - CheckBox { + Column { + spacing: 5 + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: hifi.dimensions.contentMargin.x + + HifiControls.CheckBox { id: glow text: "Glow" checked: root.drawConfig["glow"] @@ -28,84 +37,78 @@ Item { root.drawConfig["glow"] = checked; } } - ConfigSlider { - label: "Width" - integral: false - config: root.drawConfig - property: "width" - max: 5.0 - min: 0.0 - width: 280 - } - ConfigSlider { - label: "Intensity" - integral: false - config: root.drawConfig - property: "intensity" - max: 1.0 - min: 0.0 - width: 280 - } + Repeater { + model: ["Width:width:5.0:0.0", + "Intensity:intensity:1.0:0.0" + ] + ConfigSlider { + label: qsTr(modelData.split(":")[0]) + integral: false + config: root.drawConfig + property: modelData.split(":")[1] + max: modelData.split(":")[2] + min: modelData.split(":")[3] + + anchors.left: parent.left + anchors.right: parent.right + } + } GroupBox { title: "Color" - width: 280 + anchors.left: parent.left + anchors.right: parent.right Column { - spacing: 8 + spacing: 10 + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: hifi.dimensions.contentMargin.x - ConfigSlider { - label: "Red" - integral: false - config: root.drawConfig - property: "colorR" - max: 1.0 - min: 0.0 - width: 270 - } - ConfigSlider { - label: "Green" - integral: false - config: root.drawConfig - property: "colorG" - max: 1.0 - min: 0.0 - width: 270 - } - ConfigSlider { - label: "Blue" - integral: false - config: root.drawConfig - property: "colorB" - max: 1.0 - min: 0.0 - width: 270 + Repeater { + model: ["Red:colorR:1.0:0.0", + "Green:colorG:1.0:0.0", + "Blue:colorB:1.0:0.0" + ] + ConfigSlider { + label: qsTr(modelData.split(":")[0]) + integral: false + config: root.drawConfig + property: modelData.split(":")[1] + max: modelData.split(":")[2] + min: modelData.split(":")[3] + + anchors.left: parent.left + anchors.right: parent.right + } } } } GroupBox { title: "Fill Opacity" - width: 280 + anchors.left: parent.left + anchors.right: parent.right Column { - spacing: 8 + spacing: 10 + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: hifi.dimensions.contentMargin.x - ConfigSlider { - label: "Unoccluded" - integral: false - config: root.drawConfig - property: "unoccludedFillOpacity" - max: 1.0 - min: 0.0 - width: 270 - } - ConfigSlider { - label: "Occluded" - integral: false - config: root.drawConfig - property: "occludedFillOpacity" - max: 1.0 - min: 0.0 - width: 270 + Repeater { + model: ["Unoccluded:unoccludedFillOpacity:1.0:0.0", + "Occluded:occludedFillOpacity:1.0:0.0" + ] + ConfigSlider { + label: qsTr(modelData.split(":")[0]) + integral: false + config: root.drawConfig + property: modelData.split(":")[1] + max: modelData.split(":")[2] + min: modelData.split(":")[3] + + anchors.left: parent.left + anchors.right: parent.right + } } } } From 9e6472b57783c514e34121941956fcceed902cc1 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 6 Nov 2017 15:55:57 +0100 Subject: [PATCH 40/62] Added OutlineStyleStage --- .../ui/overlays/ContextOverlayInterface.cpp | 20 ++- .../src/ui/overlays/ContextOverlayInterface.h | 8 +- libraries/render-utils/src/OutlineEffect.cpp | 151 ++++++++++++------ libraries/render-utils/src/OutlineEffect.h | 89 ++++++----- .../render-utils/src/RenderDeferredTask.cpp | 16 +- .../render-utils/src/UpdateSceneTask.cpp | 2 + libraries/render/src/render/FilterTask.cpp | 7 +- libraries/render/src/render/FilterTask.h | 5 +- libraries/render/src/render/OutlineStyle.h | 39 +++++ .../render/src/render/OutlineStyleStage.cpp | 46 ++++++ .../render/src/render/OutlineStyleStage.h | 77 +++++++++ libraries/render/src/render/Scene.cpp | 64 ++++++++ libraries/render/src/render/Scene.h | 22 ++- 13 files changed, 427 insertions(+), 119 deletions(-) create mode 100644 libraries/render/src/render/OutlineStyle.h create mode 100644 libraries/render/src/render/OutlineStyleStage.cpp create mode 100644 libraries/render/src/render/OutlineStyleStage.h diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 1e9f827cb2..32e4137526 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -68,11 +68,15 @@ ContextOverlayInterface::ContextOverlayInterface() { connect(&qApp->getOverlays(), &Overlays::hoverEnterOverlay, this, &ContextOverlayInterface::contextOverlays_hoverEnterOverlay); connect(&qApp->getOverlays(), &Overlays::hoverLeaveOverlay, this, &ContextOverlayInterface::contextOverlays_hoverLeaveOverlay); - _selectionToSceneHandlers[0].initialize("contextOverlayHighlightList"); - connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandlers[0], &SelectionToSceneHandler::selectedItemsListChanged); - for (auto i = 1; i < render::Scene::MAX_OUTLINE_COUNT; i++) { - _selectionToSceneHandlers[i].initialize(QString("contextOverlayHighlightList") + QString::number(i)); - connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandlers[i], &SelectionToSceneHandler::selectedItemsListChanged); + { + render::Transaction transaction; + initializeSelectionToSceneHandler(_selectionToSceneHandlers[0], "contextOverlayHighlightList", transaction); + for (auto i = 1; i < MAX_SELECTION_COUNT; i++) { + auto selectionName = QString("contextOverlayHighlightList") + QString::number(i); + initializeSelectionToSceneHandler(_selectionToSceneHandlers[i], selectionName, transaction); + } + const render::ScenePointer& scene = qApp->getMain3DScene(); + scene->enqueueTransaction(transaction); } auto nodeList = DependencyManager::get(); @@ -81,6 +85,12 @@ ContextOverlayInterface::ContextOverlayInterface() { _challengeOwnershipTimeoutTimer.setSingleShot(true); } +void ContextOverlayInterface::initializeSelectionToSceneHandler(SelectionToSceneHandler& handler, const QString& selectionName, render::Transaction& transaction) { + handler.initialize(selectionName); + connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &handler, &SelectionToSceneHandler::selectedItemsListChanged); + transaction.resetSelectionOutline(selectionName.toStdString()); +} + static const uint32_t MOUSE_HW_ID = 0; static const uint32_t LEFT_HAND_HW_ID = 1; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index e5ebcdcff3..81e398e15d 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -76,6 +76,11 @@ private slots: void handleChallengeOwnershipReplyPacket(QSharedPointer packet, SharedNodePointer sendingNode); private: + + enum { + MAX_SELECTION_COUNT = 16 + }; + bool _verboseLogging { true }; bool _enabled { true }; EntityItemID _currentEntityWithContextOverlay{}; @@ -91,8 +96,9 @@ private: void disableEntityHighlight(const EntityItemID& entityItemID); void deletingEntity(const EntityItemID& entityItemID); + void initializeSelectionToSceneHandler(SelectionToSceneHandler& handler, const QString& selectionName, render::Transaction& transaction); - SelectionToSceneHandler _selectionToSceneHandlers[render::Scene::MAX_OUTLINE_COUNT]; + SelectionToSceneHandler _selectionToSceneHandlers[MAX_SELECTION_COUNT]; Q_INVOKABLE void startChallengeOwnershipTimer(); QTimer _challengeOwnershipTimeoutTimer; diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index a42cbfe358..590277bf6d 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -83,7 +83,11 @@ gpu::FramebufferPointer OutlineRessources::getColorFramebuffer() { } OutlineSharedParameters::OutlineSharedParameters() { - std::fill(_blurPixelWidths.begin(), _blurPixelWidths.end(), 0); + _outlineIds.fill(render::OutlineStyleStage::INVALID_INDEX); +} + +float OutlineSharedParameters::getBlurPixelWidth(const render::OutlineStyle& style, int frameBufferHeight) { + return ceilf(style.width * frameBufferHeight / 400.0f); } PrepareDrawOutline::PrepareDrawOutline() { @@ -141,8 +145,12 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con _boundsBuffer = std::make_shared(sizeof(render::ItemBound)); } - if (!inShapes.empty()) { + auto outlineStage = renderContext->_scene->getStage(render::OutlineStyleStage::getName()); + auto outlineId = _sharedParameters->_outlineIds[_outlineIndex]; + + if (!inShapes.empty() && !render::OutlineStyleStage::isIndexInvalid(outlineId)) { auto ressources = inputs.get1(); + auto& outline = outlineStage->getOutline(outlineId); RenderArgs* args = renderContext->args; ShapeKey::Builder defaultKeyBuilder; @@ -213,10 +221,10 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con // Draw stencil mask with object bounding boxes const auto outlineWidthLoc = _stencilMaskPipeline->getProgram()->getUniforms().findLocation("outlineWidth"); const auto sqrt3 = 1.74f; - const float blurPixelWidth = 2.0f * sqrt3 *_sharedParameters->_blurPixelWidths[_outlineIndex]; + const float blurPixelWidth = 2.0f * sqrt3 * OutlineSharedParameters::getBlurPixelWidth(outline._style, args->_viewport.w); const auto framebufferSize = ressources->getSourceFrameSize(); - auto stencilPipeline = _sharedParameters->_isFilled.test(_outlineIndex) ? _stencilMaskFillPipeline : _stencilMaskPipeline; + auto stencilPipeline = outline._style.isFilled() ? _stencilMaskFillPipeline : _stencilMaskPipeline; batch.setPipeline(stencilPipeline); batch.setResourceBuffer(0, _boundsBuffer); batch._glUniform2f(outlineWidthLoc, blurPixelWidth / framebufferSize.x, blurPixelWidth / framebufferSize.y); @@ -237,24 +245,6 @@ DrawOutline::DrawOutline(unsigned int outlineIndex, OutlineSharedParametersPoint _sharedParameters{ parameters } { } -void DrawOutline::configure(const Config& config) { - const auto OPACITY_EPSILON = 5e-3f; - - _parameters._color = config.color; - _parameters._intensity = config.intensity * (config.glow ? 2.0f : 1.0f); - _parameters._unoccludedFillOpacity = config.unoccludedFillOpacity; - _parameters._occludedFillOpacity = config.occludedFillOpacity; - _parameters._threshold = config.glow ? 1.0f : 1e-3f; - _parameters._blurKernelSize = std::min(7, std::max(2, (int)floorf(config.width * 3 + 0.5f))); - // Size is in normalized screen height. We decide that for outline width = 1, this is equal to 1/400. - _size = config.width / 400.0f; - _parameters._size.x = (_size * _framebufferSize.y) / _framebufferSize.x; - _parameters._size.y = _size; - _sharedParameters->_blurPixelWidths[_outlineIndex] = (int)ceilf(_size * _framebufferSize.y); - _sharedParameters->_isFilled.set(_outlineIndex, (config.unoccludedFillOpacity > OPACITY_EPSILON || config.occludedFillOpacity > OPACITY_EPSILON)); - _configuration.edit() = _parameters; -} - void DrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { auto outlineFrameBuffer = inputs.get1(); auto outlineRect = inputs.get3(); @@ -267,39 +257,50 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I auto framebufferSize = glm::ivec2(outlinedDepthTexture->getDimensions()); if (sceneDepthBuffer) { - auto pipeline = getPipeline(); auto args = renderContext->args; - if (_framebufferSize != framebufferSize) - { - _parameters._size.x = (_size * framebufferSize.y) / framebufferSize.x; - _parameters._size.y = _size; - _framebufferSize = framebufferSize; - _sharedParameters->_blurPixelWidths[_outlineIndex] = (int)ceilf(_size * _framebufferSize.y); - _configuration.edit() = _parameters; + auto outlineStage = renderContext->_scene->getStage(render::OutlineStyleStage::getName()); + auto outlineId = _sharedParameters->_outlineIds[_outlineIndex]; + if (!render::OutlineStyleStage::isIndexInvalid(outlineId)) { + auto& outline = outlineStage->getOutline(_sharedParameters->_outlineIds[_outlineIndex]); + auto pipeline = getPipeline(outline._style); + { + auto& shaderParameters = _configuration.edit(); + + shaderParameters._color = outline._style.color; + shaderParameters._intensity = outline._style.intensity * (outline._style.glow ? 2.0f : 1.0f); + shaderParameters._unoccludedFillOpacity = outline._style.unoccludedFillOpacity; + shaderParameters._occludedFillOpacity = outline._style.occludedFillOpacity; + shaderParameters._threshold = outline._style.glow ? 1.0f : 1e-3f; + shaderParameters._blurKernelSize = std::min(7, std::max(2, (int)floorf(outline._style.width * 3 + 0.5f))); + // Size is in normalized screen height. We decide that for outline width = 1, this is equal to 1/400. + auto size = outline._style.width / 400.0f; + shaderParameters._size.x = (size * framebufferSize.y) / framebufferSize.x; + shaderParameters._size.y = size; + } + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.setFramebuffer(destinationFrameBuffer); + + batch.setViewportTransform(args->_viewport); + batch.setProjectionTransform(glm::mat4()); + batch.resetViewTransform(); + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport)); + batch.setPipeline(pipeline); + + batch.setUniformBuffer(OUTLINE_PARAMS_SLOT, _configuration); + batch.setUniformBuffer(FRAME_TRANSFORM_SLOT, frameTransform->getFrameTransformBuffer()); + batch.setResourceTexture(SCENE_DEPTH_SLOT, sceneDepthBuffer->getPrimaryDepthTexture()); + batch.setResourceTexture(OUTLINED_DEPTH_SLOT, outlinedDepthTexture); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); } - - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.setFramebuffer(destinationFrameBuffer); - - batch.setViewportTransform(args->_viewport); - batch.setProjectionTransform(glm::mat4()); - batch.resetViewTransform(); - batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport)); - batch.setPipeline(pipeline); - - batch.setUniformBuffer(OUTLINE_PARAMS_SLOT, _configuration); - batch.setUniformBuffer(FRAME_TRANSFORM_SLOT, frameTransform->getFrameTransformBuffer()); - batch.setResourceTexture(SCENE_DEPTH_SLOT, sceneDepthBuffer->getPrimaryDepthTexture()); - batch.setResourceTexture(OUTLINED_DEPTH_SLOT, outlinedDepthTexture); - batch.draw(gpu::TRIANGLE_STRIP, 4); - }); } } } -const gpu::PipelinePointer& DrawOutline::getPipeline() { +const gpu::PipelinePointer& DrawOutline::getPipeline(const render::OutlineStyle& style) { if (!_pipeline) { gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setDepthTest(gpu::State::DepthTest(false, false)); @@ -324,7 +325,7 @@ const gpu::PipelinePointer& DrawOutline::getPipeline() { gpu::Shader::makeProgram(*program, slotBindings); _pipelineFilled = gpu::Pipeline::create(program, state); } - return _sharedParameters->_isFilled.test(_outlineIndex) ? _pipelineFilled : _pipeline; + return style.isFilled() ? _pipelineFilled : _pipeline; } DebugOutline::DebugOutline() { @@ -424,6 +425,35 @@ const gpu::PipelinePointer& DebugOutline::getDepthPipeline() { return _depthPipeline; } +void SelectionToOutline::run(const render::RenderContextPointer& renderContext, Outputs& outputs) { + auto outlineStage = renderContext->_scene->getStage(render::OutlineStyleStage::getName()); + + outputs.clear(); + _sharedParameters->_outlineIds.fill(render::OutlineStyleStage::INVALID_INDEX); + + for (auto i = 0; i < OutlineSharedParameters::MAX_OUTLINE_COUNT; i++) { + std::ostringstream stream; + stream << "contextOverlayHighlightList"; + if (i > 0) { + stream << i; + } + auto selectionName = stream.str(); + auto outlineId = outlineStage->getOutlineIdBySelection(selectionName); + if (!render::OutlineStyleStage::isIndexInvalid(outlineId)) { + _sharedParameters->_outlineIds[outputs.size()] = outlineId; + outputs.emplace_back(selectionName); + } + } +} + +void ExtractSelectionName::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { + if (_outlineIndex < inputs.size()) { + outputs = inputs[_outlineIndex]; + } else { + outputs.clear(); + } +} + DrawOutlineTask::DrawOutlineTask() { } @@ -433,7 +463,7 @@ void DrawOutlineTask::configure(const Config& config) { } void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { - const auto groups = inputs.getN(0).get(); + const auto items = inputs.getN(0).get(); const auto sceneFrameBuffer = inputs.getN(1); const auto primaryFramebuffer = inputs.getN(2); const auto deferredFrameTransform = inputs.getN(3); @@ -449,12 +479,15 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende } auto sharedParameters = std::make_shared(); + const auto outlineSelectionNames = task.addJob("SelectionToOutline", sharedParameters); + // Prepare for outline group rendering. const auto outlineRessources = task.addJob("PrepareOutline", primaryFramebuffer); render::Varying outline0Rect; - for (auto i = 0; i < render::Scene::MAX_OUTLINE_COUNT; i++) { - const auto groupItems = groups[i]; + for (auto i = 0; i < OutlineSharedParameters::MAX_OUTLINE_COUNT; i++) { + const auto selectionName = task.addJob("ExtractSelectionName", outlineSelectionNames, i); + const auto groupItems = addSelectItemJobs(task, selectionName, items); const auto outlinedItemIDs = task.addJob("OutlineMetaToSubItemIDs", groupItems); const auto outlinedItems = task.addJob("OutlineMetaToSubItems", outlinedItemIDs); @@ -490,6 +523,20 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende task.addJob("OutlineDebug", debugInputs); } +const render::Varying DrawOutlineTask::addSelectItemJobs(JobModel& task, const render::Varying& selectionName, + const RenderFetchCullSortTask::BucketList& items) { + const auto& opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE]; + const auto& transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; + const auto& metas = items[RenderFetchCullSortTask::META]; + + const auto selectMetaInput = SelectItems::Inputs(metas, Varying(), selectionName).asVarying(); + const auto selectedMetas = task.addJob("MetaSelection", selectMetaInput); + const auto selectMetaAndOpaqueInput = SelectItems::Inputs(opaques, selectedMetas, selectionName).asVarying(); + const auto selectedMetasAndOpaques = task.addJob("OpaqueSelection", selectMetaAndOpaqueInput); + const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques, selectionName).asVarying(); + return task.addJob("TransparentSelection", selectItemInput); +} + #include "model_shadow_vert.h" #include "skin_model_shadow_vert.h" diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index a2bfc88c81..e25184eb5b 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -13,6 +13,9 @@ #define hifi_render_utils_OutlineEffect_h #include +#include +#include + #include "DeferredFramebuffer.h" #include "DeferredFrameTransform.h" @@ -46,10 +49,15 @@ using OutlineRessourcesPointer = std::shared_ptr; class OutlineSharedParameters { public: + enum { + MAX_OUTLINE_COUNT = 8 + }; + OutlineSharedParameters(); - std::array _blurPixelWidths; - std::bitset _isFilled; + std::array _outlineIds; + + static float getBlurPixelWidth(const render::OutlineStyle& style, int frameBufferHeight); }; using OutlineSharedParametersPointer = std::shared_ptr; @@ -70,6 +78,38 @@ private: }; +class SelectionToOutline { +public: + + using Outputs = std::vector; + using JobModel = render::Job::ModelO; + + SelectionToOutline(OutlineSharedParametersPointer parameters) : _sharedParameters{ parameters } {} + + void run(const render::RenderContextPointer& renderContext, Outputs& outputs); + +private: + + OutlineSharedParametersPointer _sharedParameters; +}; + +class ExtractSelectionName { +public: + + using Inputs = SelectionToOutline::Outputs; + using Outputs = std::string; + using JobModel = render::Job::ModelIO; + + ExtractSelectionName(unsigned int outlineIndex) : _outlineIndex{ outlineIndex } {} + + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); + +private: + + unsigned int _outlineIndex; + +}; + class DrawOutlineMask { public: @@ -92,49 +132,15 @@ protected: static gpu::PipelinePointer _stencilMaskFillPipeline; }; -class DrawOutlineConfig : public render::Job::Config { - Q_OBJECT - Q_PROPERTY(bool glow MEMBER glow NOTIFY dirty) - Q_PROPERTY(float width MEMBER width NOTIFY dirty) - Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty) - Q_PROPERTY(float colorR READ getColorR WRITE setColorR NOTIFY dirty) - Q_PROPERTY(float colorG READ getColorG WRITE setColorG NOTIFY dirty) - Q_PROPERTY(float colorB READ getColorB WRITE setColorB NOTIFY dirty) - Q_PROPERTY(float unoccludedFillOpacity MEMBER unoccludedFillOpacity NOTIFY dirty) - Q_PROPERTY(float occludedFillOpacity MEMBER occludedFillOpacity NOTIFY dirty) - -public: - - void setColorR(float value) { color.r = value; emit dirty(); } - float getColorR() const { return color.r; } - - void setColorG(float value) { color.g = value; emit dirty(); } - float getColorG() const { return color.g; } - - void setColorB(float value) { color.b = value; emit dirty(); } - float getColorB() const { return color.b; } - - glm::vec3 color{ 1.f, 0.7f, 0.2f }; - float width{ 2.0f }; - float intensity{ 0.9f }; - float unoccludedFillOpacity{ 0.0f }; - float occludedFillOpacity{ 0.0f }; - bool glow{ false }; - -signals: - void dirty(); -}; - class DrawOutline { public: using Inputs = render::VaryingSet4; - using Config = DrawOutlineConfig; + using Config = render::Job::Config; using JobModel = render::Job::ModelI; DrawOutline(unsigned int outlineIndex, OutlineSharedParametersPointer parameters); - void configure(const Config& config); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); private: @@ -151,7 +157,7 @@ private: using OutlineConfigurationBuffer = gpu::StructBuffer; - const gpu::PipelinePointer& getPipeline(); + static const gpu::PipelinePointer& getPipeline(const render::OutlineStyle& style); static gpu::PipelinePointer _pipeline; static gpu::PipelinePointer _pipelineFilled; @@ -160,8 +166,6 @@ private: OutlineParameters _parameters; OutlineSharedParametersPointer _sharedParameters; OutlineConfigurationBuffer _configuration; - glm::ivec2 _framebufferSize{ 0,0 }; - float _size; }; class DebugOutlineConfig : public render::Job::Config { @@ -201,8 +205,7 @@ private: class DrawOutlineTask { public: - using Groups = render::VaryingArray; - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet4; using Config = render::Task::Config; using JobModel = render::Task::ModelI; @@ -214,6 +217,8 @@ public: private: static void initMaskPipelines(render::ShapePlumber& plumber, gpu::StatePointer state); + static const render::Varying addSelectItemJobs(JobModel& task, const render::Varying& selectionName, const RenderFetchCullSortTask::BucketList& items); + }; #endif // hifi_render_utils_OutlineEffect_h diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 16db1281f5..52b8896e69 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -64,11 +64,11 @@ const render::Varying RenderDeferredTask::addSelectItemJobs(JobModel& task, cons const render::Varying& metas, const render::Varying& opaques, const render::Varying& transparents) { - const auto selectMetaInput = SelectItems::Inputs(metas, Varying()).asVarying(); + const auto selectMetaInput = SelectItems::Inputs(metas, Varying(), std::string()).asVarying(); const auto selectedMetas = task.addJob("MetaSelection", selectMetaInput, selectionName); - const auto selectMetaAndOpaqueInput = SelectItems::Inputs(opaques, selectedMetas).asVarying(); + const auto selectMetaAndOpaqueInput = SelectItems::Inputs(opaques, selectedMetas, std::string()).asVarying(); const auto selectedMetasAndOpaques = task.addJob("OpaqueSelection", selectMetaAndOpaqueInput, selectionName); - const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques).asVarying(); + const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques, std::string()).asVarying(); return task.addJob("TransparentSelection", selectItemInput, selectionName); } @@ -187,16 +187,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren // Select items that need to be outlined const auto selectionBaseName = "contextOverlayHighlightList"; const auto selectedItems = addSelectItemJobs(task, selectionBaseName, metas, opaques, transparents); - DrawOutlineTask::Groups outlineGroups; - outlineGroups[0] = selectedItems; - for (auto i = 1; i < render::Scene::MAX_OUTLINE_COUNT; i++) { - std::ostringstream selectionName; - selectionName << selectionBaseName; - selectionName << i; - outlineGroups[i] = addSelectItemJobs(task, selectionName.str().c_str(), metas, opaques, transparents); - } - const auto outlineInputs = DrawOutlineTask::Inputs(outlineGroups, deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying(); + const auto outlineInputs = DrawOutlineTask::Inputs(items.get0(), deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying(); task.addJob("DrawOutline", outlineInputs); task.addJob("OutlineRangeTimer", outlineRangeTimer); diff --git a/libraries/render-utils/src/UpdateSceneTask.cpp b/libraries/render-utils/src/UpdateSceneTask.cpp index 06e02907f2..d37569b526 100644 --- a/libraries/render-utils/src/UpdateSceneTask.cpp +++ b/libraries/render-utils/src/UpdateSceneTask.cpp @@ -15,6 +15,7 @@ #include "BackgroundStage.h" #include "HazeStage.h" #include +#include #include "DeferredLightingEffect.h" void UpdateSceneTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { @@ -22,6 +23,7 @@ void UpdateSceneTask::build(JobModel& task, const render::Varying& input, render task.addJob("BackgroundStageSetup"); task.addJob("HazeStageSetup"); task.addJob("TransitionStageSetup"); + task.addJob("OutlineStyleStageSetup"); task.addJob("DefaultLightingSetup"); diff --git a/libraries/render/src/render/FilterTask.cpp b/libraries/render/src/render/FilterTask.cpp index f60f5895b7..20d29f3e5d 100644 --- a/libraries/render/src/render/FilterTask.cpp +++ b/libraries/render/src/render/FilterTask.cpp @@ -57,7 +57,12 @@ void SliceItems::run(const RenderContextPointer& renderContext, const ItemBounds } void SelectItems::run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems) { - auto selection = renderContext->_scene->getSelection(_name); + auto selectionName{ _name }; + if (!inputs.get2().empty()) { + selectionName = inputs.get2(); + } + + auto selection = renderContext->_scene->getSelection(selectionName); const auto& selectedItems = selection.getItems(); const auto& inItems = inputs.get0(); const auto itemsToAppend = inputs[1]; diff --git a/libraries/render/src/render/FilterTask.h b/libraries/render/src/render/FilterTask.h index a7180b6cde..9b40728b00 100644 --- a/libraries/render/src/render/FilterTask.h +++ b/libraries/render/src/render/FilterTask.h @@ -113,12 +113,13 @@ namespace render { // Keep items belonging to the job selection class SelectItems { public: - using Inputs = VaryingSet2; + using Inputs = VaryingSet3; using JobModel = Job::ModelIO; std::string _name; + SelectItems() {} SelectItems(const Selection::Name& name) : _name(name) {} - + void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems); }; diff --git a/libraries/render/src/render/OutlineStyle.h b/libraries/render/src/render/OutlineStyle.h new file mode 100644 index 0000000000..cdcdf79f3e --- /dev/null +++ b/libraries/render/src/render/OutlineStyle.h @@ -0,0 +1,39 @@ +// +// OutlineStyle.h + +// Created by Olivier Prat on 11/06/2017. +// Copyright 2017 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_render_utils_OutlineStyle_h +#define hifi_render_utils_OutlineStyle_h + +#include + +#include + +namespace render { + + // This holds the configuration for a particular outline style + class OutlineStyle { + public: + + bool isFilled() const { + return unoccludedFillOpacity > 5e-3f || occludedFillOpacity > 5e-3f; + } + + glm::vec3 color{ 1.f, 0.7f, 0.2f }; + float width{ 2.0f }; + float intensity{ 0.9f }; + float unoccludedFillOpacity{ 0.0f }; + float occludedFillOpacity{ 0.0f }; + bool glow{ false }; + std::string selectionName; + }; + +} + +#endif // hifi_render_utils_OutlineStyle_h \ No newline at end of file diff --git a/libraries/render/src/render/OutlineStyleStage.cpp b/libraries/render/src/render/OutlineStyleStage.cpp new file mode 100644 index 0000000000..e3fd1672de --- /dev/null +++ b/libraries/render/src/render/OutlineStyleStage.cpp @@ -0,0 +1,46 @@ +#include "OutlineStyleStage.h" + +using namespace render; + +std::string OutlineStyleStage::_name("OutlineStyle"); + +OutlineStyleStage::Index OutlineStyleStage::addOutline(const std::string& selectionName, const OutlineStyle& style) { + Outline outline{ selectionName, style }; + Index id; + + id = _outlines.newElement(outline); + _activeOutlineIds.push_back(id); + + return id; +} + +void OutlineStyleStage::removeOutline(Index index) { + OutlineIdList::iterator idIterator = std::find(_activeOutlineIds.begin(), _activeOutlineIds.end(), index); + if (idIterator != _activeOutlineIds.end()) { + _activeOutlineIds.erase(idIterator); + } + if (!_outlines.isElementFreed(index)) { + _outlines.freeElement(index); + } +} + +Index OutlineStyleStage::getOutlineIdBySelection(const std::string& selectionName) const { + for (auto outlineId : _activeOutlineIds) { + const auto& outline = _outlines.get(outlineId); + if (outline._selectionName == selectionName) { + return outlineId; + } + } + return INVALID_INDEX; +} + +OutlineStyleStageSetup::OutlineStyleStageSetup() { +} + +void OutlineStyleStageSetup::run(const render::RenderContextPointer& renderContext) { + if (!renderContext->_scene->getStage(OutlineStyleStage::getName())) { + auto stage = std::make_shared(); + renderContext->_scene->resetStage(OutlineStyleStage::getName(), stage); + } +} + diff --git a/libraries/render/src/render/OutlineStyleStage.h b/libraries/render/src/render/OutlineStyleStage.h new file mode 100644 index 0000000000..79835a22a4 --- /dev/null +++ b/libraries/render/src/render/OutlineStyleStage.h @@ -0,0 +1,77 @@ +// +// OutlineStyleStage.h + +// Created by Olivier Prat on 07/07/2017. +// Copyright 2017 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_render_utils_outlinestage_h +#define hifi_render_utils_outlinestage_h + +#include "Stage.h" +#include "Engine.h" +#include "IndexedContainer.h" +#include "OutlineStyle.h" + +namespace render { + + // OutlineStyle stage to set up OutlineStyle-related effects + class OutlineStyleStage : public Stage { + public: + + class Outline { + public: + + Outline(const std::string& selectionName, const OutlineStyle& style) : _selectionName{ selectionName }, _style{ style } { } + + std::string _selectionName; + OutlineStyle _style; + + }; + + static const std::string& getName() { return _name; } + + using Index = render::indexed_container::Index; + static const Index INVALID_INDEX{ render::indexed_container::INVALID_INDEX }; + using OutlineIdList = render::indexed_container::Indices; + + static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } + + bool checkOutlineId(Index index) const { return _outlines.checkIndex(index); } + + const Outline& getOutline(Index OutlineStyleId) const { return _outlines.get(OutlineStyleId); } + Outline& editOutline(Index OutlineStyleId) { return _outlines.edit(OutlineStyleId); } + + Index addOutline(const std::string& selectionName, const OutlineStyle& style = OutlineStyle()); + Index getOutlineIdBySelection(const std::string& selectionName) const; + void removeOutline(Index index); + + OutlineIdList::iterator begin() { return _activeOutlineIds.begin(); } + OutlineIdList::iterator end() { return _activeOutlineIds.end(); } + + private: + + using Outlines = render::indexed_container::IndexedVector; + + static std::string _name; + + Outlines _outlines; + OutlineIdList _activeOutlineIds; + }; + using OutlineStyleStagePointer = std::shared_ptr; + + class OutlineStyleStageSetup { + public: + using JobModel = render::Job::Model; + + OutlineStyleStageSetup(); + void run(const RenderContextPointer& renderContext); + + protected: + }; + +} +#endif // hifi_render_utils_outlinestage_h diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 714be2a8c6..0e680dfdaf 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -14,6 +14,7 @@ #include #include "Logging.h" #include "TransitionStage.h" +#include "OutlineStyleStage.h" using namespace render; @@ -54,6 +55,18 @@ void Transaction::resetSelection(const Selection& selection) { _resetSelections.emplace_back(selection); } +void Transaction::resetSelectionOutline(const std::string& selectionName, const OutlineStyle& style) { + _outlineResets.emplace_back(OutlineReset{ selectionName, style }); +} + +void Transaction::removeOutlineFromSelection(const std::string& selectionName) { + _outlineRemoves.emplace_back(selectionName); +} + +void Transaction::querySelectionOutline(const std::string& selectionName, SelectionOutlineQueryFunc func) { + _outlineQueries.emplace_back(OutlineQuery{ selectionName, func }); +} + void Transaction::merge(const Transaction& transaction) { _resetItems.insert(_resetItems.end(), transaction._resetItems.begin(), transaction._resetItems.end()); _removedItems.insert(_removedItems.end(), transaction._removedItems.begin(), transaction._removedItems.end()); @@ -62,6 +75,9 @@ void Transaction::merge(const Transaction& transaction) { _addedTransitions.insert(_addedTransitions.end(), transaction._addedTransitions.begin(), transaction._addedTransitions.end()); _queriedTransitions.insert(_queriedTransitions.end(), transaction._queriedTransitions.begin(), transaction._queriedTransitions.end()); _reAppliedTransitions.insert(_reAppliedTransitions.end(), transaction._reAppliedTransitions.begin(), transaction._reAppliedTransitions.end()); + _outlineResets.insert(_outlineResets.end(), transaction._outlineResets.begin(), transaction._outlineResets.end()); + _outlineRemoves.insert(_outlineRemoves.end(), transaction._outlineRemoves.begin(), transaction._outlineRemoves.end()); + _outlineQueries.insert(_outlineQueries.end(), transaction._outlineQueries.begin(), transaction._outlineQueries.end()); } @@ -176,6 +192,10 @@ void Scene::processTransactionFrame(const Transaction& transaction) { // resets and potential NEW items resetSelections(transaction._resetSelections); } + + resetOutlines(transaction._outlineResets); + removeOutlines(transaction._outlineRemoves); + queryOutlines(transaction._outlineQueries); } void Scene::resetItems(const Transaction::Resets& transactions) { @@ -316,6 +336,50 @@ void Scene::queryTransitionItems(const Transaction::TransitionQueries& transacti } } +void Scene::resetOutlines(const Transaction::OutlineResets& transactions) { + auto outlineStage = getStage(OutlineStyleStage::getName()); + + for (auto& transaction : transactions) { + const auto& selectionName = std::get<0>(transaction); + const auto& newStyle = std::get<1>(transaction); + auto outlineId = outlineStage->getOutlineIdBySelection(selectionName); + + if (OutlineStyleStage::isIndexInvalid(outlineId)) { + outlineStage->addOutline(selectionName, newStyle); + } else { + outlineStage->editOutline(outlineId)._style = newStyle; + } + } +} + +void Scene::removeOutlines(const Transaction::OutlineRemoves& transactions) { + auto outlineStage = getStage(OutlineStyleStage::getName()); + + for (auto& selectionName : transactions) { + auto outlineId = outlineStage->getOutlineIdBySelection(selectionName); + + if (!OutlineStyleStage::isIndexInvalid(outlineId)) { + outlineStage->removeOutline(outlineId); + } + } +} + +void Scene::queryOutlines(const Transaction::OutlineQueries& transactions) { + auto outlineStage = getStage(OutlineStyleStage::getName()); + + for (auto& transaction : transactions) { + const auto& selectionName = std::get<0>(transaction); + const auto& func = std::get<1>(transaction); + auto outlineId = outlineStage->getOutlineIdBySelection(selectionName); + + if (!OutlineStyleStage::isIndexInvalid(outlineId)) { + func(&outlineStage->editOutline(outlineId)._style); + } else { + func(nullptr); + } + } +} + void Scene::collectSubItems(ItemID parentId, ItemIDs& subItems) const { // Access the true item auto& item = _items[parentId]; diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 1f3b9a72c3..38f528aced 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -17,6 +17,7 @@ #include "Stage.h" #include "Selection.h" #include "Transition.h" +#include "OutlineStyle.h" namespace render { @@ -37,6 +38,7 @@ class Transaction { public: typedef std::function TransitionQueryFunc; + typedef std::function SelectionOutlineQueryFunc; Transaction() {} ~Transaction() {} @@ -61,6 +63,10 @@ public: // Selection transactions void resetSelection(const Selection& selection); + void resetSelectionOutline(const std::string& selectionName, const OutlineStyle& style = OutlineStyle()); + void removeOutlineFromSelection(const std::string& selectionName); + void querySelectionOutline(const std::string& selectionName, SelectionOutlineQueryFunc func); + void merge(const Transaction& transaction); // Checkers if there is work to do when processing the transaction @@ -75,6 +81,9 @@ protected: using TransitionQuery = std::tuple; using TransitionReApply = ItemID; using SelectionReset = Selection; + using OutlineReset = std::tuple; + using OutlineRemove = std::string; + using OutlineQuery = std::tuple; using Resets = std::vector; using Removes = std::vector; @@ -83,6 +92,9 @@ protected: using TransitionQueries = std::vector; using TransitionReApplies = std::vector; using SelectionResets = std::vector; + using OutlineResets = std::vector; + using OutlineRemoves = std::vector; + using OutlineQueries = std::vector; Resets _resetItems; Removes _removedItems; @@ -91,6 +103,9 @@ protected: TransitionQueries _queriedTransitions; TransitionReApplies _reAppliedTransitions; SelectionResets _resetSelections; + OutlineResets _outlineResets; + OutlineRemoves _outlineRemoves; + OutlineQueries _outlineQueries; }; typedef std::queue TransactionQueue; @@ -103,10 +118,6 @@ typedef std::queue TransactionQueue; class Scene { public: - enum { - MAX_OUTLINE_COUNT = 8 - }; - Scene(glm::vec3 origin, float size); ~Scene(); @@ -192,6 +203,9 @@ protected: void transitionItems(const Transaction::TransitionAdds& transactions); void reApplyTransitions(const Transaction::TransitionReApplies& transactions); void queryTransitionItems(const Transaction::TransitionQueries& transactions); + void resetOutlines(const Transaction::OutlineResets& transactions); + void removeOutlines(const Transaction::OutlineRemoves& transactions); + void queryOutlines(const Transaction::OutlineQueries& transactions); void collectSubItems(ItemID parentId, ItemIDs& subItems) const; From 650df247611fe83ab1b6c4e2035f42d410ce4e02 Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Mon, 6 Nov 2017 14:27:02 -0800 Subject: [PATCH 41/62] Improved Haze UI in Create menu --- scripts/system/html/entityProperties.html | 46 +++++++++++++---------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index e7d4aed3fb..7fdeee67d3 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -555,20 +555,21 @@
-
+
+ + < + /div>
-
-
-
-
-
-
-
+
+
+
+
+
@@ -582,6 +583,15 @@
+
+
+
+
+ + +
+
+
@@ -600,20 +610,18 @@
-
-
-
-
-
- - +
+
+ + +
+
+ + +
-
- - -
From 3cc445ff023b2a641996cde913b1812cf95a52bf Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 7 Nov 2017 11:07:25 +0100 Subject: [PATCH 42/62] Renamed outline to highlight --- .gitignore | 1 + .../ui/overlays/ContextOverlayInterface.cpp | 2 +- .../src/{Outline.slf => Highlight.slf} | 6 +- .../src/{Outline.slh => Highlight.slh} | 24 +- ...{OutlineEffect.cpp => HighlightEffect.cpp} | 239 +++++++++--------- .../{OutlineEffect.h => HighlightEffect.h} | 112 ++++---- ...{Outline_aabox.slv => Highlight_aabox.slv} | 0 ...utline_filled.slf => Highlight_filled.slf} | 6 +- ...utline_shared.slh => Highlight_shared.slh} | 4 +- .../render-utils/src/RenderDeferredTask.cpp | 10 +- .../render-utils/src/UpdateSceneTask.cpp | 4 +- .../render/src/render/HighlightStage.cpp | 46 ++++ libraries/render/src/render/HighlightStage.h | 77 ++++++ .../{OutlineStyle.h => HighlightStyle.h} | 17 +- .../render/src/render/OutlineStyleStage.cpp | 46 ---- .../render/src/render/OutlineStyleStage.h | 77 ------ libraries/render/src/render/Scene.cpp | 58 ++--- libraries/render/src/render/Scene.h | 34 +-- .../{debugOutline.js => debugHighlight.js} | 22 +- .../render/{outline.qml => highlight.qml} | 12 +- .../HighlightPage.qml} | 6 +- .../utilities/render/highlightPage/qmldir | 1 + .../utilities/render/outlinePage/qmldir | 1 - 23 files changed, 403 insertions(+), 402 deletions(-) rename libraries/render-utils/src/{Outline.slf => Highlight.slf} (72%) rename libraries/render-utils/src/{Outline.slh => Highlight.slh} (75%) rename libraries/render-utils/src/{OutlineEffect.cpp => HighlightEffect.cpp} (63%) rename libraries/render-utils/src/{OutlineEffect.h => HighlightEffect.h} (55%) rename libraries/render-utils/src/{Outline_aabox.slv => Highlight_aabox.slv} (100%) rename libraries/render-utils/src/{Outline_filled.slf => Highlight_filled.slf} (71%) rename libraries/render-utils/src/{Outline_shared.slh => Highlight_shared.slh} (84%) create mode 100644 libraries/render/src/render/HighlightStage.cpp create mode 100644 libraries/render/src/render/HighlightStage.h rename libraries/render/src/render/{OutlineStyle.h => HighlightStyle.h} (68%) delete mode 100644 libraries/render/src/render/OutlineStyleStage.cpp delete mode 100644 libraries/render/src/render/OutlineStyleStage.h rename scripts/developer/utilities/render/{debugOutline.js => debugHighlight.js} (88%) rename scripts/developer/utilities/render/{outline.qml => highlight.qml} (88%) rename scripts/developer/utilities/render/{outlinePage/OutlinePage.qml => highlightPage/HighlightPage.qml} (96%) create mode 100644 scripts/developer/utilities/render/highlightPage/qmldir delete mode 100644 scripts/developer/utilities/render/outlinePage/qmldir diff --git a/.gitignore b/.gitignore index 072e6001da..4b6949e268 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,7 @@ gvr-interface/libs/* # ignore files for various dev environments TAGS *.sw[po] +*.qmlc # ignore node files for the console node_modules diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 32e4137526..6a21221a8b 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -88,7 +88,7 @@ ContextOverlayInterface::ContextOverlayInterface() { void ContextOverlayInterface::initializeSelectionToSceneHandler(SelectionToSceneHandler& handler, const QString& selectionName, render::Transaction& transaction) { handler.initialize(selectionName); connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &handler, &SelectionToSceneHandler::selectedItemsListChanged); - transaction.resetSelectionOutline(selectionName.toStdString()); + transaction.resetSelectionHighlight(selectionName.toStdString()); } static const uint32_t MOUSE_HW_ID = 0; diff --git a/libraries/render-utils/src/Outline.slf b/libraries/render-utils/src/Highlight.slf similarity index 72% rename from libraries/render-utils/src/Outline.slf rename to libraries/render-utils/src/Highlight.slf index 68ef870cba..bf65f92613 100644 --- a/libraries/render-utils/src/Outline.slf +++ b/libraries/render-utils/src/Highlight.slf @@ -1,5 +1,5 @@ -// Outline.slf -// Add outline effect based on two zbuffers : one containing the total scene z and another +// Highlight.slf +// Add highlight effect based on two zbuffers : one containing the total scene z and another // with the z of only the objects to be outlined. // This is the version without the fill effect inside the silhouette. // @@ -9,5 +9,5 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include Outline.slh@> +<@include Highlight.slh@> <$main(0)$> diff --git a/libraries/render-utils/src/Outline.slh b/libraries/render-utils/src/Highlight.slh similarity index 75% rename from libraries/render-utils/src/Outline.slh rename to libraries/render-utils/src/Highlight.slh index aeaf20a24a..2faa10682e 100644 --- a/libraries/render-utils/src/Outline.slh +++ b/libraries/render-utils/src/Highlight.slh @@ -1,7 +1,7 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> <$declareDeferredFrameTransform()$> -<@include Outline_shared.slh@> +<@include Highlight_shared.slh@> -uniform outlineParamsBuffer { - OutlineParameters params; +uniform highlightParamsBuffer { + HighlightParameters params; }; uniform sampler2D sceneDepthMap; -uniform sampler2D outlinedDepthMap; +uniform sampler2D highlightedDepthMap; in vec2 varTexCoord0; out vec4 outFragColor; @@ -35,20 +35,20 @@ void main(void) { // We offset by half a texel to be centered on the depth sample. If we don't do this // the blur will have a different width between the left / right sides and top / bottom // sides of the silhouette - float outlinedDepth = texture(outlinedDepthMap, varTexCoord0).x; + float highlightedDepth = texture(highlightedDepthMap, varTexCoord0).x; float intensity = 0.0; - if (outlinedDepth < FAR_Z) { - // We're not on the far plane so we are on the outlined object, thus no outline to do! + if (highlightedDepth < FAR_Z) { + // We're not on the far plane so we are on the highlighted object, thus no outline to do! <@if IS_FILLED@> // But we need to fill the interior float sceneDepth = texture(sceneDepthMap, varTexCoord0).x; // Transform to linear depth for better precision - outlinedDepth = -evalZeyeFromZdb(outlinedDepth); + highlightedDepth = -evalZeyeFromZdb(highlightedDepth); sceneDepth = -evalZeyeFromZdb(sceneDepth); // Are we occluded? - intensity = sceneDepth < (outlinedDepth-LINEAR_DEPTH_BIAS) ? params._occludedFillOpacity : params._unoccludedFillOpacity; + intensity = sceneDepth < (highlightedDepth-LINEAR_DEPTH_BIAS) ? params._occludedFillOpacity : params._unoccludedFillOpacity; <@else@> discard; <@endif@> @@ -70,8 +70,8 @@ void main(void) { for (x=0 ; x=0.0 && uv.x<=1.0) { - outlinedDepth = texture(outlinedDepthMap, uv).x; - intensity += (outlinedDepth < FAR_Z) ? 1.0 : 0.0; + highlightedDepth = texture(highlightedDepthMap, uv).x; + intensity += (highlightedDepth < FAR_Z) ? 1.0 : 0.0; weight += 1.0; } uv.x += deltaUv.x; diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp similarity index 63% rename from libraries/render-utils/src/OutlineEffect.cpp rename to libraries/render-utils/src/HighlightEffect.cpp index 590277bf6d..e332be53de 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -1,5 +1,5 @@ // -// OutlineEffect.cpp +// HighlightEffect.cpp // render-utils/src/ // // Created by Olivier Prat on 08/08/17. @@ -8,7 +8,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "OutlineEffect.h" +#include "HighlightEffect.h" #include "GeometryCache.h" @@ -25,18 +25,19 @@ #include "surfaceGeometry_copyDepth_frag.h" #include "debug_deferred_buffer_vert.h" #include "debug_deferred_buffer_frag.h" -#include "Outline_frag.h" -#include "Outline_filled_frag.h" -#include "Outline_aabox_vert.h" +#include "Highlight_frag.h" +#include "Highlight_filled_frag.h" +#include "Highlight_aabox_vert.h" +#include "nop_frag.h" using namespace render; #define OUTLINE_STENCIL_MASK 1 -OutlineRessources::OutlineRessources() { +HighlightRessources::HighlightRessources() { } -void OutlineRessources::update(const gpu::FramebufferPointer& primaryFrameBuffer) { +void HighlightRessources::update(const gpu::FramebufferPointer& primaryFrameBuffer) { auto newFrameSize = glm::ivec2(primaryFrameBuffer->getSize()); // If the buffer size changed, we need to delete our FBOs and recreate them at the @@ -55,64 +56,64 @@ void OutlineRessources::update(const gpu::FramebufferPointer& primaryFrameBuffer } } -void OutlineRessources::allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { +void HighlightRessources::allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { _colorFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithStencil")); _colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0)); _colorFrameBuffer->setDepthStencilBuffer(_depthStencilTexture, _depthStencilTexture->getTexelFormat()); } -void OutlineRessources::allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { +void HighlightRessources::allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); _depthStencilTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, _frameSize.x, _frameSize.y)); - _depthFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("outlineDepth")); + _depthFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("highlightDepth")); _depthFrameBuffer->setDepthStencilBuffer(_depthStencilTexture, depthFormat); } -gpu::FramebufferPointer OutlineRessources::getDepthFramebuffer() { +gpu::FramebufferPointer HighlightRessources::getDepthFramebuffer() { assert(_depthFrameBuffer); return _depthFrameBuffer; } -gpu::TexturePointer OutlineRessources::getDepthTexture() { +gpu::TexturePointer HighlightRessources::getDepthTexture() { return _depthStencilTexture; } -gpu::FramebufferPointer OutlineRessources::getColorFramebuffer() { +gpu::FramebufferPointer HighlightRessources::getColorFramebuffer() { assert(_colorFrameBuffer); return _colorFrameBuffer; } -OutlineSharedParameters::OutlineSharedParameters() { - _outlineIds.fill(render::OutlineStyleStage::INVALID_INDEX); +HighlightSharedParameters::HighlightSharedParameters() { + _highlightIds.fill(render::HighlightStage::INVALID_INDEX); } -float OutlineSharedParameters::getBlurPixelWidth(const render::OutlineStyle& style, int frameBufferHeight) { - return ceilf(style.width * frameBufferHeight / 400.0f); +float HighlightSharedParameters::getBlurPixelWidth(const render::HighlightStyle& style, int frameBufferHeight) { + return ceilf(style.outlineWidth * frameBufferHeight / 400.0f); } -PrepareDrawOutline::PrepareDrawOutline() { - _ressources = std::make_shared(); +PrepareDrawHighlight::PrepareDrawHighlight() { + _ressources = std::make_shared(); } -void PrepareDrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { +void PrepareDrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { auto destinationFrameBuffer = inputs; _ressources->update(destinationFrameBuffer); outputs = _ressources; } -gpu::PipelinePointer DrawOutlineMask::_stencilMaskPipeline; -gpu::PipelinePointer DrawOutlineMask::_stencilMaskFillPipeline; -gpu::BufferPointer DrawOutlineMask::_boundsBuffer; +gpu::PipelinePointer DrawHighlightMask::_stencilMaskPipeline; +gpu::PipelinePointer DrawHighlightMask::_stencilMaskFillPipeline; +gpu::BufferPointer DrawHighlightMask::_boundsBuffer; -DrawOutlineMask::DrawOutlineMask(unsigned int outlineIndex, - render::ShapePlumberPointer shapePlumber, OutlineSharedParametersPointer parameters) : - _outlineIndex{ outlineIndex }, +DrawHighlightMask::DrawHighlightMask(unsigned int highlightIndex, + render::ShapePlumberPointer shapePlumber, HighlightSharedParametersPointer parameters) : + _highlightIndex{ highlightIndex }, _shapePlumber { shapePlumber }, _sharedParameters{ parameters } { } -void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { +void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); auto& inShapes = inputs.get0(); @@ -130,8 +131,8 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con fillState->setColorWriteMask(false, false, false, false); fillState->setCullMode(gpu::State::CULL_FRONT); - auto vs = gpu::Shader::createVertex(std::string(Outline_aabox_vert)); - auto ps = gpu::StandardShaderLib::getDrawWhitePS(); + auto vs = gpu::Shader::createVertex(std::string(Highlight_aabox_vert)); + auto ps = gpu::Shader::createPixel(std::string(nop_frag)); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -145,12 +146,12 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con _boundsBuffer = std::make_shared(sizeof(render::ItemBound)); } - auto outlineStage = renderContext->_scene->getStage(render::OutlineStyleStage::getName()); - auto outlineId = _sharedParameters->_outlineIds[_outlineIndex]; + auto highlightStage = renderContext->_scene->getStage(render::HighlightStage::getName()); + auto highlightId = _sharedParameters->_highlightIds[_highlightIndex]; - if (!inShapes.empty() && !render::OutlineStyleStage::isIndexInvalid(outlineId)) { + if (!inShapes.empty() && !render::HighlightStage::isIndexInvalid(highlightId)) { auto ressources = inputs.get1(); - auto& outline = outlineStage->getOutline(outlineId); + auto& highlight = highlightStage->getHighlight(highlightId); RenderArgs* args = renderContext->args; ShapeKey::Builder defaultKeyBuilder; @@ -219,62 +220,62 @@ void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, con batch.setViewTransform(viewMat); // Draw stencil mask with object bounding boxes - const auto outlineWidthLoc = _stencilMaskPipeline->getProgram()->getUniforms().findLocation("outlineWidth"); + const auto highlightWidthLoc = _stencilMaskPipeline->getProgram()->getUniforms().findLocation("outlineWidth"); const auto sqrt3 = 1.74f; - const float blurPixelWidth = 2.0f * sqrt3 * OutlineSharedParameters::getBlurPixelWidth(outline._style, args->_viewport.w); + const float blurPixelWidth = 2.0f * sqrt3 * HighlightSharedParameters::getBlurPixelWidth(highlight._style, args->_viewport.w); const auto framebufferSize = ressources->getSourceFrameSize(); - auto stencilPipeline = outline._style.isFilled() ? _stencilMaskFillPipeline : _stencilMaskPipeline; + auto stencilPipeline = highlight._style.isFilled() ? _stencilMaskFillPipeline : _stencilMaskPipeline; batch.setPipeline(stencilPipeline); batch.setResourceBuffer(0, _boundsBuffer); - batch._glUniform2f(outlineWidthLoc, blurPixelWidth / framebufferSize.x, blurPixelWidth / framebufferSize.y); + batch._glUniform2f(highlightWidthLoc, blurPixelWidth / framebufferSize.x, blurPixelWidth / framebufferSize.y); static const int NUM_VERTICES_PER_CUBE = 36; batch.draw(gpu::TRIANGLES, NUM_VERTICES_PER_CUBE * (gpu::uint32) itemBounds.size(), 0); }); } else { - // Outline rect should be null as there are no outlined shapes + // Highlight rect should be null as there are no highlighted shapes outputs = glm::ivec4(0, 0, 0, 0); } } -gpu::PipelinePointer DrawOutline::_pipeline; -gpu::PipelinePointer DrawOutline::_pipelineFilled; +gpu::PipelinePointer DrawHighlight::_pipeline; +gpu::PipelinePointer DrawHighlight::_pipelineFilled; -DrawOutline::DrawOutline(unsigned int outlineIndex, OutlineSharedParametersPointer parameters) : - _outlineIndex{ outlineIndex }, +DrawHighlight::DrawHighlight(unsigned int highlightIndex, HighlightSharedParametersPointer parameters) : + _highlightIndex{ highlightIndex }, _sharedParameters{ parameters } { } -void DrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { - auto outlineFrameBuffer = inputs.get1(); - auto outlineRect = inputs.get3(); +void DrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { + auto highlightFrameBuffer = inputs.get1(); + auto highlightRect = inputs.get3(); - if (outlineFrameBuffer && outlineRect.z>0 && outlineRect.w>0) { + if (highlightFrameBuffer && highlightRect.z>0 && highlightRect.w>0) { auto sceneDepthBuffer = inputs.get2(); const auto frameTransform = inputs.get0(); - auto outlinedDepthTexture = outlineFrameBuffer->getDepthTexture(); - auto destinationFrameBuffer = outlineFrameBuffer->getColorFramebuffer(); - auto framebufferSize = glm::ivec2(outlinedDepthTexture->getDimensions()); + auto highlightedDepthTexture = highlightFrameBuffer->getDepthTexture(); + auto destinationFrameBuffer = highlightFrameBuffer->getColorFramebuffer(); + auto framebufferSize = glm::ivec2(highlightedDepthTexture->getDimensions()); if (sceneDepthBuffer) { auto args = renderContext->args; - auto outlineStage = renderContext->_scene->getStage(render::OutlineStyleStage::getName()); - auto outlineId = _sharedParameters->_outlineIds[_outlineIndex]; - if (!render::OutlineStyleStage::isIndexInvalid(outlineId)) { - auto& outline = outlineStage->getOutline(_sharedParameters->_outlineIds[_outlineIndex]); - auto pipeline = getPipeline(outline._style); + auto highlightStage = renderContext->_scene->getStage(render::HighlightStage::getName()); + auto highlightId = _sharedParameters->_highlightIds[_highlightIndex]; + if (!render::HighlightStage::isIndexInvalid(highlightId)) { + auto& highlight = highlightStage->getHighlight(_sharedParameters->_highlightIds[_highlightIndex]); + auto pipeline = getPipeline(highlight._style); { auto& shaderParameters = _configuration.edit(); - shaderParameters._color = outline._style.color; - shaderParameters._intensity = outline._style.intensity * (outline._style.glow ? 2.0f : 1.0f); - shaderParameters._unoccludedFillOpacity = outline._style.unoccludedFillOpacity; - shaderParameters._occludedFillOpacity = outline._style.occludedFillOpacity; - shaderParameters._threshold = outline._style.glow ? 1.0f : 1e-3f; - shaderParameters._blurKernelSize = std::min(7, std::max(2, (int)floorf(outline._style.width * 3 + 0.5f))); - // Size is in normalized screen height. We decide that for outline width = 1, this is equal to 1/400. - auto size = outline._style.width / 400.0f; + shaderParameters._color = highlight._style.color; + shaderParameters._intensity = highlight._style.outlineIntensity * (highlight._style.isOutlineSmooth ? 2.0f : 1.0f); + shaderParameters._unoccludedFillOpacity = highlight._style.unoccludedFillOpacity; + shaderParameters._occludedFillOpacity = highlight._style.occludedFillOpacity; + shaderParameters._threshold = highlight._style.isOutlineSmooth ? 1.0f : 1e-3f; + shaderParameters._blurKernelSize = std::min(7, std::max(2, (int)floorf(highlight._style.outlineWidth * 3 + 0.5f))); + // Size is in normalized screen height. We decide that for highlight width = 1, this is equal to 1/400. + auto size = highlight._style.outlineWidth / 400.0f; shaderParameters._size.x = (size * framebufferSize.y) / framebufferSize.x; shaderParameters._size.y = size; } @@ -289,10 +290,10 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport)); batch.setPipeline(pipeline); - batch.setUniformBuffer(OUTLINE_PARAMS_SLOT, _configuration); + batch.setUniformBuffer(HIGHLIGHT_PARAMS_SLOT, _configuration); batch.setUniformBuffer(FRAME_TRANSFORM_SLOT, frameTransform->getFrameTransformBuffer()); - batch.setResourceTexture(SCENE_DEPTH_SLOT, sceneDepthBuffer->getPrimaryDepthTexture()); - batch.setResourceTexture(OUTLINED_DEPTH_SLOT, outlinedDepthTexture); + batch.setResourceTexture(SCENE_DEPTH_MAP_SLOT, sceneDepthBuffer->getPrimaryDepthTexture()); + batch.setResourceTexture(HIGHLIGHTED_DEPTH_MAP_SLOT, highlightedDepthTexture); batch.draw(gpu::TRIANGLE_STRIP, 4); }); } @@ -300,7 +301,7 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I } } -const gpu::PipelinePointer& DrawOutline::getPipeline(const render::OutlineStyle& style) { +const gpu::PipelinePointer& DrawHighlight::getPipeline(const render::HighlightStyle& style) { if (!_pipeline) { gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setDepthTest(gpu::State::DepthTest(false, false)); @@ -308,19 +309,19 @@ const gpu::PipelinePointer& DrawOutline::getPipeline(const render::OutlineStyle& state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL)); auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(Outline_frag)); + auto ps = gpu::Shader::createPixel(std::string(Highlight_frag)); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding("outlineParamsBuffer", OUTLINE_PARAMS_SLOT)); + slotBindings.insert(gpu::Shader::Binding("highlightParamsBuffer", HIGHLIGHT_PARAMS_SLOT)); slotBindings.insert(gpu::Shader::Binding("deferredFrameTransformBuffer", FRAME_TRANSFORM_SLOT)); - slotBindings.insert(gpu::Shader::Binding("sceneDepthMap", SCENE_DEPTH_SLOT)); - slotBindings.insert(gpu::Shader::Binding("outlinedDepthMap", OUTLINED_DEPTH_SLOT)); + slotBindings.insert(gpu::Shader::Binding("sceneDepthMap", SCENE_DEPTH_MAP_SLOT)); + slotBindings.insert(gpu::Shader::Binding("highlightedDepthMap", HIGHLIGHTED_DEPTH_MAP_SLOT)); gpu::Shader::makeProgram(*program, slotBindings); _pipeline = gpu::Pipeline::create(program, state); - ps = gpu::Shader::createPixel(std::string(Outline_filled_frag)); + ps = gpu::Shader::createPixel(std::string(Highlight_filled_frag)); program = gpu::Shader::createProgram(vs, ps); gpu::Shader::makeProgram(*program, slotBindings); _pipelineFilled = gpu::Pipeline::create(program, state); @@ -328,33 +329,33 @@ const gpu::PipelinePointer& DrawOutline::getPipeline(const render::OutlineStyle& return style.isFilled() ? _pipelineFilled : _pipeline; } -DebugOutline::DebugOutline() { +DebugHighlight::DebugHighlight() { _geometryDepthId = DependencyManager::get()->allocateID(); } -DebugOutline::~DebugOutline() { +DebugHighlight::~DebugHighlight() { auto geometryCache = DependencyManager::get(); if (geometryCache) { geometryCache->releaseID(_geometryDepthId); } } -void DebugOutline::configure(const Config& config) { +void DebugHighlight::configure(const Config& config) { _isDisplayEnabled = config.viewMask; } -void DebugOutline::run(const render::RenderContextPointer& renderContext, const Inputs& input) { - const auto outlineRessources = input.get0(); - const auto outlineRect = input.get1(); +void DebugHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& input) { + const auto highlightRessources = input.get0(); + const auto highlightRect = input.get1(); - if (_isDisplayEnabled && outlineRessources && outlineRect.z>0 && outlineRect.w>0) { + if (_isDisplayEnabled && highlightRessources && highlightRect.z>0 && highlightRect.w>0) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); RenderArgs* args = renderContext->args; gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); - batch.setFramebuffer(outlineRessources->getColorFramebuffer()); + batch.setFramebuffer(highlightRessources->getColorFramebuffer()); const auto geometryBuffer = DependencyManager::get(); @@ -369,7 +370,7 @@ void DebugOutline::run(const render::RenderContextPointer& renderContext, const const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); batch.setPipeline(getDepthPipeline()); - batch.setResourceTexture(0, outlineRessources->getDepthTexture()); + batch.setResourceTexture(0, highlightRessources->getDepthTexture()); const glm::vec2 bottomLeft(-1.0f, -1.0f); const glm::vec2 topRight(1.0f, 1.0f); geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryDepthId); @@ -379,7 +380,7 @@ void DebugOutline::run(const render::RenderContextPointer& renderContext, const } } -void DebugOutline::initializePipelines() { +void DebugHighlight::initializePipelines() { static const std::string VERTEX_SHADER{ debug_deferred_buffer_vert }; static const std::string FRAGMENT_SHADER{ debug_deferred_buffer_frag }; static const std::string SOURCE_PLACEHOLDER{ "//SOURCE_PLACEHOLDER" }; @@ -417,7 +418,7 @@ void DebugOutline::initializePipelines() { } } -const gpu::PipelinePointer& DebugOutline::getDepthPipeline() { +const gpu::PipelinePointer& DebugHighlight::getDepthPipeline() { if (!_depthPipeline) { initializePipelines(); } @@ -425,44 +426,44 @@ const gpu::PipelinePointer& DebugOutline::getDepthPipeline() { return _depthPipeline; } -void SelectionToOutline::run(const render::RenderContextPointer& renderContext, Outputs& outputs) { - auto outlineStage = renderContext->_scene->getStage(render::OutlineStyleStage::getName()); +void SelectionToHighlight::run(const render::RenderContextPointer& renderContext, Outputs& outputs) { + auto highlightStage = renderContext->_scene->getStage(render::HighlightStage::getName()); outputs.clear(); - _sharedParameters->_outlineIds.fill(render::OutlineStyleStage::INVALID_INDEX); + _sharedParameters->_highlightIds.fill(render::HighlightStage::INVALID_INDEX); - for (auto i = 0; i < OutlineSharedParameters::MAX_OUTLINE_COUNT; i++) { + for (auto i = 0; i < HighlightSharedParameters::MAX_HIGHLIGHT_COUNT; i++) { std::ostringstream stream; stream << "contextOverlayHighlightList"; if (i > 0) { stream << i; } auto selectionName = stream.str(); - auto outlineId = outlineStage->getOutlineIdBySelection(selectionName); - if (!render::OutlineStyleStage::isIndexInvalid(outlineId)) { - _sharedParameters->_outlineIds[outputs.size()] = outlineId; + auto highlightId = highlightStage->getHighlightIdBySelection(selectionName); + if (!render::HighlightStage::isIndexInvalid(highlightId)) { + _sharedParameters->_highlightIds[outputs.size()] = highlightId; outputs.emplace_back(selectionName); } } } void ExtractSelectionName::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { - if (_outlineIndex < inputs.size()) { - outputs = inputs[_outlineIndex]; + if (_highlightIndex < inputs.size()) { + outputs = inputs[_highlightIndex]; } else { outputs.clear(); } } -DrawOutlineTask::DrawOutlineTask() { +DrawHighlightTask::DrawHighlightTask() { } -void DrawOutlineTask::configure(const Config& config) { +void DrawHighlightTask::configure(const Config& config) { } -void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { +void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { const auto items = inputs.getN(0).get(); const auto sceneFrameBuffer = inputs.getN(1); const auto primaryFramebuffer = inputs.getN(2); @@ -477,53 +478,53 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende initMaskPipelines(*shapePlumber, state); } - auto sharedParameters = std::make_shared(); + auto sharedParameters = std::make_shared(); - const auto outlineSelectionNames = task.addJob("SelectionToOutline", sharedParameters); + const auto highlightSelectionNames = task.addJob("SelectionToHighlight", sharedParameters); - // Prepare for outline group rendering. - const auto outlineRessources = task.addJob("PrepareOutline", primaryFramebuffer); - render::Varying outline0Rect; + // Prepare for highlight group rendering. + const auto highlightRessources = task.addJob("PrepareHighlight", primaryFramebuffer); + render::Varying highlight0Rect; - for (auto i = 0; i < OutlineSharedParameters::MAX_OUTLINE_COUNT; i++) { - const auto selectionName = task.addJob("ExtractSelectionName", outlineSelectionNames, i); + for (auto i = 0; i < HighlightSharedParameters::MAX_HIGHLIGHT_COUNT; i++) { + const auto selectionName = task.addJob("ExtractSelectionName", highlightSelectionNames, i); const auto groupItems = addSelectItemJobs(task, selectionName, items); - const auto outlinedItemIDs = task.addJob("OutlineMetaToSubItemIDs", groupItems); - const auto outlinedItems = task.addJob("OutlineMetaToSubItems", outlinedItemIDs); + const auto highlightedItemIDs = task.addJob("HighlightMetaToSubItemIDs", groupItems); + const auto highlightedItems = task.addJob("HighlightMetaToSubItems", highlightedItemIDs); // Sort - const auto sortedPipelines = task.addJob("OutlinePipelineSort", outlinedItems); - const auto sortedBounds = task.addJob("OutlineDepthSort", sortedPipelines); + const auto sortedPipelines = task.addJob("HighlightPipelineSort", highlightedItems); + const auto sortedBounds = task.addJob("HighlightDepthSort", sortedPipelines); - // Draw depth of outlined objects in separate buffer + // Draw depth of highlighted objects in separate buffer std::string name; { std::ostringstream stream; - stream << "OutlineMask" << i; + stream << "HighlightMask" << i; name = stream.str(); } - const auto drawMaskInputs = DrawOutlineMask::Inputs(sortedBounds, outlineRessources).asVarying(); - const auto outlinedRect = task.addJob(name, drawMaskInputs, i, shapePlumber, sharedParameters); + const auto drawMaskInputs = DrawHighlightMask::Inputs(sortedBounds, highlightRessources).asVarying(); + const auto highlightedRect = task.addJob(name, drawMaskInputs, i, shapePlumber, sharedParameters); if (i == 0) { - outline0Rect = outlinedRect; + highlight0Rect = highlightedRect; } - // Draw outline + // Draw highlight { std::ostringstream stream; - stream << "OutlineEffect" << i; + stream << "HighlightEffect" << i; name = stream.str(); } - const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlineRessources, sceneFrameBuffer, outlinedRect).asVarying(); - task.addJob(name, drawOutlineInputs, i, sharedParameters); + const auto drawHighlightInputs = DrawHighlight::Inputs(deferredFrameTransform, highlightRessources, sceneFrameBuffer, highlightedRect).asVarying(); + task.addJob(name, drawHighlightInputs, i, sharedParameters); } - // Debug outline - const auto debugInputs = DebugOutline::Inputs(outlineRessources, const_cast(outline0Rect)).asVarying(); - task.addJob("OutlineDebug", debugInputs); + // Debug highlight + const auto debugInputs = DebugHighlight::Inputs(highlightRessources, const_cast(highlight0Rect)).asVarying(); + task.addJob("HighlightDebug", debugInputs); } -const render::Varying DrawOutlineTask::addSelectItemJobs(JobModel& task, const render::Varying& selectionName, +const render::Varying DrawHighlightTask::addSelectItemJobs(JobModel& task, const render::Varying& selectionName, const RenderFetchCullSortTask::BucketList& items) { const auto& opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE]; const auto& transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; @@ -542,7 +543,7 @@ const render::Varying DrawOutlineTask::addSelectItemJobs(JobModel& task, const r #include "model_shadow_frag.h" -void DrawOutlineTask::initMaskPipelines(render::ShapePlumber& shapePlumber, gpu::StatePointer state) { +void DrawHighlightTask::initMaskPipelines(render::ShapePlumber& shapePlumber, gpu::StatePointer state) { auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); auto modelPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(modelVertex, modelPixel); diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/HighlightEffect.h similarity index 55% rename from libraries/render-utils/src/OutlineEffect.h rename to libraries/render-utils/src/HighlightEffect.h index e25184eb5b..0531c03efd 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/HighlightEffect.h @@ -1,5 +1,5 @@ // -// OutlineEffect.h +// HighlightEffect.h // render-utils/src/ // // Created by Olivier Prat on 08/08/17. @@ -9,19 +9,19 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_render_utils_OutlineEffect_h -#define hifi_render_utils_OutlineEffect_h +#ifndef hifi_render_utils_HighlightEffect_h +#define hifi_render_utils_HighlightEffect_h #include -#include +#include #include #include "DeferredFramebuffer.h" #include "DeferredFrameTransform.h" -class OutlineRessources { +class HighlightRessources { public: - OutlineRessources(); + HighlightRessources(); gpu::FramebufferPointer getDepthFramebuffer(); gpu::TexturePointer getDepthTexture(); @@ -44,131 +44,131 @@ protected: void allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer); }; -using OutlineRessourcesPointer = std::shared_ptr; +using HighlightRessourcesPointer = std::shared_ptr; -class OutlineSharedParameters { +class HighlightSharedParameters { public: enum { - MAX_OUTLINE_COUNT = 8 + MAX_HIGHLIGHT_COUNT = 8 }; - OutlineSharedParameters(); + HighlightSharedParameters(); - std::array _outlineIds; + std::array _highlightIds; - static float getBlurPixelWidth(const render::OutlineStyle& style, int frameBufferHeight); + static float getBlurPixelWidth(const render::HighlightStyle& style, int frameBufferHeight); }; -using OutlineSharedParametersPointer = std::shared_ptr; +using HighlightSharedParametersPointer = std::shared_ptr; -class PrepareDrawOutline { +class PrepareDrawHighlight { public: using Inputs = gpu::FramebufferPointer; - using Outputs = OutlineRessourcesPointer; - using JobModel = render::Job::ModelIO; + using Outputs = HighlightRessourcesPointer; + using JobModel = render::Job::ModelIO; - PrepareDrawOutline(); + PrepareDrawHighlight(); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); private: - OutlineRessourcesPointer _ressources; + HighlightRessourcesPointer _ressources; }; -class SelectionToOutline { +class SelectionToHighlight { public: using Outputs = std::vector; - using JobModel = render::Job::ModelO; + using JobModel = render::Job::ModelO; - SelectionToOutline(OutlineSharedParametersPointer parameters) : _sharedParameters{ parameters } {} + SelectionToHighlight(HighlightSharedParametersPointer parameters) : _sharedParameters{ parameters } {} void run(const render::RenderContextPointer& renderContext, Outputs& outputs); private: - OutlineSharedParametersPointer _sharedParameters; + HighlightSharedParametersPointer _sharedParameters; }; class ExtractSelectionName { public: - using Inputs = SelectionToOutline::Outputs; + using Inputs = SelectionToHighlight::Outputs; using Outputs = std::string; using JobModel = render::Job::ModelIO; - ExtractSelectionName(unsigned int outlineIndex) : _outlineIndex{ outlineIndex } {} + ExtractSelectionName(unsigned int highlightIndex) : _highlightIndex{ highlightIndex } {} void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); private: - unsigned int _outlineIndex; + unsigned int _highlightIndex; }; -class DrawOutlineMask { +class DrawHighlightMask { public: - using Inputs = render::VaryingSet2; + using Inputs = render::VaryingSet2; using Outputs = glm::ivec4; - using JobModel = render::Job::ModelIO; + using JobModel = render::Job::ModelIO; - DrawOutlineMask(unsigned int outlineIndex, render::ShapePlumberPointer shapePlumber, OutlineSharedParametersPointer parameters); + DrawHighlightMask(unsigned int highlightIndex, render::ShapePlumberPointer shapePlumber, HighlightSharedParametersPointer parameters); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); protected: - unsigned int _outlineIndex; + unsigned int _highlightIndex; render::ShapePlumberPointer _shapePlumber; - OutlineSharedParametersPointer _sharedParameters; + HighlightSharedParametersPointer _sharedParameters; static gpu::BufferPointer _boundsBuffer; static gpu::PipelinePointer _stencilMaskPipeline; static gpu::PipelinePointer _stencilMaskFillPipeline; }; -class DrawOutline { +class DrawHighlight { public: - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet4; using Config = render::Job::Config; - using JobModel = render::Job::ModelI; + using JobModel = render::Job::ModelI; - DrawOutline(unsigned int outlineIndex, OutlineSharedParametersPointer parameters); + DrawHighlight(unsigned int highlightIndex, HighlightSharedParametersPointer parameters); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); private: -#include "Outline_shared.slh" +#include "Highlight_shared.slh" enum { - SCENE_DEPTH_SLOT = 0, - OUTLINED_DEPTH_SLOT, + SCENE_DEPTH_MAP_SLOT = 0, + HIGHLIGHTED_DEPTH_MAP_SLOT, - OUTLINE_PARAMS_SLOT = 0, + HIGHLIGHT_PARAMS_SLOT = 0, FRAME_TRANSFORM_SLOT, }; - using OutlineConfigurationBuffer = gpu::StructBuffer; + using HighlightConfigurationBuffer = gpu::StructBuffer; - static const gpu::PipelinePointer& getPipeline(const render::OutlineStyle& style); + static const gpu::PipelinePointer& getPipeline(const render::HighlightStyle& style); static gpu::PipelinePointer _pipeline; static gpu::PipelinePointer _pipelineFilled; - unsigned int _outlineIndex; - OutlineParameters _parameters; - OutlineSharedParametersPointer _sharedParameters; - OutlineConfigurationBuffer _configuration; + unsigned int _highlightIndex; + HighlightParameters _parameters; + HighlightSharedParametersPointer _sharedParameters; + HighlightConfigurationBuffer _configuration; }; -class DebugOutlineConfig : public render::Job::Config { +class DebugHighlightConfig : public render::Job::Config { Q_OBJECT Q_PROPERTY(bool viewMask MEMBER viewMask NOTIFY dirty) @@ -180,14 +180,14 @@ signals: void dirty(); }; -class DebugOutline { +class DebugHighlight { public: - using Inputs = render::VaryingSet2; - using Config = DebugOutlineConfig; - using JobModel = render::Job::ModelI; + using Inputs = render::VaryingSet2; + using Config = DebugHighlightConfig; + using JobModel = render::Job::ModelI; - DebugOutline(); - ~DebugOutline(); + DebugHighlight(); + ~DebugHighlight(); void configure(const Config& config); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); @@ -202,14 +202,14 @@ private: void initializePipelines(); }; -class DrawOutlineTask { +class DrawHighlightTask { public: using Inputs = render::VaryingSet4; using Config = render::Task::Config; - using JobModel = render::Task::ModelI; + using JobModel = render::Task::ModelI; - DrawOutlineTask(); + DrawHighlightTask(); void configure(const Config& config); void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); @@ -221,6 +221,6 @@ private: }; -#endif // hifi_render_utils_OutlineEffect_h +#endif // hifi_render_utils_HighlightEffect_h diff --git a/libraries/render-utils/src/Outline_aabox.slv b/libraries/render-utils/src/Highlight_aabox.slv similarity index 100% rename from libraries/render-utils/src/Outline_aabox.slv rename to libraries/render-utils/src/Highlight_aabox.slv diff --git a/libraries/render-utils/src/Outline_filled.slf b/libraries/render-utils/src/Highlight_filled.slf similarity index 71% rename from libraries/render-utils/src/Outline_filled.slf rename to libraries/render-utils/src/Highlight_filled.slf index aaa3396bac..53530746f0 100644 --- a/libraries/render-utils/src/Outline_filled.slf +++ b/libraries/render-utils/src/Highlight_filled.slf @@ -1,5 +1,5 @@ -// Outline_filled.slf -// Add outline effect based on two zbuffers : one containing the total scene z and another +// Highlight_filled.slf +// Add highlight effect based on two zbuffers : one containing the total scene z and another // with the z of only the objects to be outlined. // This is the version with the fill effect inside the silhouette. // @@ -9,5 +9,5 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include Outline.slh@> +<@include Highlight.slh@> <$main(1)$> diff --git a/libraries/render-utils/src/Outline_shared.slh b/libraries/render-utils/src/Highlight_shared.slh similarity index 84% rename from libraries/render-utils/src/Outline_shared.slh rename to libraries/render-utils/src/Highlight_shared.slh index 3fd089e2fc..5efbde4d52 100644 --- a/libraries/render-utils/src/Outline_shared.slh +++ b/libraries/render-utils/src/Highlight_shared.slh @@ -1,4 +1,4 @@ -// glsl / C++ compatible source as interface for Outline +// glsl / C++ compatible source as interface for highlight #ifdef __cplusplus # define TVEC2 glm::vec2 # define TVEC3 glm::vec3 @@ -9,7 +9,7 @@ # define TVEC4 vec4 #endif -struct OutlineParameters +struct HighlightParameters { TVEC3 _color; float _intensity; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 52b8896e69..1f839b25eb 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -42,7 +42,7 @@ #include "ToneMappingEffect.h" #include "SubsurfaceScattering.h" #include "DrawHaze.h" -#include "OutlineEffect.h" +#include "HighlightEffect.h" #include @@ -183,15 +183,15 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying(); task.addJob("ToneMapping", toneMappingInputs); - const auto outlineRangeTimer = task.addJob("BeginOutlineRangeTimer", "Outline"); + const auto outlineRangeTimer = task.addJob("BeginHighlightRangeTimer", "Highlight"); // Select items that need to be outlined const auto selectionBaseName = "contextOverlayHighlightList"; const auto selectedItems = addSelectItemJobs(task, selectionBaseName, metas, opaques, transparents); - const auto outlineInputs = DrawOutlineTask::Inputs(items.get0(), deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying(); - task.addJob("DrawOutline", outlineInputs); + const auto outlineInputs = DrawHighlightTask::Inputs(items.get0(), deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying(); + task.addJob("DrawHighlight", outlineInputs); - task.addJob("OutlineRangeTimer", outlineRangeTimer); + task.addJob("HighlightRangeTimer", outlineRangeTimer); { // DEbug the bounds of the rendered items, still look at the zbuffer task.addJob("DrawMetaBounds", metas); diff --git a/libraries/render-utils/src/UpdateSceneTask.cpp b/libraries/render-utils/src/UpdateSceneTask.cpp index d37569b526..e05f28ef0d 100644 --- a/libraries/render-utils/src/UpdateSceneTask.cpp +++ b/libraries/render-utils/src/UpdateSceneTask.cpp @@ -15,7 +15,7 @@ #include "BackgroundStage.h" #include "HazeStage.h" #include -#include +#include #include "DeferredLightingEffect.h" void UpdateSceneTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { @@ -23,7 +23,7 @@ void UpdateSceneTask::build(JobModel& task, const render::Varying& input, render task.addJob("BackgroundStageSetup"); task.addJob("HazeStageSetup"); task.addJob("TransitionStageSetup"); - task.addJob("OutlineStyleStageSetup"); + task.addJob("HighlightStageSetup"); task.addJob("DefaultLightingSetup"); diff --git a/libraries/render/src/render/HighlightStage.cpp b/libraries/render/src/render/HighlightStage.cpp new file mode 100644 index 0000000000..effb7c7e98 --- /dev/null +++ b/libraries/render/src/render/HighlightStage.cpp @@ -0,0 +1,46 @@ +#include "HighlightStage.h" + +using namespace render; + +std::string HighlightStage::_name("Highlight"); + +HighlightStage::Index HighlightStage::addHighlight(const std::string& selectionName, const HighlightStyle& style) { + Highlight outline{ selectionName, style }; + Index id; + + id = _highlights.newElement(outline); + _activeHighlightIds.push_back(id); + + return id; +} + +void HighlightStage::removeHighlight(Index index) { + HighlightIdList::iterator idIterator = std::find(_activeHighlightIds.begin(), _activeHighlightIds.end(), index); + if (idIterator != _activeHighlightIds.end()) { + _activeHighlightIds.erase(idIterator); + } + if (!_highlights.isElementFreed(index)) { + _highlights.freeElement(index); + } +} + +Index HighlightStage::getHighlightIdBySelection(const std::string& selectionName) const { + for (auto outlineId : _activeHighlightIds) { + const auto& outline = _highlights.get(outlineId); + if (outline._selectionName == selectionName) { + return outlineId; + } + } + return INVALID_INDEX; +} + +HighlightStageSetup::HighlightStageSetup() { +} + +void HighlightStageSetup::run(const render::RenderContextPointer& renderContext) { + if (!renderContext->_scene->getStage(HighlightStage::getName())) { + auto stage = std::make_shared(); + renderContext->_scene->resetStage(HighlightStage::getName(), stage); + } +} + diff --git a/libraries/render/src/render/HighlightStage.h b/libraries/render/src/render/HighlightStage.h new file mode 100644 index 0000000000..a2c123580c --- /dev/null +++ b/libraries/render/src/render/HighlightStage.h @@ -0,0 +1,77 @@ +// +// HighlightStage.h + +// Created by Olivier Prat on 07/07/2017. +// Copyright 2017 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_render_utils_HighlightStage_h +#define hifi_render_utils_HighlightStage_h + +#include "Stage.h" +#include "Engine.h" +#include "IndexedContainer.h" +#include "HighlightStyle.h" + +namespace render { + + // Highlight stage to set up HighlightStyle-related effects + class HighlightStage : public Stage { + public: + + class Highlight { + public: + + Highlight(const std::string& selectionName, const HighlightStyle& style) : _selectionName{ selectionName }, _style{ style } { } + + std::string _selectionName; + HighlightStyle _style; + + }; + + static const std::string& getName() { return _name; } + + using Index = render::indexed_container::Index; + static const Index INVALID_INDEX{ render::indexed_container::INVALID_INDEX }; + using HighlightIdList = render::indexed_container::Indices; + + static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } + + bool checkHighlightId(Index index) const { return _highlights.checkIndex(index); } + + const Highlight& getHighlight(Index index) const { return _highlights.get(index); } + Highlight& editHighlight(Index index) { return _highlights.edit(index); } + + Index addHighlight(const std::string& selectionName, const HighlightStyle& style = HighlightStyle()); + Index getHighlightIdBySelection(const std::string& selectionName) const; + void removeHighlight(Index index); + + HighlightIdList::iterator begin() { return _activeHighlightIds.begin(); } + HighlightIdList::iterator end() { return _activeHighlightIds.end(); } + + private: + + using Highlights = render::indexed_container::IndexedVector; + + static std::string _name; + + Highlights _highlights; + HighlightIdList _activeHighlightIds; + }; + using HighlightStagePointer = std::shared_ptr; + + class HighlightStageSetup { + public: + using JobModel = render::Job::Model; + + HighlightStageSetup(); + void run(const RenderContextPointer& renderContext); + + protected: + }; + +} +#endif // hifi_render_utils_HighlightStage_h diff --git a/libraries/render/src/render/OutlineStyle.h b/libraries/render/src/render/HighlightStyle.h similarity index 68% rename from libraries/render/src/render/OutlineStyle.h rename to libraries/render/src/render/HighlightStyle.h index cdcdf79f3e..6e7373c78b 100644 --- a/libraries/render/src/render/OutlineStyle.h +++ b/libraries/render/src/render/HighlightStyle.h @@ -1,5 +1,5 @@ // -// OutlineStyle.h +// HighlightStyle.h // Created by Olivier Prat on 11/06/2017. // Copyright 2017 High Fidelity, Inc. @@ -8,8 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_render_utils_OutlineStyle_h -#define hifi_render_utils_OutlineStyle_h +#ifndef hifi_render_utils_HighlightStyle_h +#define hifi_render_utils_HighlightStyle_h #include @@ -18,7 +18,7 @@ namespace render { // This holds the configuration for a particular outline style - class OutlineStyle { + class HighlightStyle { public: bool isFilled() const { @@ -26,14 +26,13 @@ namespace render { } glm::vec3 color{ 1.f, 0.7f, 0.2f }; - float width{ 2.0f }; - float intensity{ 0.9f }; + float outlineWidth{ 2.0f }; + float outlineIntensity{ 0.9f }; float unoccludedFillOpacity{ 0.0f }; float occludedFillOpacity{ 0.0f }; - bool glow{ false }; - std::string selectionName; + bool isOutlineSmooth{ false }; }; } -#endif // hifi_render_utils_OutlineStyle_h \ No newline at end of file +#endif // hifi_render_utils_HighlightStyle_h \ No newline at end of file diff --git a/libraries/render/src/render/OutlineStyleStage.cpp b/libraries/render/src/render/OutlineStyleStage.cpp deleted file mode 100644 index e3fd1672de..0000000000 --- a/libraries/render/src/render/OutlineStyleStage.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "OutlineStyleStage.h" - -using namespace render; - -std::string OutlineStyleStage::_name("OutlineStyle"); - -OutlineStyleStage::Index OutlineStyleStage::addOutline(const std::string& selectionName, const OutlineStyle& style) { - Outline outline{ selectionName, style }; - Index id; - - id = _outlines.newElement(outline); - _activeOutlineIds.push_back(id); - - return id; -} - -void OutlineStyleStage::removeOutline(Index index) { - OutlineIdList::iterator idIterator = std::find(_activeOutlineIds.begin(), _activeOutlineIds.end(), index); - if (idIterator != _activeOutlineIds.end()) { - _activeOutlineIds.erase(idIterator); - } - if (!_outlines.isElementFreed(index)) { - _outlines.freeElement(index); - } -} - -Index OutlineStyleStage::getOutlineIdBySelection(const std::string& selectionName) const { - for (auto outlineId : _activeOutlineIds) { - const auto& outline = _outlines.get(outlineId); - if (outline._selectionName == selectionName) { - return outlineId; - } - } - return INVALID_INDEX; -} - -OutlineStyleStageSetup::OutlineStyleStageSetup() { -} - -void OutlineStyleStageSetup::run(const render::RenderContextPointer& renderContext) { - if (!renderContext->_scene->getStage(OutlineStyleStage::getName())) { - auto stage = std::make_shared(); - renderContext->_scene->resetStage(OutlineStyleStage::getName(), stage); - } -} - diff --git a/libraries/render/src/render/OutlineStyleStage.h b/libraries/render/src/render/OutlineStyleStage.h deleted file mode 100644 index 79835a22a4..0000000000 --- a/libraries/render/src/render/OutlineStyleStage.h +++ /dev/null @@ -1,77 +0,0 @@ -// -// OutlineStyleStage.h - -// Created by Olivier Prat on 07/07/2017. -// Copyright 2017 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_render_utils_outlinestage_h -#define hifi_render_utils_outlinestage_h - -#include "Stage.h" -#include "Engine.h" -#include "IndexedContainer.h" -#include "OutlineStyle.h" - -namespace render { - - // OutlineStyle stage to set up OutlineStyle-related effects - class OutlineStyleStage : public Stage { - public: - - class Outline { - public: - - Outline(const std::string& selectionName, const OutlineStyle& style) : _selectionName{ selectionName }, _style{ style } { } - - std::string _selectionName; - OutlineStyle _style; - - }; - - static const std::string& getName() { return _name; } - - using Index = render::indexed_container::Index; - static const Index INVALID_INDEX{ render::indexed_container::INVALID_INDEX }; - using OutlineIdList = render::indexed_container::Indices; - - static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } - - bool checkOutlineId(Index index) const { return _outlines.checkIndex(index); } - - const Outline& getOutline(Index OutlineStyleId) const { return _outlines.get(OutlineStyleId); } - Outline& editOutline(Index OutlineStyleId) { return _outlines.edit(OutlineStyleId); } - - Index addOutline(const std::string& selectionName, const OutlineStyle& style = OutlineStyle()); - Index getOutlineIdBySelection(const std::string& selectionName) const; - void removeOutline(Index index); - - OutlineIdList::iterator begin() { return _activeOutlineIds.begin(); } - OutlineIdList::iterator end() { return _activeOutlineIds.end(); } - - private: - - using Outlines = render::indexed_container::IndexedVector; - - static std::string _name; - - Outlines _outlines; - OutlineIdList _activeOutlineIds; - }; - using OutlineStyleStagePointer = std::shared_ptr; - - class OutlineStyleStageSetup { - public: - using JobModel = render::Job::Model; - - OutlineStyleStageSetup(); - void run(const RenderContextPointer& renderContext); - - protected: - }; - -} -#endif // hifi_render_utils_outlinestage_h diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 0e680dfdaf..9cdaa89cf6 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -14,7 +14,7 @@ #include #include "Logging.h" #include "TransitionStage.h" -#include "OutlineStyleStage.h" +#include "HighlightStage.h" using namespace render; @@ -55,16 +55,16 @@ void Transaction::resetSelection(const Selection& selection) { _resetSelections.emplace_back(selection); } -void Transaction::resetSelectionOutline(const std::string& selectionName, const OutlineStyle& style) { - _outlineResets.emplace_back(OutlineReset{ selectionName, style }); +void Transaction::resetSelectionHighlight(const std::string& selectionName, const HighlightStyle& style) { + _highlightResets.emplace_back(HighlightReset{ selectionName, style }); } -void Transaction::removeOutlineFromSelection(const std::string& selectionName) { - _outlineRemoves.emplace_back(selectionName); +void Transaction::removeHighlightFromSelection(const std::string& selectionName) { + _highlightRemoves.emplace_back(selectionName); } -void Transaction::querySelectionOutline(const std::string& selectionName, SelectionOutlineQueryFunc func) { - _outlineQueries.emplace_back(OutlineQuery{ selectionName, func }); +void Transaction::querySelectionHighlight(const std::string& selectionName, SelectionHighlightQueryFunc func) { + _highlightQueries.emplace_back(HighlightQuery{ selectionName, func }); } void Transaction::merge(const Transaction& transaction) { @@ -75,9 +75,9 @@ void Transaction::merge(const Transaction& transaction) { _addedTransitions.insert(_addedTransitions.end(), transaction._addedTransitions.begin(), transaction._addedTransitions.end()); _queriedTransitions.insert(_queriedTransitions.end(), transaction._queriedTransitions.begin(), transaction._queriedTransitions.end()); _reAppliedTransitions.insert(_reAppliedTransitions.end(), transaction._reAppliedTransitions.begin(), transaction._reAppliedTransitions.end()); - _outlineResets.insert(_outlineResets.end(), transaction._outlineResets.begin(), transaction._outlineResets.end()); - _outlineRemoves.insert(_outlineRemoves.end(), transaction._outlineRemoves.begin(), transaction._outlineRemoves.end()); - _outlineQueries.insert(_outlineQueries.end(), transaction._outlineQueries.begin(), transaction._outlineQueries.end()); + _highlightResets.insert(_highlightResets.end(), transaction._highlightResets.begin(), transaction._highlightResets.end()); + _highlightRemoves.insert(_highlightRemoves.end(), transaction._highlightRemoves.begin(), transaction._highlightRemoves.end()); + _highlightQueries.insert(_highlightQueries.end(), transaction._highlightQueries.begin(), transaction._highlightQueries.end()); } @@ -193,9 +193,9 @@ void Scene::processTransactionFrame(const Transaction& transaction) { resetSelections(transaction._resetSelections); } - resetOutlines(transaction._outlineResets); - removeOutlines(transaction._outlineRemoves); - queryOutlines(transaction._outlineQueries); + resetHighlights(transaction._highlightResets); + removeHighlights(transaction._highlightRemoves); + queryHighlights(transaction._highlightQueries); } void Scene::resetItems(const Transaction::Resets& transactions) { @@ -336,44 +336,44 @@ void Scene::queryTransitionItems(const Transaction::TransitionQueries& transacti } } -void Scene::resetOutlines(const Transaction::OutlineResets& transactions) { - auto outlineStage = getStage(OutlineStyleStage::getName()); +void Scene::resetHighlights(const Transaction::HighlightResets& transactions) { + auto outlineStage = getStage(HighlightStage::getName()); for (auto& transaction : transactions) { const auto& selectionName = std::get<0>(transaction); const auto& newStyle = std::get<1>(transaction); - auto outlineId = outlineStage->getOutlineIdBySelection(selectionName); + auto outlineId = outlineStage->getHighlightIdBySelection(selectionName); - if (OutlineStyleStage::isIndexInvalid(outlineId)) { - outlineStage->addOutline(selectionName, newStyle); + if (HighlightStage::isIndexInvalid(outlineId)) { + outlineStage->addHighlight(selectionName, newStyle); } else { - outlineStage->editOutline(outlineId)._style = newStyle; + outlineStage->editHighlight(outlineId)._style = newStyle; } } } -void Scene::removeOutlines(const Transaction::OutlineRemoves& transactions) { - auto outlineStage = getStage(OutlineStyleStage::getName()); +void Scene::removeHighlights(const Transaction::HighlightRemoves& transactions) { + auto outlineStage = getStage(HighlightStage::getName()); for (auto& selectionName : transactions) { - auto outlineId = outlineStage->getOutlineIdBySelection(selectionName); + auto outlineId = outlineStage->getHighlightIdBySelection(selectionName); - if (!OutlineStyleStage::isIndexInvalid(outlineId)) { - outlineStage->removeOutline(outlineId); + if (!HighlightStage::isIndexInvalid(outlineId)) { + outlineStage->removeHighlight(outlineId); } } } -void Scene::queryOutlines(const Transaction::OutlineQueries& transactions) { - auto outlineStage = getStage(OutlineStyleStage::getName()); +void Scene::queryHighlights(const Transaction::HighlightQueries& transactions) { + auto outlineStage = getStage(HighlightStage::getName()); for (auto& transaction : transactions) { const auto& selectionName = std::get<0>(transaction); const auto& func = std::get<1>(transaction); - auto outlineId = outlineStage->getOutlineIdBySelection(selectionName); + auto outlineId = outlineStage->getHighlightIdBySelection(selectionName); - if (!OutlineStyleStage::isIndexInvalid(outlineId)) { - func(&outlineStage->editOutline(outlineId)._style); + if (!HighlightStage::isIndexInvalid(outlineId)) { + func(&outlineStage->editHighlight(outlineId)._style); } else { func(nullptr); } diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 38f528aced..4bf38b89cc 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -17,7 +17,7 @@ #include "Stage.h" #include "Selection.h" #include "Transition.h" -#include "OutlineStyle.h" +#include "HighlightStyle.h" namespace render { @@ -38,7 +38,7 @@ class Transaction { public: typedef std::function TransitionQueryFunc; - typedef std::function SelectionOutlineQueryFunc; + typedef std::function SelectionHighlightQueryFunc; Transaction() {} ~Transaction() {} @@ -63,9 +63,9 @@ public: // Selection transactions void resetSelection(const Selection& selection); - void resetSelectionOutline(const std::string& selectionName, const OutlineStyle& style = OutlineStyle()); - void removeOutlineFromSelection(const std::string& selectionName); - void querySelectionOutline(const std::string& selectionName, SelectionOutlineQueryFunc func); + void resetSelectionHighlight(const std::string& selectionName, const HighlightStyle& style = HighlightStyle()); + void removeHighlightFromSelection(const std::string& selectionName); + void querySelectionHighlight(const std::string& selectionName, SelectionHighlightQueryFunc func); void merge(const Transaction& transaction); @@ -81,9 +81,9 @@ protected: using TransitionQuery = std::tuple; using TransitionReApply = ItemID; using SelectionReset = Selection; - using OutlineReset = std::tuple; - using OutlineRemove = std::string; - using OutlineQuery = std::tuple; + using HighlightReset = std::tuple; + using HighlightRemove = std::string; + using HighlightQuery = std::tuple; using Resets = std::vector; using Removes = std::vector; @@ -92,9 +92,9 @@ protected: using TransitionQueries = std::vector; using TransitionReApplies = std::vector; using SelectionResets = std::vector; - using OutlineResets = std::vector; - using OutlineRemoves = std::vector; - using OutlineQueries = std::vector; + using HighlightResets = std::vector; + using HighlightRemoves = std::vector; + using HighlightQueries = std::vector; Resets _resetItems; Removes _removedItems; @@ -103,9 +103,9 @@ protected: TransitionQueries _queriedTransitions; TransitionReApplies _reAppliedTransitions; SelectionResets _resetSelections; - OutlineResets _outlineResets; - OutlineRemoves _outlineRemoves; - OutlineQueries _outlineQueries; + HighlightResets _highlightResets; + HighlightRemoves _highlightRemoves; + HighlightQueries _highlightQueries; }; typedef std::queue TransactionQueue; @@ -203,9 +203,9 @@ protected: void transitionItems(const Transaction::TransitionAdds& transactions); void reApplyTransitions(const Transaction::TransitionReApplies& transactions); void queryTransitionItems(const Transaction::TransitionQueries& transactions); - void resetOutlines(const Transaction::OutlineResets& transactions); - void removeOutlines(const Transaction::OutlineRemoves& transactions); - void queryOutlines(const Transaction::OutlineQueries& transactions); + void resetHighlights(const Transaction::HighlightResets& transactions); + void removeHighlights(const Transaction::HighlightRemoves& transactions); + void queryHighlights(const Transaction::HighlightQueries& transactions); void collectSubItems(ItemID parentId, ItemIDs& subItems) const; diff --git a/scripts/developer/utilities/render/debugOutline.js b/scripts/developer/utilities/render/debugHighlight.js similarity index 88% rename from scripts/developer/utilities/render/debugOutline.js rename to scripts/developer/utilities/render/debugHighlight.js index ce32d61e1b..5175761978 100644 --- a/scripts/developer/utilities/render/debugOutline.js +++ b/scripts/developer/utilities/render/debugHighlight.js @@ -1,5 +1,5 @@ // -// debugOutline.js +// debugHighlight.js // developer/utilities/render // // Olivier Prat, created on 08/08/2017. @@ -10,9 +10,9 @@ // // Set up the qml ui -var qml = Script.resolvePath('outline.qml'); +var qml = Script.resolvePath('highlight.qml'); var window = new OverlayWindow({ - title: 'Outline', + title: 'Highlight', source: qml, width: 400, height: 400, @@ -54,7 +54,7 @@ var end2 = { visible: true } -var outlineGroupIndex = 0 +var highlightGroupIndex = 0 var isSelectionAddEnabled = false var isSelectionEnabled = false var renderStates = [{name: "test", end: end}]; @@ -72,18 +72,18 @@ var ray = LaserPointers.createLaserPointer({ function getSelectionName() { var selectionName = "contextOverlayHighlightList" - if (outlineGroupIndex>0) { - selectionName += outlineGroupIndex + if (highlightGroupIndex>0) { + selectionName += highlightGroupIndex } return selectionName } function fromQml(message) { tokens = message.split(' ') - print("Received '"+message+"' from outline.qml") - if (tokens[0]=="outline") { - outlineGroupIndex = parseInt(tokens[1]) - print("Switching to outline group "+outlineGroupIndex) + print("Received '"+message+"' from hightlight.qml") + if (tokens[0]=="highlight") { + highlightGroupIndex = parseInt(tokens[1]) + print("Switching to highlight group "+highlightGroupIndex) } else if (tokens[0]=="pick") { isSelectionEnabled = tokens[1]=='true' print("Ray picking set to "+isSelectionEnabled.toString()) @@ -143,7 +143,7 @@ function update(deltaTime) { selectedID = prevID selectedType = prevType Selection.addToSelectedItemsList(selectionName, selectedType, selectedID) - print("OUTLINE " + outlineGroupIndex + " picked type: " + result.type + ", id: " + result.objectID); + print("HIGHLIGHT " + highlightGroupIndex + " picked type: " + result.type + ", id: " + result.objectID); } } else { if (prevID != 0 && !isSelectionAddEnabled) { diff --git a/scripts/developer/utilities/render/outline.qml b/scripts/developer/utilities/render/highlight.qml similarity index 88% rename from scripts/developer/utilities/render/outline.qml rename to scripts/developer/utilities/render/highlight.qml index 8269ea830e..eb2c66b275 100644 --- a/scripts/developer/utilities/render/outline.qml +++ b/scripts/developer/utilities/render/highlight.qml @@ -1,5 +1,5 @@ // -// outline.qml +// highlight.qml // developer/utilities/render // // Olivier Prat, created on 08/08/2017. @@ -11,7 +11,7 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "outlinePage" +import "highlightPage" import "qrc:///qml/styles-uit" import "qrc:///qml/controls-uit" as HifiControls @@ -21,7 +21,7 @@ Rectangle { color: hifi.colors.baseGray; anchors.margins: hifi.dimensions.contentMargin.x - property var debugConfig: Render.getConfig("RenderMainView.OutlineDebug") + property var debugConfig: Render.getConfig("RenderMainView.HighlightDebug") signal sendToScript(var message); Column { @@ -65,15 +65,15 @@ Rectangle { height: 400 onCurrentIndexChanged: { - sendToScript("outline "+currentIndex) + sendToScript("highlight "+currentIndex) } Repeater { model: [ 0, 1, 2, 3 ] Tab { title: "Outl."+modelData - OutlinePage { - outlineIndex: modelData + HighlightPage { + highlightIndex: modelData } } } diff --git a/scripts/developer/utilities/render/outlinePage/OutlinePage.qml b/scripts/developer/utilities/render/highlightPage/HighlightPage.qml similarity index 96% rename from scripts/developer/utilities/render/outlinePage/OutlinePage.qml rename to scripts/developer/utilities/render/highlightPage/HighlightPage.qml index ff1d7fa23b..5669f90628 100644 --- a/scripts/developer/utilities/render/outlinePage/OutlinePage.qml +++ b/scripts/developer/utilities/render/highlightPage/HighlightPage.qml @@ -1,5 +1,5 @@ // -// outlinePage.qml +// highlightPage.qml // developer/utilities/render // // Olivier Prat, created on 08/08/2017. @@ -17,8 +17,8 @@ import "qrc:///qml/controls-uit" as HifiControls Rectangle { id: root - property var outlineIndex: 0 - property var drawConfig: Render.getConfig("RenderMainView.OutlineEffect"+outlineIndex) + property var highlightIndex: 0 + property var drawConfig: Render.getConfig("RenderMainView.HighlightEffect"+highlightIndex) HifiConstants { id: hifi;} anchors.margins: hifi.dimensions.contentMargin.x diff --git a/scripts/developer/utilities/render/highlightPage/qmldir b/scripts/developer/utilities/render/highlightPage/qmldir new file mode 100644 index 0000000000..bb3de24b84 --- /dev/null +++ b/scripts/developer/utilities/render/highlightPage/qmldir @@ -0,0 +1 @@ +HighlightPage 1.0 HighlightPage.qml \ No newline at end of file diff --git a/scripts/developer/utilities/render/outlinePage/qmldir b/scripts/developer/utilities/render/outlinePage/qmldir deleted file mode 100644 index 56f5d45414..0000000000 --- a/scripts/developer/utilities/render/outlinePage/qmldir +++ /dev/null @@ -1 +0,0 @@ -OutlinePage 1.0 OutlinePage.qml \ No newline at end of file From 9817cb4c44e4f47b777fdd0368c1893e5623f554 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 7 Nov 2017 15:35:43 +0100 Subject: [PATCH 43/62] Added highlight configuration in HighlightStageSetup job --- .../ui/overlays/ContextOverlayInterface.cpp | 2 +- .../render-utils/src/HighlightEffect.cpp | 5 +- .../render/src/render/HighlightStage.cpp | 97 +++++++++++++- libraries/render/src/render/HighlightStage.h | 60 ++++++++- .../developer/utilities/render/highlight.qml | 123 ++++++++++++++++-- 5 files changed, 262 insertions(+), 25 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 6a21221a8b..d40c0972e9 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -72,7 +72,7 @@ ContextOverlayInterface::ContextOverlayInterface() { render::Transaction transaction; initializeSelectionToSceneHandler(_selectionToSceneHandlers[0], "contextOverlayHighlightList", transaction); for (auto i = 1; i < MAX_SELECTION_COUNT; i++) { - auto selectionName = QString("contextOverlayHighlightList") + QString::number(i); + auto selectionName = QString("highlightList") + QString::number(i); initializeSelectionToSceneHandler(_selectionToSceneHandlers[i], selectionName, transaction); } const render::ScenePointer& scene = qApp->getMain3DScene(); diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index e332be53de..c938567425 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -434,9 +434,10 @@ void SelectionToHighlight::run(const render::RenderContextPointer& renderContext for (auto i = 0; i < HighlightSharedParameters::MAX_HIGHLIGHT_COUNT; i++) { std::ostringstream stream; - stream << "contextOverlayHighlightList"; if (i > 0) { - stream << i; + stream << "highlightList" << i; + } else { + stream << "contextOverlayHighlightList"; } auto selectionName = stream.str(); auto highlightId = highlightStage->getHighlightIdBySelection(selectionName); diff --git a/libraries/render/src/render/HighlightStage.cpp b/libraries/render/src/render/HighlightStage.cpp index effb7c7e98..6821504649 100644 --- a/libraries/render/src/render/HighlightStage.cpp +++ b/libraries/render/src/render/HighlightStage.cpp @@ -34,13 +34,96 @@ Index HighlightStage::getHighlightIdBySelection(const std::string& selectionName return INVALID_INDEX; } -HighlightStageSetup::HighlightStageSetup() { -} - -void HighlightStageSetup::run(const render::RenderContextPointer& renderContext) { - if (!renderContext->_scene->getStage(HighlightStage::getName())) { - auto stage = std::make_shared(); - renderContext->_scene->resetStage(HighlightStage::getName(), stage); +const HighlightStyle& HighlightStageConfig::getStyle() const { + auto styleIterator = _styles.find(_selectionName); + if (styleIterator != _styles.end()) { + return styleIterator->second; + } else { + auto insertion = _styles.insert(SelectionStyles::value_type{ _selectionName, HighlightStyle{} }); + return insertion.first->second; + } +} + +HighlightStyle& HighlightStageConfig::editStyle() { + auto styleIterator = _styles.find(_selectionName); + if (styleIterator != _styles.end()) { + return styleIterator->second; + } else { + auto insertion = _styles.insert(SelectionStyles::value_type{ _selectionName, HighlightStyle{} }); + return insertion.first->second; + } +} + +void HighlightStageConfig::setSelectionName(const QString& name) { + _selectionName = name.toStdString(); + emit dirty(); +} + +void HighlightStageConfig::setOutlineSmooth(bool isSmooth) { + editStyle().isOutlineSmooth = isSmooth; + emit dirty(); +} + +void HighlightStageConfig::setColorRed(float value) { + editStyle().color.r = value; + emit dirty(); +} + +void HighlightStageConfig::setColorGreen(float value) { + editStyle().color.g = value; + emit dirty(); +} + +void HighlightStageConfig::setColorBlue(float value) { + editStyle().color.b = value; + emit dirty(); +} + +void HighlightStageConfig::setOutlineWidth(float value) { + editStyle().outlineWidth = value; + emit dirty(); +} + +void HighlightStageConfig::setOutlineIntensity(float value) { + editStyle().outlineIntensity = value; + emit dirty(); +} + +void HighlightStageConfig::setUnoccludedFillOpacity(float value) { + editStyle().unoccludedFillOpacity = value; + emit dirty(); +} + +void HighlightStageConfig::setOccludedFillOpacity(float value) { + editStyle().occludedFillOpacity = value; + emit dirty(); +} + +HighlightStageSetup::HighlightStageSetup() { +} + +void HighlightStageSetup::configure(const Config& config) { + // Copy the styles here but update the stage with the new styles in run to be sure everything is + // thread safe... + _styles = config._styles; +} + +void HighlightStageSetup::run(const render::RenderContextPointer& renderContext) { + auto stage = renderContext->_scene->getStage(HighlightStage::getName()); + if (!stage) { + stage = std::make_shared(); + renderContext->_scene->resetStage(HighlightStage::getName(), stage); + } + + if (!_styles.empty()) { + render::Transaction transaction; + for (const auto& selection : _styles) { + auto& selectionName = selection.first; + auto& selectionStyle = selection.second; + transaction.resetSelectionHighlight(selectionName, selectionStyle); + } + renderContext->_scene->enqueueTransaction(transaction); + _styles.clear(); } } diff --git a/libraries/render/src/render/HighlightStage.h b/libraries/render/src/render/HighlightStage.h index a2c123580c..7600f1f724 100644 --- a/libraries/render/src/render/HighlightStage.h +++ b/libraries/render/src/render/HighlightStage.h @@ -63,14 +63,72 @@ namespace render { }; using HighlightStagePointer = std::shared_ptr; + class HighlightStageConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(QString selectionName READ getSelectionName WRITE setSelectionName NOTIFY dirty) + Q_PROPERTY(bool isOutlineSmooth READ isOutlineSmooth WRITE setOutlineSmooth NOTIFY dirty) + Q_PROPERTY(float colorR READ getColorRed WRITE setColorRed NOTIFY dirty) + Q_PROPERTY(float colorG READ getColorGreen WRITE setColorGreen NOTIFY dirty) + Q_PROPERTY(float colorB READ getColorBlue WRITE setColorBlue NOTIFY dirty) + Q_PROPERTY(float outlineWidth READ getOutlineWidth WRITE setOutlineWidth NOTIFY dirty) + Q_PROPERTY(float outlineIntensity READ getOutlineIntensity WRITE setOutlineIntensity NOTIFY dirty) + Q_PROPERTY(float unoccludedFillOpacity READ getUnoccludedFillOpacity WRITE setUnoccludedFillOpacity NOTIFY dirty) + Q_PROPERTY(float occludedFillOpacity READ getOccludedFillOpacity WRITE setOccludedFillOpacity NOTIFY dirty) + + public: + + using SelectionStyles = std::map; + + QString getSelectionName() const { return QString(_selectionName.c_str()); } + void setSelectionName(const QString& name); + + bool isOutlineSmooth() const { return getStyle().isOutlineSmooth; } + void setOutlineSmooth(bool isSmooth); + + float getColorRed() const { return getStyle().color.r; } + void setColorRed(float value); + + float getColorGreen() const { return getStyle().color.g; } + void setColorGreen(float value); + + float getColorBlue() const { return getStyle().color.b; } + void setColorBlue(float value); + + float getOutlineWidth() const { return getStyle().outlineWidth; } + void setOutlineWidth(float value); + + float getOutlineIntensity() const { return getStyle().outlineIntensity; } + void setOutlineIntensity(float value); + + float getUnoccludedFillOpacity() const { return getStyle().unoccludedFillOpacity; } + void setUnoccludedFillOpacity(float value); + + float getOccludedFillOpacity() const { return getStyle().occludedFillOpacity; } + void setOccludedFillOpacity(float value); + + std::string _selectionName{ "contextOverlayHighlightList" }; + mutable SelectionStyles _styles; + + const HighlightStyle& getStyle() const; + HighlightStyle& editStyle(); + + signals: + void dirty(); + }; + class HighlightStageSetup { public: - using JobModel = render::Job::Model; + using Config = HighlightStageConfig; + using JobModel = render::Job::Model; HighlightStageSetup(); + + void configure(const Config& config); void run(const RenderContextPointer& renderContext); protected: + + HighlightStageConfig::SelectionStyles _styles; }; } diff --git a/scripts/developer/utilities/render/highlight.qml b/scripts/developer/utilities/render/highlight.qml index eb2c66b275..6be74fcf40 100644 --- a/scripts/developer/utilities/render/highlight.qml +++ b/scripts/developer/utilities/render/highlight.qml @@ -11,9 +11,10 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "highlightPage" + import "qrc:///qml/styles-uit" import "qrc:///qml/controls-uit" as HifiControls +import "configSlider" Rectangle { id: root @@ -22,10 +23,13 @@ Rectangle { anchors.margins: hifi.dimensions.contentMargin.x property var debugConfig: Render.getConfig("RenderMainView.HighlightDebug") + property var highlightConfig: Render.getConfig("UpdateScene.HighlightStageSetup") + signal sendToScript(var message); Column { - spacing: 5 + id: col + spacing: 10 anchors.left: parent.left anchors.right: parent.right anchors.margins: hifi.dimensions.contentMargin.x @@ -59,21 +63,112 @@ Rectangle { } } - TabView { - id: tabs - width: 384 - height: 400 + HifiControls.ComboBox { + id: box + width: 350 + z: 999 + editable: true + colorScheme: hifi.colorSchemes.dark + model: [ + "contextOverlayHighlightList", + "highlightList1", + "highlightList2", + "highlightList3", + "highlightList4"] + label: "" - onCurrentIndexChanged: { - sendToScript("highlight "+currentIndex) + Timer { + id: postpone + interval: 100; running: false; repeat: false + onTriggered: { paramWidgetLoader.sourceComponent = paramWidgets } } + onCurrentIndexChanged: { + root.highlightConfig["selectionName"] = model[currentIndex]; + sendToScript("highlight "+currentIndex) + // This is a hack to be sure the widgets below properly reflect the change of category: delete the Component + // by setting the loader source to Null and then recreate it 100ms later + paramWidgetLoader.sourceComponent = undefined; + postpone.interval = 100 + postpone.start() + } + } - Repeater { - model: [ 0, 1, 2, 3 ] - Tab { - title: "Outl."+modelData - HighlightPage { - highlightIndex: modelData + Loader { + id: paramWidgetLoader + sourceComponent: paramWidgets + width: 350 + } + + Component { + id: paramWidgets + + Column { + spacing: 10 + anchors.margins: hifi.dimensions.contentMargin.x + + HifiControls.Label { + text: "Outline" + } + Column { + spacing: 10 + anchors.left: parent.left + anchors.right: parent.right + HifiControls.CheckBox { + text: "Smooth" + checked: root.highlightConfig["isOutlineSmooth"] + onCheckedChanged: { + root.highlightConfig["isOutlineSmooth"] = checked; + } + } + Repeater { + model: ["Width:outlineWidth:5.0:0.0", + "Intensity:outlineIntensity:1.0:0.0" + ] + ConfigSlider { + label: qsTr(modelData.split(":")[0]) + integral: false + config: root.highlightConfig + property: modelData.split(":")[1] + max: modelData.split(":")[2] + min: modelData.split(":")[3] + } + } + } + + Separator {} + HifiControls.Label { + text: "Color" + } + Repeater { + model: ["Red:colorR:1.0:0.0", + "Green:colorG:1.0:0.0", + "Blue:colorB:1.0:0.0" + ] + ConfigSlider { + label: qsTr(modelData.split(":")[0]) + integral: false + config: root.highlightConfig + property: modelData.split(":")[1] + max: modelData.split(":")[2] + min: modelData.split(":")[3] + } + } + + Separator {} + HifiControls.Label { + text: "Fill Opacity" + } + Repeater { + model: ["Unoccluded:unoccludedFillOpacity:1.0:0.0", + "Occluded:occludedFillOpacity:1.0:0.0" + ] + ConfigSlider { + label: qsTr(modelData.split(":")[0]) + integral: false + config: root.highlightConfig + property: modelData.split(":")[1] + max: modelData.split(":")[2] + min: modelData.split(":")[3] } } } From 25b2a2ff5ee070cb90c4a94b5276fbdf341ddb4b Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 7 Nov 2017 16:57:33 +0100 Subject: [PATCH 44/62] Bounds buffer is now per instance to fix a bug --- libraries/render-utils/src/OutlineEffect.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index a2bfc88c81..50c30db728 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -86,8 +86,8 @@ protected: unsigned int _outlineIndex; render::ShapePlumberPointer _shapePlumber; OutlineSharedParametersPointer _sharedParameters; + gpu::BufferPointer _boundsBuffer; - static gpu::BufferPointer _boundsBuffer; static gpu::PipelinePointer _stencilMaskPipeline; static gpu::PipelinePointer _stencilMaskFillPipeline; }; From fe79730012da39e024b86af80cb60dc5fc462593 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 7 Nov 2017 16:58:04 +0100 Subject: [PATCH 45/62] Bounds buffer is now per instance to fix a bug --- libraries/render-utils/src/OutlineEffect.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index a42cbfe358..4d2682cebd 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -99,7 +99,6 @@ void PrepareDrawOutline::run(const render::RenderContextPointer& renderContext, gpu::PipelinePointer DrawOutlineMask::_stencilMaskPipeline; gpu::PipelinePointer DrawOutlineMask::_stencilMaskFillPipeline; -gpu::BufferPointer DrawOutlineMask::_boundsBuffer; DrawOutlineMask::DrawOutlineMask(unsigned int outlineIndex, render::ShapePlumberPointer shapePlumber, OutlineSharedParametersPointer parameters) : From ffa46b487a898db3eac17a436fab8f744cd38eb0 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 7 Nov 2017 17:07:37 +0100 Subject: [PATCH 46/62] Only non empty selections are now linked to highlight passes --- .../render-utils/src/HighlightEffect.cpp | 36 ++++++++++--------- libraries/render-utils/src/HighlightEffect.h | 16 ++++----- libraries/render/src/render/Scene.cpp | 13 ++++++- libraries/render/src/render/Scene.h | 4 +++ 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index c938567425..98a8835409 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -104,11 +104,10 @@ void PrepareDrawHighlight::run(const render::RenderContextPointer& renderContext gpu::PipelinePointer DrawHighlightMask::_stencilMaskPipeline; gpu::PipelinePointer DrawHighlightMask::_stencilMaskFillPipeline; -gpu::BufferPointer DrawHighlightMask::_boundsBuffer; DrawHighlightMask::DrawHighlightMask(unsigned int highlightIndex, render::ShapePlumberPointer shapePlumber, HighlightSharedParametersPointer parameters) : - _highlightIndex{ highlightIndex }, + _highlightPassIndex{ highlightIndex }, _shapePlumber { shapePlumber }, _sharedParameters{ parameters } { } @@ -147,7 +146,7 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c } auto highlightStage = renderContext->_scene->getStage(render::HighlightStage::getName()); - auto highlightId = _sharedParameters->_highlightIds[_highlightIndex]; + auto highlightId = _sharedParameters->_highlightIds[_highlightPassIndex]; if (!inShapes.empty() && !render::HighlightStage::isIndexInvalid(highlightId)) { auto ressources = inputs.get1(); @@ -221,8 +220,8 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c // Draw stencil mask with object bounding boxes const auto highlightWidthLoc = _stencilMaskPipeline->getProgram()->getUniforms().findLocation("outlineWidth"); - const auto sqrt3 = 1.74f; - const float blurPixelWidth = 2.0f * sqrt3 * HighlightSharedParameters::getBlurPixelWidth(highlight._style, args->_viewport.w); + const auto securityMargin = 2.0f; + const float blurPixelWidth = 2.0f * securityMargin * HighlightSharedParameters::getBlurPixelWidth(highlight._style, args->_viewport.w); const auto framebufferSize = ressources->getSourceFrameSize(); auto stencilPipeline = highlight._style.isFilled() ? _stencilMaskFillPipeline : _stencilMaskPipeline; @@ -242,7 +241,7 @@ gpu::PipelinePointer DrawHighlight::_pipeline; gpu::PipelinePointer DrawHighlight::_pipelineFilled; DrawHighlight::DrawHighlight(unsigned int highlightIndex, HighlightSharedParametersPointer parameters) : - _highlightIndex{ highlightIndex }, + _highlightPassIndex{ highlightIndex }, _sharedParameters{ parameters } { } @@ -261,9 +260,9 @@ void DrawHighlight::run(const render::RenderContextPointer& renderContext, const auto args = renderContext->args; auto highlightStage = renderContext->_scene->getStage(render::HighlightStage::getName()); - auto highlightId = _sharedParameters->_highlightIds[_highlightIndex]; + auto highlightId = _sharedParameters->_highlightIds[_highlightPassIndex]; if (!render::HighlightStage::isIndexInvalid(highlightId)) { - auto& highlight = highlightStage->getHighlight(_sharedParameters->_highlightIds[_highlightIndex]); + auto& highlight = highlightStage->getHighlight(highlightId); auto pipeline = getPipeline(highlight._style); { auto& shaderParameters = _configuration.edit(); @@ -427,12 +426,13 @@ const gpu::PipelinePointer& DebugHighlight::getDepthPipeline() { } void SelectionToHighlight::run(const render::RenderContextPointer& renderContext, Outputs& outputs) { - auto highlightStage = renderContext->_scene->getStage(render::HighlightStage::getName()); + auto scene = renderContext->_scene; + auto highlightStage = scene->getStage(render::HighlightStage::getName()); outputs.clear(); _sharedParameters->_highlightIds.fill(render::HighlightStage::INVALID_INDEX); - for (auto i = 0; i < HighlightSharedParameters::MAX_HIGHLIGHT_COUNT; i++) { + for (auto i = 0; i < HighlightSharedParameters::MAX_PASS_COUNT; i++) { std::ostringstream stream; if (i > 0) { stream << "highlightList" << i; @@ -440,17 +440,19 @@ void SelectionToHighlight::run(const render::RenderContextPointer& renderContext stream << "contextOverlayHighlightList"; } auto selectionName = stream.str(); - auto highlightId = highlightStage->getHighlightIdBySelection(selectionName); - if (!render::HighlightStage::isIndexInvalid(highlightId)) { - _sharedParameters->_highlightIds[outputs.size()] = highlightId; - outputs.emplace_back(selectionName); + if (!scene->isSelectionEmpty(selectionName)) { + auto highlightId = highlightStage->getHighlightIdBySelection(selectionName); + if (!render::HighlightStage::isIndexInvalid(highlightId)) { + _sharedParameters->_highlightIds[outputs.size()] = highlightId; + outputs.emplace_back(selectionName); + } } } } void ExtractSelectionName::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { - if (_highlightIndex < inputs.size()) { - outputs = inputs[_highlightIndex]; + if (_highlightPassIndex < inputs.size()) { + outputs = inputs[_highlightPassIndex]; } else { outputs.clear(); } @@ -487,7 +489,7 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren const auto highlightRessources = task.addJob("PrepareHighlight", primaryFramebuffer); render::Varying highlight0Rect; - for (auto i = 0; i < HighlightSharedParameters::MAX_HIGHLIGHT_COUNT; i++) { + for (auto i = 0; i < HighlightSharedParameters::MAX_PASS_COUNT; i++) { const auto selectionName = task.addJob("ExtractSelectionName", highlightSelectionNames, i); const auto groupItems = addSelectItemJobs(task, selectionName, items); const auto highlightedItemIDs = task.addJob("HighlightMetaToSubItemIDs", groupItems); diff --git a/libraries/render-utils/src/HighlightEffect.h b/libraries/render-utils/src/HighlightEffect.h index 0531c03efd..90a8e730ce 100644 --- a/libraries/render-utils/src/HighlightEffect.h +++ b/libraries/render-utils/src/HighlightEffect.h @@ -50,12 +50,12 @@ class HighlightSharedParameters { public: enum { - MAX_HIGHLIGHT_COUNT = 8 + MAX_PASS_COUNT = 8 }; HighlightSharedParameters(); - std::array _highlightIds; + std::array _highlightIds; static float getBlurPixelWidth(const render::HighlightStyle& style, int frameBufferHeight); }; @@ -100,13 +100,13 @@ public: using Outputs = std::string; using JobModel = render::Job::ModelIO; - ExtractSelectionName(unsigned int highlightIndex) : _highlightIndex{ highlightIndex } {} + ExtractSelectionName(unsigned int highlightIndex) : _highlightPassIndex{ highlightIndex } {} void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); private: - unsigned int _highlightIndex; + unsigned int _highlightPassIndex; }; @@ -123,11 +123,11 @@ public: protected: - unsigned int _highlightIndex; + unsigned int _highlightPassIndex; render::ShapePlumberPointer _shapePlumber; HighlightSharedParametersPointer _sharedParameters; - - static gpu::BufferPointer _boundsBuffer; + gpu::BufferPointer _boundsBuffer; + static gpu::PipelinePointer _stencilMaskPipeline; static gpu::PipelinePointer _stencilMaskFillPipeline; }; @@ -162,7 +162,7 @@ private: static gpu::PipelinePointer _pipeline; static gpu::PipelinePointer _pipelineFilled; - unsigned int _highlightIndex; + unsigned int _highlightPassIndex; HighlightParameters _parameters; HighlightSharedParametersPointer _sharedParameters; HighlightConfigurationBuffer _configuration; diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 9cdaa89cf6..88e25b6d27 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -426,7 +426,7 @@ void Scene::resetItemTransition(ItemID itemId) { } } -// THis fucntion is thread safe +// This function is thread safe Selection Scene::getSelection(const Selection::Name& name) const { std::unique_lock lock(_selectionsMutex); auto found = _selections.find(name); @@ -437,6 +437,17 @@ Selection Scene::getSelection(const Selection::Name& name) const { } } +// This function is thread safe +bool Scene::isSelectionEmpty(const Selection::Name& name) const { + std::unique_lock lock(_selectionsMutex); + auto found = _selections.find(name); + if (found == _selections.end()) { + return false; + } else { + return (*found).second.isEmpty(); + } +} + void Scene::resetSelections(const Transaction::SelectionResets& transactions) { for (auto selection : transactions) { auto found = _selections.find(selection.getName()); diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 4bf38b89cc..af6204acb4 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -143,6 +143,10 @@ public: // Thread safe Selection getSelection(const Selection::Name& name) const; + // Check if a particular selection is empty (returns true if doesn't exist) + // Thread safe + bool isSelectionEmpty(const Selection::Name& name) const; + // This next call are NOT threadsafe, you have to call them from the correct thread to avoid any potential issues // Access a particular item form its ID From 0bbd1edc0aff10cb275c11bfae23c211dcf9603f Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 7 Nov 2017 18:26:15 +0100 Subject: [PATCH 47/62] Fixed fluttering highlight bug by attaching only stencil when drawing outline --- libraries/gpu/src/gpu/Framebuffer.cpp | 63 +++++++++++++++---- libraries/gpu/src/gpu/Framebuffer.h | 3 + .../render-utils/src/HighlightEffect.cpp | 2 +- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp index f1257e7c83..4fcc9ab0f9 100755 --- a/libraries/gpu/src/gpu/Framebuffer.cpp +++ b/libraries/gpu/src/gpu/Framebuffer.cpp @@ -220,7 +220,7 @@ uint32 Framebuffer::getRenderBufferSubresource(uint32 slot) const { } } -bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) { +bool Framebuffer::assignDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) { if (isSwapchain()) { return false; } @@ -244,20 +244,59 @@ bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const For // assign the new one _depthStencilBuffer = TextureView(texture, subresource, format); - _bufferMask = ( _bufferMask & ~BUFFER_DEPTHSTENCIL); - if (texture) { - if (format.getSemantic() == gpu::DEPTH) { - _bufferMask |= BUFFER_DEPTH; - } else if (format.getSemantic() == gpu::STENCIL) { - _bufferMask |= BUFFER_STENCIL; - } else if (format.getSemantic() == gpu::DEPTH_STENCIL) { - _bufferMask |= BUFFER_DEPTHSTENCIL; - } - } - return true; } +bool Framebuffer::setDepthBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) { + if (assignDepthStencilBuffer(texture, format, subresource)) { + _bufferMask = (_bufferMask & ~BUFFER_DEPTHSTENCIL); + if (texture) { + if (format.getSemantic() == gpu::DEPTH || format.getSemantic() == gpu::DEPTH_STENCIL) { + _bufferMask |= BUFFER_DEPTH; + } else { + return false; + } + } + + return true; + } + return false; +} + +bool Framebuffer::setStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) { + if (assignDepthStencilBuffer(texture, format, subresource)) { + _bufferMask = (_bufferMask & ~BUFFER_DEPTHSTENCIL); + if (texture) { + if (format.getSemantic() == gpu::STENCIL || format.getSemantic() == gpu::DEPTH_STENCIL) { + _bufferMask |= BUFFER_STENCIL; + } else { + return false; + } + } + + return true; + } + return false; +} + +bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) { + if (assignDepthStencilBuffer(texture, format, subresource)) { + _bufferMask = (_bufferMask & ~BUFFER_DEPTHSTENCIL); + if (texture) { + if (format.getSemantic() == gpu::DEPTH) { + _bufferMask |= BUFFER_DEPTH; + } else if (format.getSemantic() == gpu::STENCIL) { + _bufferMask |= BUFFER_STENCIL; + } else if (format.getSemantic() == gpu::DEPTH_STENCIL) { + _bufferMask |= BUFFER_DEPTHSTENCIL; + } + } + + return true; + } + return false; +} + TexturePointer Framebuffer::getDepthStencilBuffer() const { if (isSwapchain()) { return TexturePointer(); diff --git a/libraries/gpu/src/gpu/Framebuffer.h b/libraries/gpu/src/gpu/Framebuffer.h index b3a500d68f..b3cf0fbba3 100755 --- a/libraries/gpu/src/gpu/Framebuffer.h +++ b/libraries/gpu/src/gpu/Framebuffer.h @@ -107,6 +107,8 @@ public: TexturePointer getRenderBuffer(uint32 slot) const; uint32 getRenderBufferSubresource(uint32 slot) const; + bool setDepthBuffer(const TexturePointer& texture, const Format& format, uint32 subresource = 0); + bool setStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource = 0); bool setDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource = 0); TexturePointer getDepthStencilBuffer() const; uint32 getDepthStencilBufferSubresource() const; @@ -168,6 +170,7 @@ protected: uint16 _numSamples = 0; void updateSize(const TexturePointer& texture); + bool assignDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource); // Non exposed Framebuffer(const Framebuffer& framebuffer) = delete; diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index 98a8835409..7c58e5ba66 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -59,7 +59,7 @@ void HighlightRessources::update(const gpu::FramebufferPointer& primaryFrameBuff void HighlightRessources::allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { _colorFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithStencil")); _colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0)); - _colorFrameBuffer->setDepthStencilBuffer(_depthStencilTexture, _depthStencilTexture->getTexelFormat()); + _colorFrameBuffer->setStencilBuffer(_depthStencilTexture, _depthStencilTexture->getTexelFormat()); } void HighlightRessources::allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { From bbcb0b2d9b82dce3d233d4e207f4419af115d7b3 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 8 Nov 2017 13:49:18 +0100 Subject: [PATCH 48/62] Fixed highlight in stereo mode, including HMD (testing on simulator) --- libraries/gpu/src/gpu/Transform.slh | 12 ++++++++---- libraries/render-utils/src/Highlight_aabox.slv | 5 +++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index 9feca4a3c9..b9b8544601 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -193,13 +193,17 @@ TransformObject getTransformObject() { } <@endfunc@> -<@func transformModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@> - { // transformModelToClipPos +<@func transformModelToMonoClipPos(cameraTransform, objectTransform, modelPos, clipPos)@> + { // transformModelToMonoClipPos vec4 eyeWAPos; <$transformModelToEyeWorldAlignedPos($cameraTransform$, $objectTransform$, $modelPos$, eyeWAPos)$> - <$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * eyeWAPos; - + } +<@endfunc@> + +<@func transformModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@> + { // transformModelToClipPos + <$transformModelToMonoClipPos($cameraTransform$, $objectTransform$, $modelPos$, $clipPos$)$> <$transformStereoClipsSpace($cameraTransform$, $clipPos$)$> } <@endfunc@> diff --git a/libraries/render-utils/src/Highlight_aabox.slv b/libraries/render-utils/src/Highlight_aabox.slv index 1a46ccd9c7..4927db9610 100644 --- a/libraries/render-utils/src/Highlight_aabox.slv +++ b/libraries/render-utils/src/Highlight_aabox.slv @@ -93,11 +93,12 @@ void main(void) { // standard transform TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, pos, gl_Position)$> + <$transformModelToMonoClipPos(cam, obj, pos, gl_Position)$> // Offset the vertex to take into account the outline width pos.xyz += UNIT_BOX_NORMALS[triangleIndex]; vec4 offsetPosition; - <$transformModelToClipPos(cam, obj, pos, offsetPosition)$> + <$transformModelToMonoClipPos(cam, obj, pos, offsetPosition)$> gl_Position.xy += normalize(offsetPosition.xy-gl_Position.xy) * outlineWidth * gl_Position.w; + <$transformStereoClipsSpace(cam, gl_Position)$> } From 5e32298183caa1cc7ef8f1a05d372d545cf6bef8 Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 8 Nov 2017 18:43:59 +0100 Subject: [PATCH 49/62] Rework as script=only solution --- interface/src/Application.cpp | 18 ++++-------------- interface/src/Application.h | 4 ---- scripts/developer/debugging/debugWindow.js | 10 ++++++++++ 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 13765e41d3..4f051697ad 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5781,7 +5781,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe ClipboardScriptingInterface* clipboardScriptable = new ClipboardScriptingInterface(); scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable); connect(scriptEngine.data(), &ScriptEngine::finished, clipboardScriptable, &ClipboardScriptingInterface::deleteLater); - connect(scriptEngine.data(), &ScriptEngine::finished, this, &Application::cleanupRunningScripts); scriptEngine->registerGlobalObject("Overlays", &_overlays); qScriptRegisterMetaType(scriptEngine.data(), OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue); @@ -6195,15 +6194,10 @@ void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const } void Application::showScriptLogs() { - if (!_runningScripts.contains("debugWindow.js")) { - auto scriptEngines = DependencyManager::get(); - QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); - defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/debugging/debugWindow.js"); - ScriptEnginePointer sePointer = scriptEngines->loadScript(defaultScriptsLoc.toString()); - _runningScripts["debugWindow.js"] = sePointer; - } else { - qWarning() << "Scripts Log already running"; - } + auto scriptEngines = DependencyManager::get(); + QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); + defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/debugging/debugWindow.js"); + scriptEngines->loadScript(defaultScriptsLoc.toString()); } void Application::showAssetServerWidget(QString filePath) { @@ -7302,10 +7296,6 @@ void Application::switchDisplayMode() { _previousHMDWornStatus = currentHMDWornStatus; } -void Application::cleanupRunningScripts(const QString& fileNameString, ScriptEnginePointer) { - _runningScripts.remove(QUrl(fileNameString).fileName()); -} - void Application::startHMDStandBySession() { _autoSwitchDisplayModeSupportedHMDPlugin->startStandBySession(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index c651ea6d5b..fbfb3979be 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -440,8 +440,6 @@ private slots: void handleSandboxStatus(QNetworkReply* reply); void switchDisplayMode(); - - void cleanupRunningScripts(const QString& fileNameString, ScriptEnginePointer); private: static void initDisplay(); void init(); @@ -712,7 +710,5 @@ private: std::atomic _pendingIdleEvent { false }; std::atomic _pendingRenderEvent { false }; - - QHash _runningScripts; }; #endif // hifi_Application_h diff --git a/scripts/developer/debugging/debugWindow.js b/scripts/developer/debugging/debugWindow.js index b16739b2b8..4390cf1f77 100644 --- a/scripts/developer/debugging/debugWindow.js +++ b/scripts/developer/debugging/debugWindow.js @@ -10,13 +10,23 @@ (function() { // BEGIN LOCAL_SCOPE +//check if script already running. +var scriptData = ScriptDiscoveryService.getRunning(); +var scripts = scriptData.filter(function (datum) { return datum.name === 'debugWindow.js'; }); +if (scripts.length >= 2) { + //2nd instance of the script is too much + return; +} + // Set up the qml ui var qml = Script.resolvePath('debugWindow.qml'); + var window = new OverlayWindow({ title: 'Debug Window', source: qml, width: 400, height: 900, }); + window.setPosition(25, 50); window.closed.connect(function() { Script.stop(); }); From 02625a6e28d63c0f6fb7f488eb3b1d310a3d8703 Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 8 Nov 2017 21:53:42 +0100 Subject: [PATCH 50/62] Remove script from engine --- scripts/developer/debugging/debugWindow.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/developer/debugging/debugWindow.js b/scripts/developer/debugging/debugWindow.js index 4390cf1f77..068efb351b 100644 --- a/scripts/developer/debugging/debugWindow.js +++ b/scripts/developer/debugging/debugWindow.js @@ -15,6 +15,7 @@ var scriptData = ScriptDiscoveryService.getRunning(); var scripts = scriptData.filter(function (datum) { return datum.name === 'debugWindow.js'; }); if (scripts.length >= 2) { //2nd instance of the script is too much + ScriptDiscoveryService.stopScript(scripts[1].url); return; } From dac2ae19a427fc64450c129cd7481d145102d463 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Wed, 8 Nov 2017 12:24:16 -0500 Subject: [PATCH 51/62] [Case 6569] Add protection for dynamic vs static collision type (details below). When creating a model, it was possible to elect Exact collision with the dynamic property even though this combination is prohibited. This combination would silently fail rather than notifying the user of the error. This commit implements safeguards against the invalid combination: * If the user elects the Exact collision type and the dynamic field is unchecked, then dynamic field is disabled. * If the user elects a different collision type and the dynamic filed is unchecked, then the dynamic field is enabled. * If the user has checked the dynamic field and subsequently elects the Exact collision type, then the selection is void and the previous collision selection is retained; however, an error dialog is presented informing the user of the error. Changes Committed: modified: interface/resources/qml/hifi/tablet/NewModelDialog.qml --- .../qml/hifi/tablet/NewModelDialog.qml | 49 ++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/NewModelDialog.qml b/interface/resources/qml/hifi/tablet/NewModelDialog.qml index 47d28486a9..e6459fe7bb 100644 --- a/interface/resources/qml/hifi/tablet/NewModelDialog.qml +++ b/interface/resources/qml/hifi/tablet/NewModelDialog.qml @@ -11,8 +11,11 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 +import QtQuick.Dialogs 1.2 as OriginalDialogs + import "../../styles-uit" import "../../controls-uit" +import "../dialogs" Rectangle { id: newModelDialog @@ -25,6 +28,15 @@ Rectangle { property bool punctuationMode: false property bool keyboardRasied: false + function errorMessageBox(message) { + return desktop.messageBox({ + icon: hifi.icons.warning, + defaultButton: OriginalDialogs.StandardButton.Ok, + title: "Error", + text: message + }); + } + Item { id: column1 anchors.rightMargin: 10 @@ -98,7 +110,6 @@ Rectangle { CheckBox { id: dynamic text: qsTr("Dynamic") - } Row { @@ -139,15 +150,39 @@ Rectangle { ComboBox { id: collisionType + + property int priorIndex: 0 + property string staticMeshCollisionText: "Exact - All polygons" + property var collisionArray: ["No Collision", + "Basic - Whole model", + "Good - Sub-meshes", + staticMeshCollisionText, + "Box", + "Sphere"] + width: 200 z: 100 transformOrigin: Item.Center - model: ["No Collision", - "Basic - Whole model", - "Good - Sub-meshes", - "Exact - All polygons", - "Box", - "Sphere"] + model: collisionArray + + onCurrentIndexChanged: { + if (collisionArray[currentIndex] === staticMeshCollisionText) { + + if (dynamic.checked) { + currentIndex = priorIndex; + + errorMessageBox("Models with Automatic Collisions set to \"" + staticMeshCollisionText + "\" cannot be dynamic."); + //--EARLY EXIT--( Can't have a static mesh model that's dynamic ) + return; + } + + dynamic.enabled = false; + } else { + dynamic.enabled = true; + } + + priorIndex = currentIndex; + } } Row { From a057cb914f65c37b3ac7b4a823488c9068ae8e1c Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Wed, 8 Nov 2017 14:18:04 -0500 Subject: [PATCH 52/62] [Case 6569] Fixes clipping of cancel button by window edge. Changes Committed: modified: interface/resources/qml/hifi/tablet/NewModelDialog.qml --- interface/resources/qml/hifi/tablet/NewModelDialog.qml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/NewModelDialog.qml b/interface/resources/qml/hifi/tablet/NewModelDialog.qml index e6459fe7bb..da77464325 100644 --- a/interface/resources/qml/hifi/tablet/NewModelDialog.qml +++ b/interface/resources/qml/hifi/tablet/NewModelDialog.qml @@ -128,6 +128,7 @@ Rectangle { Text { id: text2 width: 160 + x: dynamic.width / 2 color: "#ffffff" text: qsTr("Models with automatic collisions set to 'Exact' cannot be dynamic, and should not be used as floors") wrapMode: Text.WordWrap @@ -190,10 +191,10 @@ Rectangle { width: 200 height: 400 spacing: 5 - - anchors { - rightMargin: 15 - } + + anchors.horizontalCenter: column3.horizontalCenter + anchors.horizontalCenterOffset: -20 + Button { id: button1 text: qsTr("Add") From 0e90c92cf5038c732cc2449fd39a4b840da9eee6 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Wed, 8 Nov 2017 18:35:12 -0500 Subject: [PATCH 53/62] [Case 6569] Minor: Break up line to pass column count guidelines. Changes Committed: modified: interface/resources/qml/hifi/tablet/NewModelDialog.qml --- interface/resources/qml/hifi/tablet/NewModelDialog.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/tablet/NewModelDialog.qml b/interface/resources/qml/hifi/tablet/NewModelDialog.qml index da77464325..3debc8b9e7 100644 --- a/interface/resources/qml/hifi/tablet/NewModelDialog.qml +++ b/interface/resources/qml/hifi/tablet/NewModelDialog.qml @@ -172,7 +172,8 @@ Rectangle { if (dynamic.checked) { currentIndex = priorIndex; - errorMessageBox("Models with Automatic Collisions set to \"" + staticMeshCollisionText + "\" cannot be dynamic."); + errorMessageBox("Models with Automatic Collisions set to \"" + + staticMeshCollisionText + "\" cannot be dynamic."); //--EARLY EXIT--( Can't have a static mesh model that's dynamic ) return; } From c1b5d009934ba5f8529807abd4bf2d5e96b58547 Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Wed, 8 Nov 2017 15:41:24 -0800 Subject: [PATCH 54/62] Converted 'Background Blend' control to a slider. --- scripts/system/html/entityProperties.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 7fdeee67d3..0b96b59ddb 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -587,8 +587,8 @@

- - + +
From 5c7ebc0af3151e3fe93117f39565fc30751b0b89 Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Wed, 8 Nov 2017 16:44:05 -0800 Subject: [PATCH 55/62] Converted 'Glare Angle' control to a slider. --- scripts/system/html/entityProperties.html | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 0b96b59ddb..dba6b7e792 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -587,8 +587,11 @@

- - + + + + +
@@ -612,8 +615,11 @@

- - + + + +
+
From 7cb9514feb96eb5440f42001009f17a040cd58f3 Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Wed, 8 Nov 2017 16:50:44 -0800 Subject: [PATCH 56/62] Converted 'Glare Angle' control to a slider. --- scripts/system/html/entityProperties.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index dba6b7e792..9453b476ee 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -557,8 +557,8 @@
- < - /div> + +
From 6d019b43029d579ea1f01502f13b9b743492a5dd Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 8 Nov 2017 18:16:42 -0800 Subject: [PATCH 57/62] Update the audio codecs --- cmake/externals/hifiAudioCodec/CMakeLists.txt | 76 +++++++++---------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/cmake/externals/hifiAudioCodec/CMakeLists.txt b/cmake/externals/hifiAudioCodec/CMakeLists.txt index a30396c6fd..808a6174ca 100644 --- a/cmake/externals/hifiAudioCodec/CMakeLists.txt +++ b/cmake/externals/hifiAudioCodec/CMakeLists.txt @@ -5,43 +5,41 @@ set(EXTERNAL_NAME hifiAudioCodec) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -if (NOT ANDROID) - - if (WIN32 OR APPLE) - ExternalProject_Add( - ${EXTERNAL_NAME} - URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-1.zip - URL_MD5 23ec3fe51eaa155ea159a4971856fc13 - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 - ) - else () - ExternalProject_Add( - ${EXTERNAL_NAME} - URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux.zip - URL_MD5 7d37914a18aa4de971d2f45dd3043bde - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 - ) - endif() - - # Hide this external target (for ide users) - set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - - ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) - - set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL) - - if (WIN32) - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE TYPE INTERNAL) - elseif(APPLE) - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL) - elseif(NOT ANDROID) - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL) - endif() - +if (WIN32) + set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-win-2.0.zip) + set(DOWNLOAD_MD5 9199d4dbd6b16bed736b235efe980e67) +elseif (APPLE) + set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-mac-2.0.zip) + set(DOWNLOAD_MD5 21649881e7d5dc94f922179be96f76ba) +elseif (UNIX) + set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux-2.0.zip) + set(DOWNLOAD_MD5 988194f22f8d064c53d548259f426dce) +elseif (ANDROID) + set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-android-2.0.zip) + set(DOWNLOAD_MD5 aef2a852600d498d58aa586668191683) +else() + return() +endif() + +ExternalProject_Add( + ${EXTERNAL_NAME} + URL ${DOWNLOAD_URL} + URL_MD5 ${DOWNLOAD_MD5} + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 +) + +# Hide this external target (for ide users) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + +ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) + +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL) + +if (WIN32) + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE TYPE INTERNAL) +else() + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL) endif() From 4f7ed38e98e2c49a3764a608dabaee7cbb8bfaaa Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 9 Nov 2017 11:26:08 +0100 Subject: [PATCH 58/62] Fixed potential link errors on Mac --- libraries/render-utils/src/BackgroundStage.cpp | 1 + libraries/render-utils/src/BackgroundStage.h | 2 +- libraries/render-utils/src/HazeStage.cpp | 1 + libraries/render-utils/src/HazeStage.h | 2 +- libraries/render-utils/src/LightStage.cpp | 1 + libraries/render-utils/src/LightStage.h | 2 +- libraries/render/src/render/HighlightStage.cpp | 1 + libraries/render/src/render/HighlightStage.h | 2 +- libraries/render/src/render/TransitionStage.cpp | 1 + libraries/render/src/render/TransitionStage.h | 2 +- 10 files changed, 10 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/BackgroundStage.cpp b/libraries/render-utils/src/BackgroundStage.cpp index 2ea3683c4a..2d2c0ed150 100644 --- a/libraries/render-utils/src/BackgroundStage.cpp +++ b/libraries/render-utils/src/BackgroundStage.cpp @@ -14,6 +14,7 @@ #include std::string BackgroundStage::_stageName { "BACKGROUND_STAGE"}; +const BackgroundStage::Index BackgroundStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; BackgroundStage::Index BackgroundStage::findBackground(const BackgroundPointer& background) const { auto found = _backgroundMap.find(background); diff --git a/libraries/render-utils/src/BackgroundStage.h b/libraries/render-utils/src/BackgroundStage.h index eab7c94f0d..4e0e09db5b 100644 --- a/libraries/render-utils/src/BackgroundStage.h +++ b/libraries/render-utils/src/BackgroundStage.h @@ -27,7 +27,7 @@ public: static const std::string& getName() { return _stageName; } using Index = render::indexed_container::Index; - static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX }; + static const Index INVALID_INDEX; static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } using BackgroundPointer = model::SunSkyStagePointer; diff --git a/libraries/render-utils/src/HazeStage.cpp b/libraries/render-utils/src/HazeStage.cpp index 7a12ee3c8a..016282d16f 100644 --- a/libraries/render-utils/src/HazeStage.cpp +++ b/libraries/render-utils/src/HazeStage.cpp @@ -14,6 +14,7 @@ #include std::string HazeStage::_stageName { "HAZE_STAGE"}; +const HazeStage::Index HazeStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; FetchHazeStage::FetchHazeStage() { _haze = std::make_shared(); diff --git a/libraries/render-utils/src/HazeStage.h b/libraries/render-utils/src/HazeStage.h index 102f299d8f..c355f06644 100644 --- a/libraries/render-utils/src/HazeStage.h +++ b/libraries/render-utils/src/HazeStage.h @@ -28,7 +28,7 @@ public: static const std::string& getName() { return _stageName; } using Index = render::indexed_container::Index; - static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX }; + static const Index INVALID_INDEX; static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } using HazePointer = model::HazePointer; diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 079c63f367..c280abfeaf 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -14,6 +14,7 @@ #include "LightStage.h" std::string LightStage::_stageName { "LIGHT_STAGE"}; +const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; LightStage::LightStage() { } diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 66d73c9a6e..c26f504658 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -32,7 +32,7 @@ public: static const std::string& getName() { return _stageName; } using Index = render::indexed_container::Index; - static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX }; + static const Index INVALID_INDEX; static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } using LightPointer = model::LightPointer; diff --git a/libraries/render/src/render/HighlightStage.cpp b/libraries/render/src/render/HighlightStage.cpp index 6821504649..ade3844321 100644 --- a/libraries/render/src/render/HighlightStage.cpp +++ b/libraries/render/src/render/HighlightStage.cpp @@ -3,6 +3,7 @@ using namespace render; std::string HighlightStage::_name("Highlight"); +const HighlightStage::Index HighlightStage::INVALID_INDEX{ render::indexed_container::INVALID_INDEX }; HighlightStage::Index HighlightStage::addHighlight(const std::string& selectionName, const HighlightStyle& style) { Highlight outline{ selectionName, style }; diff --git a/libraries/render/src/render/HighlightStage.h b/libraries/render/src/render/HighlightStage.h index 7600f1f724..b35fff654c 100644 --- a/libraries/render/src/render/HighlightStage.h +++ b/libraries/render/src/render/HighlightStage.h @@ -35,7 +35,7 @@ namespace render { static const std::string& getName() { return _name; } using Index = render::indexed_container::Index; - static const Index INVALID_INDEX{ render::indexed_container::INVALID_INDEX }; + static const Index INVALID_INDEX; using HighlightIdList = render::indexed_container::Indices; static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } diff --git a/libraries/render/src/render/TransitionStage.cpp b/libraries/render/src/render/TransitionStage.cpp index 33ef829c64..9ddc72f4db 100644 --- a/libraries/render/src/render/TransitionStage.cpp +++ b/libraries/render/src/render/TransitionStage.cpp @@ -5,6 +5,7 @@ using namespace render; std::string TransitionStage::_name("Transition"); +const TransitionStage::Index TransitionStage::INVALID_INDEX{ indexed_container::INVALID_INDEX }; TransitionStage::Index TransitionStage::addTransition(ItemID itemId, Transition::Type type, ItemID boundId) { Transition transition; diff --git a/libraries/render/src/render/TransitionStage.h b/libraries/render/src/render/TransitionStage.h index 226d531d8b..1d10a5dedb 100644 --- a/libraries/render/src/render/TransitionStage.h +++ b/libraries/render/src/render/TransitionStage.h @@ -25,7 +25,7 @@ namespace render { static const std::string& getName() { return _name; } using Index = indexed_container::Index; - static const Index INVALID_INDEX{ indexed_container::INVALID_INDEX }; + static const Index INVALID_INDEX; using TransitionIdList = indexed_container::Indices; static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } From 5507bcea3c0e1e90bfc1abf9b2c9251ce42b3568 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 9 Nov 2017 10:34:01 -0800 Subject: [PATCH 59/62] Remove hack for missing inttypes.h header on VS2010 --- libraries/avatars/src/AvatarData.h | 14 -------------- libraries/octree/src/OctreeQuery.h | 14 -------------- 2 files changed, 28 deletions(-) diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 89fe270af1..d8169bb8f7 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -15,21 +15,7 @@ #include #include #include - -/* VS2010 defines stdint.h, but not inttypes.h */ -#if defined(_MSC_VER) -typedef signed char int8_t; -typedef signed short int16_t; -typedef signed int int32_t; -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef signed long long int64_t; -typedef unsigned long long quint64; -#define PRId64 "I64d" -#else #include -#endif #include #include diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h index 81a63a696c..fc9ea525e6 100644 --- a/libraries/octree/src/OctreeQuery.h +++ b/libraries/octree/src/OctreeQuery.h @@ -12,21 +12,7 @@ #ifndef hifi_OctreeQuery_h #define hifi_OctreeQuery_h -/* VS2010 defines stdint.h, but not inttypes.h */ -#if defined(_MSC_VER) -typedef signed char int8_t; -typedef signed short int16_t; -typedef signed int int32_t; -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef signed long long int64_t; -typedef unsigned long long quint64; -#define PRId64 "I64d" -#else #include -#endif - #include #include From 9a1386bf0d9c1ee157f6b4ec199eb2c9c3479b87 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 9 Nov 2017 10:34:52 -0800 Subject: [PATCH 60/62] Fix build errors on Linux --- cmake/externals/hifiAudioCodec/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/externals/hifiAudioCodec/CMakeLists.txt b/cmake/externals/hifiAudioCodec/CMakeLists.txt index 808a6174ca..298f666304 100644 --- a/cmake/externals/hifiAudioCodec/CMakeLists.txt +++ b/cmake/externals/hifiAudioCodec/CMakeLists.txt @@ -13,7 +13,7 @@ elseif (APPLE) set(DOWNLOAD_MD5 21649881e7d5dc94f922179be96f76ba) elseif (UNIX) set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux-2.0.zip) - set(DOWNLOAD_MD5 988194f22f8d064c53d548259f426dce) + set(DOWNLOAD_MD5 67fb7755f9bcafb98a9fceea53bc7481) elseif (ANDROID) set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-android-2.0.zip) set(DOWNLOAD_MD5 aef2a852600d498d58aa586668191683) From c324f342b4bae05044f9c072127dc8c009b85761 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 9 Nov 2017 13:39:13 -0800 Subject: [PATCH 61/62] Fix to check ANDROID before UNIX --- cmake/externals/hifiAudioCodec/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/externals/hifiAudioCodec/CMakeLists.txt b/cmake/externals/hifiAudioCodec/CMakeLists.txt index 298f666304..e3ba36a440 100644 --- a/cmake/externals/hifiAudioCodec/CMakeLists.txt +++ b/cmake/externals/hifiAudioCodec/CMakeLists.txt @@ -11,12 +11,12 @@ if (WIN32) elseif (APPLE) set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-mac-2.0.zip) set(DOWNLOAD_MD5 21649881e7d5dc94f922179be96f76ba) -elseif (UNIX) - set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux-2.0.zip) - set(DOWNLOAD_MD5 67fb7755f9bcafb98a9fceea53bc7481) elseif (ANDROID) set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-android-2.0.zip) set(DOWNLOAD_MD5 aef2a852600d498d58aa586668191683) +elseif (UNIX) + set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux-2.0.zip) + set(DOWNLOAD_MD5 67fb7755f9bcafb98a9fceea53bc7481) else() return() endif() From ba4c0f189e9600921efa5c4107858775b04b6de3 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 10 Nov 2017 10:25:29 -0800 Subject: [PATCH 62/62] code review feedback removed discontinuity in safeDeltaTime, which is used to prevent division by zero. --- interface/src/LODManager.cpp | 3 ++- libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 8f01296e1c..01ccbd0d9a 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -54,7 +54,8 @@ void LODManager::autoAdjustLOD(float batchTime, float engineRunTime, float delta float renderTime = batchTime + OVERLAY_AND_SWAP_TIME_BUDGET; float maxTime = glm::max(renderTime, engineRunTime); const float BLEND_TIMESCALE = 0.3f; // sec - const float safeDeltaTime = (deltaTimeSec == 0.0f) ? 0.001f : deltaTimeSec; + const float MIN_DELTA_TIME = 0.001f; + const float safeDeltaTime = glm::max(deltaTimeSec, MIN_DELTA_TIME); float blend = BLEND_TIMESCALE / safeDeltaTime; if (blend > 1.0f) { blend = 1.0f; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 2335095056..49d2431098 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -453,7 +453,8 @@ void Avatar::applyPositionDelta(const glm::vec3& delta) { void Avatar::measureMotionDerivatives(float deltaTime) { PerformanceTimer perfTimer("derivatives"); // linear - const float safeDeltaTime = (deltaTime == 0.0f) ? 0.001f : deltaTime; + const float MIN_DELTA_TIME = 0.001f; + const float safeDeltaTime = glm::max(deltaTime, MIN_DELTA_TIME); float invDeltaTime = 1.0f / safeDeltaTime; // Floating point error prevents us from computing velocity in a naive way // (e.g. vel = (pos - oldPos) / dt) so instead we use _positionOffsetAccumulator.