From 9e6472b57783c514e34121941956fcceed902cc1 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 6 Nov 2017 15:55:57 +0100 Subject: [PATCH] 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;