//################################################################################################## // // Custom Visualization Core library // Copyright (C) 2011-2012 Ceetron AS // // This library is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This library is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at <> // for more details. // //################################################################################################## #include "cvfBase.h" #include "cvfPartHighlighter.h" #include "cvfRenderSequence.h" #include "cvfFramebufferObject.h" #include "cvfRenderbufferObject.h" #include "cvfSampler.h" #include "cvfSingleQuadRenderingGenerator.h" #include "cvfRenderStateDepth.h" #include "cvfShaderSourceProvider.h" #include "cvfCamera.h" #include "cvfScene.h" #include "cvfModelBasicList.h" #include "cvfShaderProgramGenerator.h" #include "cvfShaderProgram.h" #include "cvfRenderStateStencil.h" #include "cvfEffect.h" #include "cvfUniform.h" #include "cvfGaussianBlur.h" #include "cvfRenderStateBlending.h" #include "cvfRenderStatePolygonMode.h" #include "cvfRenderStateLine.h" #include "cvfClipPlaneSet.h" namespace cvf { //================================================================================================== /// /// \class cvf::PartHighlighter /// \ingroup Viewing /// /// /// //================================================================================================== //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- PartHighlighter::PartHighlighter(Model* highlightModel, Camera* mainCamera) : m_highlightModel(highlightModel), m_mainCamera(mainCamera), m_highlightColor(Color3::WHITE) { m_highlightModel = highlightModel; m_mainCamera = mainCamera; //ref selectedColorTexture = new Texture(Texture::TEXTURE_RECTANGLE, Texture::RGBA); ref selectedColorTexture = new Texture(Texture::TEXTURE_2D, Texture::RGBA); selectedColorTexture->setSize(1, 1); m_drawFbo = new FramebufferObject; m_drawFbo->attachColorTexture2d(0, selectedColorTexture.p()); // Note that we clear alpha to 0 m_highlightCamera = new Camera; m_highlightCamera->viewport()->setClearColor(Color4f(Color3::GRAY, 0)); // Main 'solid' rendering m_drawRendering = new Rendering; m_drawRendering->setTargetFramebuffer(m_drawFbo.p()); m_drawRendering->setCamera(m_highlightCamera.p()); m_drawRendering->setScene(new Scene); m_drawRendering->scene()->addModel(m_highlightModel.p()); m_drawRendering->setRenderingName("PartHighlighter : Main 'solid' rendering (m_drawRendering)"); // Draw geometry again using lines to increase footprint of slim details bool growFootprintUsingLineDrawing = true; if (growFootprintUsingLineDrawing) { m_drawRenderingLines = new Rendering; m_drawRenderingLines->setTargetFramebuffer(m_drawFbo.p()); m_drawRenderingLines->setCamera(m_highlightCamera.p()); m_drawRenderingLines->setScene(new Scene); m_drawRenderingLines->scene()->addModel(m_highlightModel.p()); m_drawRenderingLines->setClearMode(Viewport::DO_NOT_CLEAR); m_drawRenderingLines->setRenderingName("PartHighlighter : Main 'solid' rendering (lines) (m_drawRenderingLines)"); } configureOverrideEffects(false); // Setup the blur helper // The sigma value her should be tuned to the line width used above // If sigma gets larger that the line width we will get artifacts during the blur pass m_blur = new GaussianBlur(selectedColorTexture.p(), 7, 2); // Mix rendering { ref sampler = new Sampler; sampler->setWrapMode(Sampler::CLAMP_TO_EDGE); sampler->setMinFilter(Sampler::LINEAR); sampler->setMagFilter(Sampler::LINEAR); ref rsBlending = new RenderStateBlending; rsBlending->configureTransparencyBlending(); m_highlightClrUniform = new UniformFloat("u_highlightColor", m_highlightColor); SingleQuadRenderingGenerator quadRenderGen("MixRendering"); //quadRenderGen.addTexture(selectedColorTexture.p(), sampler.p(), "u_texture2DRect"); quadRenderGen.addTexture(selectedColorTexture.p(), sampler.p(), "u_texture2D"); quadRenderGen.addFragmentShaderCode(ShaderSourceProvider::instance()->getSourceFromRepository(ShaderSourceRepository::fs_HighlightMix)); quadRenderGen.setRenderState(rsBlending.p()); quadRenderGen.setUniform(m_highlightClrUniform.p()); m_mixRendering = quadRenderGen.generate(); m_mixRendering->setClearMode(Viewport::DO_NOT_CLEAR); m_mixRendering->setRenderingName("PartHighlighter : Mix Rendering (m_mixRendering)"); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void PartHighlighter::setHighlightColor(const Color3f& color) { m_highlightColor = color; m_highlightClrUniform->set(m_highlightColor); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void PartHighlighter::addRenderingsToSequence(RenderSequence* renderSequence) { CVF_ASSERT(renderSequence); if (m_drawRendering.notNull()) { renderSequence->addRendering(m_drawRendering.p()); } if (m_drawRenderingLines.notNull()) { renderSequence->addRendering(m_drawRenderingLines.p()); } if (m_blur.notNull()) { m_blur->addRenderingsToSequence(renderSequence); } if (m_mixRendering.notNull()) { renderSequence->addRendering(m_mixRendering.p()); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void PartHighlighter::removeRenderingsFromSequence(RenderSequence* renderSequence) { CVF_ASSERT(renderSequence); if (m_drawRendering.notNull()) renderSequence->removeRendering(m_drawRendering.p()); if (m_drawRenderingLines.notNull()) renderSequence->removeRendering(m_drawRenderingLines.p()); if (m_mixRendering.notNull()) renderSequence->removeRendering(m_mixRendering.p()); if (m_blur.notNull()) { m_blur->removeRenderingsFromSequence(renderSequence); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void PartHighlighter::resize(int x, int y, uint width, uint height) { m_drawFbo->resizeAttachedBuffers(width, height); // Since draw and drawLines shares a camera, this call will set both m_drawRendering->camera()->setViewport(0, 0, width, height); // Mix rendering is the only "on-screen" rendering so here we do need to set the position of the viewport m_mixRendering->camera()->setViewport(x, y, width, height); if (m_blur.notNull()) { m_blur->resizeFromTextureSize(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void PartHighlighter::prepareForRedraw() { if (m_mainCamera->projection() == Camera::PERSPECTIVE) { m_highlightCamera->setProjectionAsPerspective(m_mainCamera->fieldOfViewYDeg(), m_mainCamera->nearPlane(), m_mainCamera->farPlane()); } else { m_highlightCamera->setProjectionAsOrtho(m_mainCamera->frontPlaneFrustumHeight(), m_mainCamera->nearPlane(), m_mainCamera->farPlane()); } m_highlightCamera->setViewMatrix(m_mainCamera->viewMatrix()); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void PartHighlighter::setClipPlaneSet(ClipPlaneSet* clipPlaneSet) { m_drawRendering->removeAllGlobalDynamicUniformSets(); if (m_drawRenderingLines.notNull()) { m_drawRenderingLines->removeAllGlobalDynamicUniformSets(); } if (clipPlaneSet) { m_drawRendering->addGlobalDynamicUniformSet(clipPlaneSet); if (m_drawRenderingLines.notNull()) { m_drawRenderingLines->addGlobalDynamicUniformSet(clipPlaneSet); } configureOverrideEffects(true); } else { configureOverrideEffects(false); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void PartHighlighter::configureOverrideEffects(bool useClipping) { ShaderProgramGenerator spGen("DrawHighlightGeo", ShaderSourceProvider::instance()); if (useClipping) { spGen.addVertexCode(cvf::ShaderSourceRepository::calcClipDistances); spGen.addFragmentCode(cvf::ShaderSourceRepository::checkDiscard_ClipDistances); } spGen.addVertexCode(ShaderSourceRepository::vs_Standard); spGen.addFragmentCode(ShaderSourceRepository::src_Color); spGen.addFragmentCode(ShaderSourceRepository::fs_Unlit); ref shaderProg = spGen.generate(); if (useClipping) { shaderProg->setDefaultUniform(new cvf::UniformInt( "u_clipPlaneCount", 0)); shaderProg->setDefaultUniform(new cvf::UniformFloat("u_ecClipPlanes", cvf::Vec4f(0, 0, 1, 0))); } ref eff = new Effect; eff->setShaderProgram(shaderProg.p()); // Use magenta to detect if something isn't working as it should eff->setUniform(new UniformFloat("u_color", Color4f(Color3::MAGENTA))); m_drawRendering->setEffectOverride(eff.p()); if (m_drawRenderingLines.notNull()) { ref eff = new Effect; eff->setShaderProgram(shaderProg.p()); eff->setRenderState(new RenderStatePolygonMode(RenderStatePolygonMode::LINE)); eff->setRenderState(new RenderStateLine(3)); // Use cyan to detect if something isn't working as it should eff->setUniform(new UniformFloat("u_color", Color4f(Color3::CYAN))); m_drawRenderingLines->setEffectOverride(eff.p()); } } //================================================================================================== /// /// \class cvf::PartHighlighterStencil /// \ingroup Viewing /// /// /// //================================================================================================== //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- PartHighlighterStencil::PartHighlighterStencil(Model* highlightModel, Camera* mainCamera) : m_highlightModel(highlightModel), m_mainCamera(mainCamera), m_highlightColor(Color3::WHITE) { m_highlightModel = highlightModel; m_mainCamera = mainCamera; // Selected rendering { ref depthStencilRBO = new RenderbufferObject(RenderbufferObject::DEPTH24_STENCIL8, 1, 1); ref selectedColorTexture = new Texture(Texture::TEXTURE_RECTANGLE, Texture::RGBA); selectedColorTexture->setSize(1, 1); m_drawFbo = new FramebufferObject; m_drawFbo->attachDepthStencilRenderbuffer(depthStencilRBO.p()); m_drawFbo->attachColorTexture2d(0, selectedColorTexture.p()); m_highlightCamera = new Camera; m_highlightCamera->viewport()->setClearColor(Color4f(0, 0, 0, 0)); m_drawRendering = new Rendering; m_drawRendering->setTargetFramebuffer(m_drawFbo.p()); m_drawRendering->setClearMode(Viewport::CLEAR_COLOR_DEPTH_STENCIL); m_drawRendering->setCamera(m_highlightCamera.p()); m_drawRendering->setScene(new Scene); m_drawRendering->scene()->addModel(m_highlightModel.p()); { ShaderProgramGenerator spGen("DrawSelected", ShaderSourceProvider::instance()); spGen.addVertexCode(ShaderSourceRepository::vs_Standard); spGen.addFragmentCode(ShaderSourceRepository::fs_HighlightStencilDraw); ref prog = spGen.generate(); ref rsStencil = new RenderStateStencil; rsStencil->setFunction(RenderStateStencil::ALWAYS, 1, 0xffffffff); rsStencil->setOperation(RenderStateStencil::KEEP, RenderStateStencil::REPLACE, RenderStateStencil::REPLACE); rsStencil->enableStencilTest(true); ref eff = new Effect; eff->setShaderProgram(prog.p()); eff->setUniform(new UniformFloat("u_color", Color4f(m_highlightColor))); eff->setRenderState(new RenderStateDepth(true, RenderStateDepth::LEQUAL)); eff->setRenderState(rsStencil.p()); m_drawRendering->setEffectOverride(eff.p()); } // Blur rendering { ref blurredColorTexture = new Texture(Texture::TEXTURE_RECTANGLE, Texture::RGBA); blurredColorTexture->setSize(1, 1); m_blurFbo = new FramebufferObject; m_blurFbo->attachDepthStencilRenderbuffer(depthStencilRBO.p()); m_blurFbo->attachColorTexture2d(0, blurredColorTexture.p()); SingleQuadRenderingGenerator quadRenderGen("BlurRendering"); { quadRenderGen.addFragmentShaderCode(ShaderSourceProvider::instance()->getSourceFromRepository(ShaderSourceRepository::fs_HighlightStencilBlur_v33)); ref sampler = new Sampler; sampler->setWrapMode(Sampler::CLAMP_TO_EDGE); sampler->setMinFilter(Sampler::NEAREST); sampler->setMagFilter(Sampler::NEAREST); quadRenderGen.addTexture(selectedColorTexture.p(), sampler.p(), "u_texture2DRect"); ref s = new RenderStateStencil; s->setFunction(RenderStateStencil::EQUAL, 0, 0xffffffff); s->enableStencilTest(true); quadRenderGen.setRenderState(s.p()); quadRenderGen.setRenderState(new RenderStateDepth(false, RenderStateDepth::LESS, false)); } m_blurRendering = quadRenderGen.generate(); m_blurRendering->setTargetFramebuffer(m_blurFbo.p()); m_blurRendering->setClearMode(Viewport::CLEAR_COLOR); m_blurRendering->camera()->viewport()->setClearColor(Color4f(0.0, 0.0, 0.0, 0)); // Mix rendering { ref sampler = new Sampler; sampler->setWrapMode(Sampler::CLAMP_TO_EDGE); sampler->setMinFilter(Sampler::NEAREST); sampler->setMagFilter(Sampler::NEAREST); SingleQuadRenderingGenerator quadRenderGen("MixRendering"); quadRenderGen.addTexture(blurredColorTexture.p(), sampler.p(), "u_texture2DRect"); quadRenderGen.addFragmentShaderCode(ShaderSourceProvider::instance()->getSourceFromRepository(ShaderSourceRepository::fs_HighlightStencilMix_v33)); m_mixRendering = quadRenderGen.generate(); m_mixRendering->setClearMode(Viewport::DO_NOT_CLEAR); } } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void PartHighlighterStencil::setHighlightColor(const Color3f& color) { m_highlightColor = color; applyHighlightColor(); } //-------------------------------------------------------------------------------------------------- /// Applies the currently set highlight color by modifying the override effect used in the draw pass //-------------------------------------------------------------------------------------------------- void PartHighlighterStencil::applyHighlightColor() { if (m_drawRendering.notNull()) { ref eff = m_drawRendering->effectOverride(); if (eff.notNull()) { eff->setUniform(new UniformFloat("u_color", Color4f(m_highlightColor))); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void PartHighlighterStencil::addRenderingsToSequence(RenderSequence* renderSequence) { CVF_ASSERT(renderSequence); if (m_drawRendering.notNull()) renderSequence->addRendering(m_drawRendering.p()); if (m_blurRendering.notNull()) renderSequence->addRendering(m_blurRendering.p()); if (m_mixRendering.notNull()) renderSequence->addRendering(m_mixRendering.p()); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void PartHighlighterStencil::removeRenderingsFromSequence(RenderSequence* renderSequence) { CVF_ASSERT(renderSequence); if (m_drawRendering.notNull()) renderSequence->removeRendering(m_drawRendering.p()); if (m_blurRendering.notNull()) renderSequence->removeRendering(m_blurRendering.p()); if (m_mixRendering.notNull()) renderSequence->removeRendering(m_mixRendering.p()); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void PartHighlighterStencil::resize(uint width, uint height) { m_drawFbo->resizeAttachedBuffers(width, height); m_blurFbo->resizeAttachedBuffers(width, height); m_drawRendering->camera()->setViewport(0, 0, width, height); m_blurRendering->camera()->setViewport(0, 0, width, height); m_mixRendering->camera()->setViewport(0, 0, width, height); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void PartHighlighterStencil::prepareForRedraw() { m_highlightCamera->setProjectionAsPerspective(m_mainCamera->fieldOfViewYDeg(), m_mainCamera->nearPlane(), m_mainCamera->farPlane()); m_highlightCamera->setViewMatrix(m_mainCamera->viewMatrix()); } }