diff --git a/src/libRender/RadiumNBR/Passes/WireframePass.cpp b/src/libRender/RadiumNBR/Passes/WireframePass.cpp index 295ccb0dd0150aa0177fce81b174b6b239eecfbf..6fca85c6d3268c63d74b8b1c4b784978dfdca218 100644 --- a/src/libRender/RadiumNBR/Passes/WireframePass.cpp +++ b/src/libRender/RadiumNBR/Passes/WireframePass.cpp @@ -13,10 +13,8 @@ using namespace Ra::Core::Utils; // log #include <globjects/Framebuffer.h> -#ifdef ADVANCED_WIREFRAME -# include <Engine/Data/Mesh.hpp> -# include <Engine/Data/ViewingParameters.hpp> -#endif +#include <Engine/Data/Mesh.hpp> +#include <Engine/Data/ViewingParameters.hpp> namespace RadiumNBR { using namespace gl; @@ -47,96 +45,102 @@ void WireframePass::setOutput( const SharedTextures& colorBuffer ) { } bool WireframePass::update() { -#ifdef ADVANCED_WIREFRAME - // build the wireframe representation of all drawn objects - m_wireframes.clear(); - for ( const auto& ro : *m_objectsToRender ) + // keep the wireframe cache coherent with the scene + if ( m_wireframes.size() > m_objectsToRender->size() ) { m_wireframes.clear(); }; + return true; +} + +template <typename IndexContainerType> +void computeIndices( Ra::Core::Geometry::LineMesh::IndexContainerType& indices, + IndexContainerType& other ) { + + for ( const auto& index : other ) { - std::shared_ptr<Ra::Engine::Data::LineMesh> disp; - - using trimesh = Ra::Engine::Data::IndexedGeometry<Ra::Core::Geometry::TriangleMesh>; - using polymesh = Ra::Engine::Data::IndexedGeometry<Ra::Core::Geometry::PolyMesh>; - - auto displayable = ro->getMesh(); - auto tm = std::dynamic_pointer_cast<trimesh>( displayable ); - auto tp = std::dynamic_pointer_cast<polymesh>( displayable ); - bool drawable; - Ra::Core::Geometry::LineMesh lines; - Ra::Core::Geometry::LineMesh::IndexContainerType indices; - if ( tm ) + auto s = index.size(); + for ( unsigned int i = 0; i < s; ++i ) { - drawable = tm->getRenderMode() == - Ra::Engine::Data::AttribArrayDisplayable::MeshRenderMode::RM_TRIANGLES; - if ( drawable ) - { - lines.setVertices( tm->getCoreGeometry().vertices() ); - for ( const auto& index : tm->getCoreGeometry().getIndices() ) - { - for ( unsigned int i = 0; i < 3; ++i ) - { - int i1 = index[i]; - int i2 = index[( i + 1 ) % 3]; - if ( i1 > i2 ) std::swap( i1, i2 ); - indices.emplace_back( i1, i2 ); - } - } - } + int i1 = index[i]; + int i2 = index[( i + 1 ) % s]; + if ( i1 > i2 ) std::swap( i1, i2 ); + indices.emplace_back( i1, i2 ); } - if ( tp ) - { - drawable = tp->getRenderMode() == - Ra::Engine::Data::AttribArrayDisplayable::MeshRenderMode::RM_TRIANGLES; + } - if ( drawable ) - { - drawable = tp->getRenderMode() == - Ra::Engine::Data::AttribArrayDisplayable::MeshRenderMode::RM_TRIANGLES; - lines.setVertices( tp->getCoreGeometry().vertices() ); - for ( const auto& index : tp->getCoreGeometry().getIndices() ) - { - auto s = index.size(); - for ( unsigned int i = 0; i < s; ++i ) - { - int i1 = index[i]; - int i2 = index[( i + 1 ) % s]; - if ( i1 > i2 ) std::swap( i1, i2 ); - indices.emplace_back( i1, i2 ); - } - } - } - } - if ( drawable ) - { - std::sort( indices.begin(), - indices.end(), - []( const Ra::Core::Geometry::LineMesh::IndexType& a, - const Ra::Core::Geometry::LineMesh::IndexType& b ) { - return a[0] < b[0] || ( a[0] == b[0] && a[1] < b[1] ); - } ); - indices.erase( std::unique( indices.begin(), indices.end() ), indices.end() ); - - lines.setIndices( std::move( indices ) ); - - disp = std::make_shared<Ra::Engine::Data::LineMesh>( std::string( "wireframe" ), - std::move( lines ) ); - disp->updateGL(); - m_wireframes[ro.get()] = disp; - } - else - { - disp.reset(); - m_wireframes[ro.get()] = {nullptr}; - } + std::sort( indices.begin(), + indices.end(), + []( const Ra::Core::Geometry::LineMesh::IndexType& a, + const Ra::Core::Geometry::LineMesh::IndexType& b ) { + return a[0] < b[0] || ( a[0] == b[0] && a[1] < b[1] ); + } ); + indices.erase( std::unique( indices.begin(), indices.end() ), indices.end() ); +} + +// store LineMesh and Core, define the observer functor to update data one core update for wireframe +// linemesh +template <typename CoreGeometry> +class VerticesUpdater +{ + public: + VerticesUpdater( std::shared_ptr<Ra::Engine::Data::LineMesh> disp, CoreGeometry& core ) : + m_disp{disp}, m_core{core} {}; + + void operator()() { m_disp->getCoreGeometry().setVertices( m_core.vertices() ); } + std::shared_ptr<Ra::Engine::Data::LineMesh> m_disp; + CoreGeometry& m_core; +}; + +template <typename CoreGeometry> +class IndicesUpdater +{ + public: + IndicesUpdater( std::shared_ptr<Ra::Engine::Data::LineMesh> disp, CoreGeometry& core ) : + m_disp{disp}, m_core{core} {}; + + void operator()() { + auto lineIndices = m_disp->getCoreGeometry().getIndicesWithLock(); + computeIndices( lineIndices, m_core.getIndices() ); + m_disp->getCoreGeometry().indicesUnlock(); } -#endif - return true; + std::shared_ptr<Ra::Engine::Data::LineMesh> m_disp; + CoreGeometry& m_core; +}; + +// create a linemesh to draw wireframe given a core mesh +template <typename CoreGeometry> +void setupLineMesh( std::shared_ptr<Ra::Engine::Data::LineMesh>& disp, CoreGeometry& core ) { + Ra::Core::Geometry::LineMesh lines; + Ra::Core::Geometry::LineMesh::IndexContainerType indices; + lines.setVertices( core.vertices() ); + computeIndices( indices, core.getIndices() ); + if ( indices.size() > 0 ) + { + lines.setIndices( std::move( indices ) ); + disp = std::make_shared<Ra::Engine::Data::LineMesh>( std::string( "wireframe" ), + std::move( lines ) ); + disp->updateGL(); + // add observer + auto handle = core.template getAttribHandle<typename CoreGeometry::Point>( + Ra::Engine::Data::Mesh::getAttribName( Ra::Engine::Data::Mesh::VERTEX_POSITION ) ); + core.vertexAttribs().getAttrib( handle ).attach( VerticesUpdater( disp, core ) ); + core.attach( IndicesUpdater( disp, core ) ); + } + else + { disp.reset(); } +} + +template <typename CoreGeometry> +void processLineMesh( const std::shared_ptr<CoreGeometry>& m, + std::shared_ptr<Ra::Engine::Data::LineMesh>& r ) { + if ( m->getRenderMode() == + Ra::Engine::Data::AttribArrayDisplayable::MeshRenderMode::RM_TRIANGLES ) + { setupLineMesh( r, m->getCoreGeometry() ); } } void WireframePass::resize( size_t width, size_t height ) { -#ifdef ADVANCED_WIREFRAME + m_width = width; m_height = height; -#endif + // Only resize the owned textures. Imported one are resized by their owner for ( auto& t : m_localTextures ) { @@ -161,66 +165,61 @@ void WireframePass::resize( size_t width, size_t height ) { } void WireframePass::execute( const Ra::Engine::Data::ViewingParameters& viewParams ) const { - m_fbo->bind(); // only draw into 1 buffers (Color) - GL_ASSERT( glDrawBuffers( 1, buffers ) ); - GL_ASSERT( glDepthMask( GL_FALSE ) ); - GL_ASSERT( glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ) ); - - GL_ASSERT( glEnable( GL_DEPTH_TEST ) ); - GL_ASSERT( glDepthFunc( GL_LESS ) ); - GL_ASSERT( glEnable( GL_BLEND ) ); -#ifdef ADVANCED_WIREFRAME_SHADER + glDrawBuffers( 1, buffers ); + glDepthMask( GL_FALSE ); + glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); + glEnable( GL_DEPTH_TEST ); + glDepthFunc( GL_LESS ); + glEnable( GL_BLEND ); + glBlendEquationSeparate( GL_FUNC_ADD, GL_FUNC_ADD ); glBlendFuncSeparate( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO ); // To prevent z-fighting, remove polygon offset glDisable( GL_POLYGON_OFFSET_FILL ); -#else - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); - glEnable( GL_LINE_SMOOTH ); - glLineWidth( 1.f ); - glDisable( GL_POLYGON_OFFSET_LINE ); -#endif for ( const auto& ro : *m_objectsToRender ) { if ( ro->isVisible() ) { -#ifdef ADVANCED_WIREFRAME + std::shared_ptr<Ra::Engine::Data::Displayable> wro; + auto it = m_wireframes.find( ro.get() ); - if ( it != m_wireframes.end() ) + if ( it == m_wireframes.end() ) { - auto wro = it->second; - auto shader = m_shaderMngr->getShaderProgram( "WireframePass::WireframeProgram" ); - if ( wro && shader ) - { - shader->bind(); - Ra::Core::Matrix4 modelMatrix = ro->getTransformAsMatrix(); - shader->setUniform( "transform.proj", viewParams.projMatrix ); - shader->setUniform( "transform.view", viewParams.viewMatrix ); - shader->setUniform( "transform.model", modelMatrix ); - shader->setUniform( "viewport", Ra::Core::Vector2{m_width, m_height} ); - wro->render( shader ); - GL_CHECK_ERROR; - } + using trimesh = Ra::Engine::Data::IndexedGeometry<Ra::Core::Geometry::TriangleMesh>; + using polymesh = Ra::Engine::Data::IndexedGeometry<Ra::Core::Geometry::PolyMesh>; + std::shared_ptr<Ra::Engine::Data::LineMesh> disp; + auto displayable = ro->getMesh(); + auto tm = std::dynamic_pointer_cast<trimesh>( displayable ); + auto tp = std::dynamic_pointer_cast<polymesh>( displayable ); + if ( tm ) { processLineMesh( tm, disp ); } + if ( tp ) { processLineMesh( tp, disp ); } + m_wireframes[ro.get()] = disp; + wro = disp; } + else + { wro = it->second; } -#else - ro->render( m_passParams, viewParams, passIndex() ); -#endif + auto shader = m_shaderMngr->getShaderProgram( "WireframePass::WireframeProgram" ); + if ( wro && shader ) + { + wro->updateGL(); + shader->bind(); + Ra::Core::Matrix4 modelMatrix = ro->getTransformAsMatrix(); + shader->setUniform( "transform.proj", viewParams.projMatrix ); + shader->setUniform( "transform.view", viewParams.viewMatrix ); + shader->setUniform( "transform.model", modelMatrix ); + shader->setUniform( "viewport", Ra::Core::Vector2{m_width, m_height} ); + wro->render( shader ); + GL_CHECK_ERROR; + } } } -#ifdef ADVANCED_WIREFRAME_SHADER // need to maintain global state invariant glEnable( GL_POLYGON_OFFSET_FILL ); -#else - glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); - // do we need to enable this ? it is not part of the invariant ... - // glEnable( GL_POLYGON_OFFSET_LINE ); -#endif - GL_ASSERT( glDisable( GL_BLEND ) ); + glDisable( GL_BLEND ); } bool WireframePass::buildRenderTechnique( const Ra::Engine::Rendering::RenderObject* ro, @@ -235,7 +234,7 @@ bool WireframePass::buildRenderTechnique( const Ra::Engine::Rendering::RenderObj { rt.setConfiguration( *cfg, passIndex() ); } else { -#ifdef ADVANCED_WIREFRAME_SHADER + Ra::Engine::Data::ShaderConfiguration theConfig{ {"WireframePass::WireframeProgram"}, resourcesRootDir + "Shaders/WireframePass/Advanced/wireframe.vert.glsl", @@ -243,13 +242,6 @@ bool WireframePass::buildRenderTechnique( const Ra::Engine::Rendering::RenderObj theConfig.addShader( Ra::Engine::Data::ShaderType_GEOMETRY, resourcesRootDir + "Shaders/WireframePass/Advanced/wireframe.geom.glsl" ); -#else - // Build the shader configuration - Ra::Engine::Data::ShaderConfiguration theConfig{ - {"WireframePass::WireframeProgram"}, - resourcesRootDir + "Shaders/WireframePass/Basic/wireframepass.vert.glsl", - resourcesRootDir + "Shaders/WireframePass/Basic/wireframepass.frag.glsl"}; -#endif // Add to the ShaderConfigManager Ra::Engine::Data::ShaderConfigurationFactory::addConfiguration( theConfig ); // Add to the RenderTechnique diff --git a/src/libRender/RadiumNBR/Passes/WireframePass.hpp b/src/libRender/RadiumNBR/Passes/WireframePass.hpp index 919b6fa852061b30c8e744b0134d03d34e14cd44..61305c7c044d13cfdfddbb078856aaf245c7988d 100644 --- a/src/libRender/RadiumNBR/Passes/WireframePass.hpp +++ b/src/libRender/RadiumNBR/Passes/WireframePass.hpp @@ -2,13 +2,7 @@ #include <RadiumNBR/RenderPass.hpp> #include <Core/Utils/Color.hpp> - -// This is the default. The two way of rendering wireframe is kept for illustration only -#define ADVANCED_WIREFRAME -#define ADVANCED_WIREFRAME_SHADER -#ifdef ADVANCED_WIREFRAME -# include <Engine/Data/DisplayableObject.hpp> -#endif +#include <Engine/Data/DisplayableObject.hpp> namespace Ra::Engine::Data { class ShaderProgram; @@ -52,15 +46,13 @@ class WireframePass : public RenderPass /// The color texture for output.Stored here for easy access. SharedTextures m_outputTexture; -#ifdef ADVANCED_WIREFRAME using WireMap = std::map<Ra::Engine::Rendering::RenderObject*, std::shared_ptr<Ra::Engine::Data::Displayable>>; - WireMap m_wireframes; + mutable WireMap m_wireframes; bool m_wireframeAa{true}; Ra::Core::Utils::Color m_wireframeColor{Ra::Core::Utils::Color::White()}; uint m_width{0}; uint m_height{0}; -#endif }; } // namespace RadiumNBR