From 4782a9ec74718fa10c66f95fd0341f09386aac71 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Tue, 31 Mar 2026 12:40:55 +0200 Subject: [PATCH 01/49] [render] add CrossSectionUniforms class --- .../uniforms/cross_section_uniforms.h | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h diff --git a/vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h b/vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h new file mode 100644 index 0000000000..438f17390b --- /dev/null +++ b/vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h @@ -0,0 +1,84 @@ +/***************************************************************************** + * VCLib * + * Visual Computing Library * + * * + * Copyright(C) 2021-2026 * + * Visual Computing Lab * + * ISTI - Italian National Research Council * + * * + * All rights reserved. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the Mozilla Public License Version 2.0 as published * + * by the Mozilla Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 * + * Mozilla Public License Version 2.0 * + * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * + ****************************************************************************/ + +#ifndef VCL_BGFX_DRAWABLE_UNIFORMS_CROSS_SECTION_UNIFORMS_H +#define VCL_BGFX_DRAWABLE_UNIFORMS_CROSS_SECTION_UNIFORMS_H + +#include + +#include + +namespace vcl { + +class CrossSectionUniforms +{ + inline static std::array sCrossSectionMinData = { + Point3f::min().x(), // x min + Point3f::min().y(), // y min + Point3f::min().z(), // z min + 0.0 // unused + }; + + inline static std::array sCrossSectionMaxData = { + Point3f::max().x(), // x max + Point3f::max().y(), // y max + Point3f::max().z(), // z max + 0.0 // unused + }; + + inline static Uniform sCrossSectionMinDataUniform; + inline static Uniform sCrossSectionMaxDataUniform; + +public: + CrossSectionUniforms() = delete; + + static void set( + const Point3f& minPoint = Point3f::min(), + const Point3f& maxPoint = Point3f::max()) + { + sCrossSectionMinData[0] = minPoint.x(); + sCrossSectionMinData[1] = minPoint.y(); + sCrossSectionMinData[2] = minPoint.z(); + + sCrossSectionMaxData[0] = maxPoint.x(); + sCrossSectionMaxData[1] = maxPoint.y(); + sCrossSectionMaxData[2] = maxPoint.z(); + } + + static void bind() + { + // lazy initialization + // to avoid creating uniforms before bgfx is initialized + if (!sCrossSectionMinDataUniform.isValid()) + sCrossSectionMinDataUniform = + Uniform("u_crossSectionMinData", bgfx::UniformType::Vec4); + if (!sCrossSectionMaxDataUniform.isValid()) + sCrossSectionMaxDataUniform = + Uniform("u_crossSectionMaxData", bgfx::UniformType::Vec4); + sCrossSectionMinDataUniform.bind(sCrossSectionMinData.data()); + sCrossSectionMaxDataUniform.bind(sCrossSectionMaxData.data()); + } +}; + +} // namespace vcl + +#endif // VCL_BGFX_DRAWABLE_UNIFORMS_CROSS_SECTION_UNIFORMS_H From 73726c76378c5d9bbf467fcb8b482b6fb47dfe6d Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Tue, 31 Mar 2026 12:57:27 +0200 Subject: [PATCH 02/49] [render] add CrossSectionUniforms shader header --- .../uniforms/cross_section_uniforms.sh | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 vclib/render/shaders/vclib/bgfx/drawable/uniforms/cross_section_uniforms.sh diff --git a/vclib/render/shaders/vclib/bgfx/drawable/uniforms/cross_section_uniforms.sh b/vclib/render/shaders/vclib/bgfx/drawable/uniforms/cross_section_uniforms.sh new file mode 100644 index 0000000000..861606d960 --- /dev/null +++ b/vclib/render/shaders/vclib/bgfx/drawable/uniforms/cross_section_uniforms.sh @@ -0,0 +1,37 @@ +/***************************************************************************** + * VCLib * + * Visual Computing Library * + * * + * Copyright(C) 2021-2026 * + * Visual Computing Lab * + * ISTI - Italian National Research Council * + * * + * All rights reserved. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the Mozilla Public License Version 2.0 as published * + * by the Mozilla Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 * + * Mozilla Public License Version 2.0 * + * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * + ****************************************************************************/ + +#ifndef VCL_EXT_BGFX_UNIFORMS_CROSS_SECTION_UNIFORMS_SH +#define VCL_EXT_BGFX_UNIFORMS_CROSS_SECTION_UNIFORMS_SH + +uniform vec4 u_crossSectionMinData; +uniform vec4 u_crossSectionMaxData; + +#define u_crossSectionMinX u_crossSectionMinData.x +#define u_crossSectionMinY u_crossSectionMinData.y +#define u_crossSectionMinZ u_crossSectionMinData.z + +#define u_crossSectionMaxX u_crossSectionMaxData.x +#define u_crossSectionMaxY u_crossSectionMaxData.y +#define u_crossSectionMaxZ u_crossSectionMaxData.z + +#endif // VCL_EXT_BGFX_UNIFORMS_CROSS_SECTION_UNIFORMS_SH From 45d28b31b5be8e9e449fb518e9bd5a61b47ad912 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Tue, 31 Mar 2026 12:57:43 +0200 Subject: [PATCH 03/49] [render] Add cross-section uniforms and controls to drawable mesh classes --- .../vclib/bgfx/drawable/drawable_mesh_bgfx.h | 11 ++++++++ .../render/drawable/abstract_drawable_mesh.h | 25 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h index fda0270da6..36a4e5af69 100644 --- a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h +++ b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -216,6 +217,16 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType DrawableMeshUniforms::setColor(*this); MeshRenderSettingsUniforms::set(mMRS); + if (AbstractDrawableMesh::mCrossSectionEnabled) { + CrossSectionUniforms::set( + AbstractDrawableMesh::mCrossSectionMin, + AbstractDrawableMesh::mCrossSectionMax); + } + else { + CrossSectionUniforms::set(); + } + CrossSectionUniforms::bind(); + if (mMRS.isSurface(MRI::Surface::VISIBLE)) { const PBRViewerSettings& pbrSettings = settings.pbrSettings; const DrawableEnvironment* env = settings.environment; diff --git a/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h b/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h index e2d921334c..af8cc356c9 100644 --- a/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h +++ b/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h @@ -46,6 +46,11 @@ class AbstractDrawableMesh : public vcl::DrawableObject Box3d mBoundingBox; + bool mCrossSectionEnabled = false; + + Point3f mCrossSectionMin = Point3f::min(); + Point3f mCrossSectionMax = Point3f::max(); + public: using MatIt = std::vector::const_iterator; @@ -60,6 +65,23 @@ class AbstractDrawableMesh : public vcl::DrawableObject const MeshRenderSettings& renderSettings() const { return mMRS; } + bool isCrossSectionEnabled() const { return mCrossSectionEnabled; } + + void setCrossSectionEnabled(bool enabled) + { + mCrossSectionEnabled = enabled; + } + + void setCrossSectionMin(const Point3f& minPoint) + { + mCrossSectionMin = minPoint; + } + + void setCrossSectionMax(const Point3f& maxPoint) + { + mCrossSectionMax = maxPoint; + } + virtual void updateBuffers( MeshRenderInfo::BuffersBitSet buffersToUpdate = MeshRenderInfo::BUFFERS_ALL) = 0; @@ -96,6 +118,9 @@ class AbstractDrawableMesh : public vcl::DrawableObject vcl::DrawableObject::swap(other); swap(mMRS, other.mMRS); swap(mBoundingBox, other.mBoundingBox); + swap(mCrossSectionEnabled, other.mCrossSectionEnabled); + swap(mCrossSectionMin, other.mCrossSectionMin); + swap(mCrossSectionMax, other.mCrossSectionMax); } // if the mesh does not have a bounding box, or if it has it but it is From 9409624efa5b2396873875bc121b98c0680638de Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Tue, 31 Mar 2026 15:26:40 +0200 Subject: [PATCH 04/49] [render] per vertex and per fragment cross section --- .../vclib/bgfx/drawable/drawable_mesh_bgfx.h | 6 ++++-- .../drawable/uniforms/cross_section_uniforms.h | 4 +++- .../render/drawable/abstract_drawable_mesh.h | 17 ++++++++++++----- .../drawable/uniforms/cross_section_uniforms.sh | 2 ++ 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h index 36a4e5af69..47f0b32cc2 100644 --- a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h +++ b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h @@ -217,10 +217,12 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType DrawableMeshUniforms::setColor(*this); MeshRenderSettingsUniforms::set(mMRS); - if (AbstractDrawableMesh::mCrossSectionEnabled) { + if (AbstractDrawableMesh::isCrossSectionEnabled()) { + using enum AbstractDrawableMesh::CrossSectionType; CrossSectionUniforms::set( AbstractDrawableMesh::mCrossSectionMin, - AbstractDrawableMesh::mCrossSectionMax); + AbstractDrawableMesh::mCrossSectionMax, + AbstractDrawableMesh::mCrossSectionType == PER_FRAGMENT); } else { CrossSectionUniforms::set(); diff --git a/vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h b/vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h index 438f17390b..c2b4217cec 100644 --- a/vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h +++ b/vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h @@ -53,11 +53,13 @@ class CrossSectionUniforms static void set( const Point3f& minPoint = Point3f::min(), - const Point3f& maxPoint = Point3f::max()) + const Point3f& maxPoint = Point3f::max(), + bool perFragment = false) { sCrossSectionMinData[0] = minPoint.x(); sCrossSectionMinData[1] = minPoint.y(); sCrossSectionMinData[2] = minPoint.z(); + sCrossSectionMinData[3] = float(perFragment); sCrossSectionMaxData[0] = maxPoint.x(); sCrossSectionMaxData[1] = maxPoint.y(); diff --git a/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h b/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h index af8cc356c9..e1bc4160d0 100644 --- a/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h +++ b/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h @@ -39,6 +39,10 @@ namespace vcl { */ class AbstractDrawableMesh : public vcl::DrawableObject { +public: + enum class CrossSectionType { NONE, PER_VERTEX, PER_FRAGMENT }; + +private: inline static const Image EMPTY_IMAGE; protected: @@ -46,7 +50,7 @@ class AbstractDrawableMesh : public vcl::DrawableObject Box3d mBoundingBox; - bool mCrossSectionEnabled = false; + CrossSectionType mCrossSectionType = CrossSectionType::NONE; Point3f mCrossSectionMin = Point3f::min(); Point3f mCrossSectionMax = Point3f::max(); @@ -65,11 +69,14 @@ class AbstractDrawableMesh : public vcl::DrawableObject const MeshRenderSettings& renderSettings() const { return mMRS; } - bool isCrossSectionEnabled() const { return mCrossSectionEnabled; } + bool isCrossSectionEnabled() const + { + return mCrossSectionType != CrossSectionType::NONE; + } - void setCrossSectionEnabled(bool enabled) + void setCrossSectionType(CrossSectionType type) { - mCrossSectionEnabled = enabled; + mCrossSectionType = type; } void setCrossSectionMin(const Point3f& minPoint) @@ -118,7 +125,7 @@ class AbstractDrawableMesh : public vcl::DrawableObject vcl::DrawableObject::swap(other); swap(mMRS, other.mMRS); swap(mBoundingBox, other.mBoundingBox); - swap(mCrossSectionEnabled, other.mCrossSectionEnabled); + swap(mCrossSectionType, other.mCrossSectionType); swap(mCrossSectionMin, other.mCrossSectionMin); swap(mCrossSectionMax, other.mCrossSectionMax); } diff --git a/vclib/render/shaders/vclib/bgfx/drawable/uniforms/cross_section_uniforms.sh b/vclib/render/shaders/vclib/bgfx/drawable/uniforms/cross_section_uniforms.sh index 861606d960..ce08426eda 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/uniforms/cross_section_uniforms.sh +++ b/vclib/render/shaders/vclib/bgfx/drawable/uniforms/cross_section_uniforms.sh @@ -26,6 +26,8 @@ uniform vec4 u_crossSectionMinData; uniform vec4 u_crossSectionMaxData; +#define u_crossSectionPerFragment bool(u_crossSectionMinData.w != 0.0) + #define u_crossSectionMinX u_crossSectionMinData.x #define u_crossSectionMinY u_crossSectionMinData.y #define u_crossSectionMinZ u_crossSectionMinData.z From 9207f49559d5697818fe1e979f5b95adb119dff2 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Tue, 31 Mar 2026 15:27:03 +0200 Subject: [PATCH 05/49] [render] implement cross section for drawable mesh surface --- .../drawable_mesh/surface/fs_surface_uber.sc | 15 ++++++++++++++- .../drawable_mesh/surface/varying.def.sc | 14 ++++++++------ .../drawable/drawable_mesh/surface/vs_surface.sc | 16 +++++++++++++++- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc index aa1c617415..491962e84b 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc @@ -20,10 +20,11 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include #include +#include #include #define primitiveID (u_firstChunkPrimitiveID + gl_PrimitiveID) @@ -33,6 +34,18 @@ BUFFER_RO(primitiveNormals, float, VCL_MRB_PRIMITIVE_NORMAL_BUFFER); // normal o void main() { + if (v_discardFlag > 0.0001) { // if vertex is marked as discard, discard the fragment + discard; + } + + // discard the fragment if it is outside the cross-section box + if (v_worldPos.x < u_crossSectionMinX || v_worldPos.x > u_crossSectionMaxX || + v_worldPos.y < u_crossSectionMinY || v_worldPos.y > u_crossSectionMaxY || + v_worldPos.z < u_crossSectionMinZ || v_worldPos.z > u_crossSectionMaxZ) + { + discard; + } + // depth offset - avoid z-fighting float depthOffset = 0.0; diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc index a369f1cdc6..c31ee4f3fc 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc @@ -5,9 +5,11 @@ vec4 a_color0 : COLOR0; vec2 a_texcoord0 : TEXCOORD0; vec2 a_texcoord1 : TEXCOORD1; -vec3 v_position : TEXCOORD2; // unused texcoord semantic -vec3 v_normal : NORMAL; -vec4 v_tangent : TANGENT; -vec4 v_color : COLOR0; -vec2 v_texcoord0 : TEXCOORD0; -vec2 v_texcoord1 : TEXCOORD1; +vec3 v_position : TEXCOORD2; // unused texcoord semantic +vec3 v_worldPos : TEXCOORD3; // unused texcoord semantic +float v_discardFlag : TEXCOORD4; // unused texcoord semantic +vec3 v_normal : NORMAL; +vec4 v_tangent : TANGENT; +vec4 v_color : COLOR0; +vec2 v_texcoord0 : TEXCOORD0; +vec2 v_texcoord1 : TEXCOORD1; diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc index 2ff4babf63..d54570be1d 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc @@ -21,14 +21,16 @@ ****************************************************************************/ $input a_position, a_normal, a_tangent, a_color0, a_texcoord0, a_texcoord1 -$output v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$output v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include +#include void main() { gl_Position = mul(u_modelViewProj, vec4(a_position, 1.0)); v_position = mul(u_modelView, vec4(a_position, 1.0)).xyz; + v_worldPos = mul(u_model[0], vec4(a_position, 1.0)).xyz; v_normal = normalize(mul(u_normalMatrix, a_normal)); v_texcoord0 = a_texcoord0; v_texcoord1 = a_texcoord1; @@ -37,4 +39,16 @@ void main() // default case - color is taken from buffer v_color = a_color0; + + // discard flag - used to discard the whole vertex, but in fragment shader + if (!u_crossSectionPerFragment) { + bool isOutside = v_worldPos.x < u_crossSectionMinX || v_worldPos.x > u_crossSectionMaxX || + v_worldPos.y < u_crossSectionMinY || v_worldPos.y > u_crossSectionMaxY || + v_worldPos.z < u_crossSectionMinZ || v_worldPos.z > u_crossSectionMaxZ; + + v_discardFlag = isOutside ? 1.0 : 0.0; + } + else { + v_discardFlag = 0.0; // no discard + } } From 16622eb27f7692ed86ae02afb318dcd657dc2ee5 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Wed, 1 Apr 2026 16:25:32 +0200 Subject: [PATCH 06/49] [render] Refactor cross section settings into CrossSectionSettings class and UI --- .../vclib/bgfx/drawable/drawable_mesh_bgfx.h | 10 +- .../render/drawable/abstract_drawable_mesh.h | 32 ++---- .../drawable/mesh/cross_section_settings.h | 101 ++++++++++++++++++ .../qt/gui/mesh_render_settings_frame.ui | 86 ++++++++++++--- 4 files changed, 186 insertions(+), 43 deletions(-) create mode 100644 vclib/render/include/vclib/render/drawable/mesh/cross_section_settings.h diff --git a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h index 47f0b32cc2..e975d4beed 100644 --- a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h +++ b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h @@ -217,12 +217,12 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType DrawableMeshUniforms::setColor(*this); MeshRenderSettingsUniforms::set(mMRS); - if (AbstractDrawableMesh::isCrossSectionEnabled()) { - using enum AbstractDrawableMesh::CrossSectionType; + if (AbstractDrawableMesh::mCSS.isEnabled()) { + using enum CrossSectionSettings::CrossSectionType; CrossSectionUniforms::set( - AbstractDrawableMesh::mCrossSectionMin, - AbstractDrawableMesh::mCrossSectionMax, - AbstractDrawableMesh::mCrossSectionType == PER_FRAGMENT); + AbstractDrawableMesh::mCSS.lower(), + AbstractDrawableMesh::mCSS.upper(), + AbstractDrawableMesh::mCSS.type() == PER_FRAGMENT); } else { CrossSectionUniforms::set(); diff --git a/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h b/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h index e1bc4160d0..77644a5c78 100644 --- a/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h +++ b/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h @@ -24,6 +24,7 @@ #define VCL_RENDER_DRAWABLE_ABSTRACT_DRAWABLE_MESH_H #include "drawable_object.h" +#include "mesh/cross_section_settings.h" #include "mesh/mesh_render_settings.h" #include @@ -47,14 +48,10 @@ class AbstractDrawableMesh : public vcl::DrawableObject protected: MeshRenderSettings mMRS; + CrossSectionSettings mCSS; Box3d mBoundingBox; - CrossSectionType mCrossSectionType = CrossSectionType::NONE; - - Point3f mCrossSectionMin = Point3f::min(); - Point3f mCrossSectionMax = Point3f::max(); - public: using MatIt = std::vector::const_iterator; @@ -63,30 +60,17 @@ class AbstractDrawableMesh : public vcl::DrawableObject AbstractDrawableMesh(const AbstractDrawableMesh& other) = default; template - AbstractDrawableMesh(const MeshType& m) : mMRS(m) + AbstractDrawableMesh(const MeshType& m) : mMRS(m), mCSS(m) { } const MeshRenderSettings& renderSettings() const { return mMRS; } - bool isCrossSectionEnabled() const - { - return mCrossSectionType != CrossSectionType::NONE; - } - - void setCrossSectionType(CrossSectionType type) - { - mCrossSectionType = type; - } - - void setCrossSectionMin(const Point3f& minPoint) - { - mCrossSectionMin = minPoint; - } + const CrossSectionSettings& crossSectionSettings() const { return mCSS; } - void setCrossSectionMax(const Point3f& maxPoint) + void setCrossSectionSettings(const CrossSectionSettings& css) { - mCrossSectionMax = maxPoint; + mCSS = css; } virtual void updateBuffers( @@ -124,10 +108,8 @@ class AbstractDrawableMesh : public vcl::DrawableObject using std::swap; vcl::DrawableObject::swap(other); swap(mMRS, other.mMRS); + swap(mCSS, other.mCSS); swap(mBoundingBox, other.mBoundingBox); - swap(mCrossSectionType, other.mCrossSectionType); - swap(mCrossSectionMin, other.mCrossSectionMin); - swap(mCrossSectionMax, other.mCrossSectionMax); } // if the mesh does not have a bounding box, or if it has it but it is diff --git a/vclib/render/include/vclib/render/drawable/mesh/cross_section_settings.h b/vclib/render/include/vclib/render/drawable/mesh/cross_section_settings.h new file mode 100644 index 0000000000..fd2769fe97 --- /dev/null +++ b/vclib/render/include/vclib/render/drawable/mesh/cross_section_settings.h @@ -0,0 +1,101 @@ +/***************************************************************************** + * VCLib * + * Visual Computing Library * + * * + * Copyright(C) 2021-2026 * + * Visual Computing Lab * + * ISTI - Italian National Research Council * + * * + * All rights reserved. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the Mozilla Public License Version 2.0 as published * + * by the Mozilla Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 * + * Mozilla Public License Version 2.0 * + * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * + ****************************************************************************/ + +#ifndef VCL_RENDER_DRAWABLE_MESH_CROSS_SECTION_SETTINGS_H +#define VCL_RENDER_DRAWABLE_MESH_CROSS_SECTION_SETTINGS_H + +#include +#include +#include + +namespace vcl { + +class CrossSectionSettings +{ +public: + enum class CrossSectionType { NONE, PER_VERTEX, PER_FRAGMENT }; + +private: + CrossSectionType mType = CrossSectionType::NONE; + + Box3f mBBox = Box3f(Point3f::min(), Point3f::max()); + + Point3f mLower = Point3f::min(); + Point3f mUpper = Point3f::max(); + +public: + CrossSectionSettings() = default; + + template + CrossSectionSettings(const MeshType& m) + { + mBBox = vcl::boundingBox(m).template cast(); + if constexpr (HasTransformMatrix) { + Matrix44f transform = m.transformMatrix().template cast(); + mBBox = transformBox(mBBox, transform); + } + mLower = mBBox.min(); + mUpper = mBBox.max(); + } + + bool isEnabled() const { return mType != CrossSectionType::NONE; } + + CrossSectionType& type() { return mType; } + + CrossSectionType type() const { return mType; } + + const Box3f& boundingBox() const { return mBBox; } + + const Point3f& lower() const { return mLower; } + + const Point3f& upper() const { return mUpper; } + + void setBoundingBox(const Box3f& bbox) + { + mBBox = bbox; + + if (mLower < bbox.min()) { + mLower = bbox.min(); + } + if (mUpper > bbox.max()) { + mUpper = bbox.max(); + } + } + + void setLower(const Point3f& lower) + { + if (mBBox.isInside(lower)) { + mLower = lower; + } + } + + void setUpper(const Point3f& upper) + { + if (mBBox.isInside(upper)) { + mUpper = upper; + } + } +}; + +} // namespace vcl + +#endif // VCL_RENDER_DRAWABLE_MESH_CROSS_SECTION_SETTINGS_H diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui index a5808003ff..7c536e55c4 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui @@ -19,19 +19,7 @@ Frame - - - 0 - - - 0 - - - 0 - - - 0 - + @@ -39,8 +27,80 @@ + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Plain + + + + + + false + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Per Vertex + + + true + + + crossSectionTypeButton + + + + + + + Per Fragment + + + crossSectionTypeButton + + + + + + + + + + Enable Cross Section + + + + + + + + + From ca6a604d66e0db1b767aeea856e53bcd5257a919 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Thu, 2 Apr 2026 08:50:47 +0200 Subject: [PATCH 07/49] [render] Fix input on surface fragment shaders --- .../generic_mesh_render_settings_frame.h | 2 +- .../drawable_mesh/surface/fs_surface_flat_color_face.sc | 2 +- .../drawable_mesh/surface/fs_surface_flat_color_mesh.sc | 2 +- .../drawable_mesh/surface/fs_surface_flat_color_user.sc | 2 +- .../drawable_mesh/surface/fs_surface_flat_color_vertex.sc | 2 +- .../drawable_mesh/surface/fs_surface_flat_tex_vertex.sc | 2 +- .../drawable/drawable_mesh/surface/fs_surface_flat_tex_wedge.sc | 2 +- .../drawable_mesh/surface/fs_surface_none_color_face.sc | 2 +- .../drawable_mesh/surface/fs_surface_none_color_mesh.sc | 2 +- .../drawable_mesh/surface/fs_surface_none_color_user.sc | 2 +- .../drawable_mesh/surface/fs_surface_none_color_vertex.sc | 2 +- .../drawable_mesh/surface/fs_surface_none_tex_vertex.sc | 2 +- .../drawable/drawable_mesh/surface/fs_surface_none_tex_wedge.sc | 2 +- .../drawable_mesh/surface/fs_surface_smooth_color_face.sc | 2 +- .../drawable_mesh/surface/fs_surface_smooth_color_mesh.sc | 2 +- .../drawable_mesh/surface/fs_surface_smooth_color_user.sc | 2 +- .../drawable_mesh/surface/fs_surface_smooth_color_vertex.sc | 2 +- .../drawable_mesh/surface/fs_surface_smooth_tex_vertex.sc | 2 +- .../drawable_mesh/surface/fs_surface_smooth_tex_wedge.sc | 2 +- .../bgfx/drawable/drawable_mesh/surface/fs_surface_uber_pbr.sc | 2 +- .../bgfx/drawable/drawable_mesh/surface/surface_uber_in.sh | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/generic_mesh_render_settings_frame.h b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/generic_mesh_render_settings_frame.h index 2edc071c5c..0f27c0318f 100644 --- a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/generic_mesh_render_settings_frame.h +++ b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/generic_mesh_render_settings_frame.h @@ -47,7 +47,7 @@ class GenericMeshRenderSettingsFrame : public QFrame virtual void updateFrameFromSettings() = 0; signals: - void settingsUpdated(); + void meshRenderSettingsUpdated(); protected: static void setButtonBackGround(QPushButton* b, const QColor& c); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_face.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_face.sc index 49fea687cb..59715944b0 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_face.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_face.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "flat.sh" #include "color_face.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_mesh.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_mesh.sc index d62d3580b6..e82658be79 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_mesh.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_mesh.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "flat.sh" #include "color_mesh.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_user.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_user.sc index d4339c7fe1..28e612c6de 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_user.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_user.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "flat.sh" #include "color_user.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_vertex.sc index fd47161067..075b1dedf2 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_vertex.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "flat.sh" #include "color_vertex.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_vertex.sc index 874ff0d8ee..a08e2d66e1 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_vertex.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "flat.sh" #include "tex_vertex.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_wedge.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_wedge.sc index acc9fd36cd..ca01b58446 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_wedge.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_wedge.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "flat.sh" #include "tex_wedge.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_face.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_face.sc index d8e5c5c8ae..f1da27f761 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_face.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_face.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "none.sh" #include "color_face.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_mesh.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_mesh.sc index d863948163..816ffef4bb 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_mesh.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_mesh.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "none.sh" #include "color_mesh.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_user.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_user.sc index fe3fe4e4cb..16a97f8b41 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_user.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_user.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "none.sh" #include "color_user.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_vertex.sc index c786fec50b..e5779a82ee 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_vertex.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "none.sh" #include "color_vertex.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_vertex.sc index 892f817851..730cb527db 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_vertex.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "none.sh" #include "tex_vertex.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_wedge.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_wedge.sc index 2bb8a1bba6..3255e57a5d 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_wedge.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_wedge.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "none.sh" #include "tex_wedge.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_face.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_face.sc index dc4d3b9758..75db62cd07 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_face.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_face.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "smooth.sh" #include "color_face.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_mesh.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_mesh.sc index e95c86eb1c..3c1b47a1c4 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_mesh.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_mesh.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "smooth.sh" #include "color_mesh.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_user.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_user.sc index 4ce79ec89d..928c90292b 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_user.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_user.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "smooth.sh" #include "color_user.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_vertex.sc index 087de81ca5..79a328a218 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_vertex.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "smooth.sh" #include "color_vertex.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_vertex.sc index 77d5102b52..7780305822 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_vertex.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "smooth.sh" #include "tex_vertex.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_wedge.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_wedge.sc index bf9b0577f0..136b595878 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_wedge.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_wedge.sc @@ -1,4 +1,4 @@ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include "smooth.sh" #include "tex_wedge.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber_pbr.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber_pbr.sc index cd95323eee..b9e0989ded 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber_pbr.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber_pbr.sc @@ -20,7 +20,7 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include #include diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface_uber_in.sh b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface_uber_in.sh index cfdbc3c9bd..00c8641fe3 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface_uber_in.sh +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface_uber_in.sh @@ -20,7 +20,7 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ -$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include #include From 907d79f4074b000c70e8cd09a7ffe69e379e69bd Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Thu, 2 Apr 2026 08:51:26 +0200 Subject: [PATCH 08/49] [render] Rename settingsUpdated signal to meshRenderSettingsUpdated throughout code --- .../vclib/qt/gui/mesh_render_settings_frame.h | 2 +- vclib/render/include/vclib/qt/mesh_viewer.h | 2 +- .../vclib/qt/gui/mesh_render_settings_frame.cpp | 5 ++++- .../mesh_render_settings_frame/edges_frame.cpp | 14 +++++++------- .../mesh_render_settings_frame/points_frame.cpp | 16 ++++++++-------- .../mesh_render_settings_frame/surface_frame.cpp | 12 ++++++------ .../wireframe_frame.cpp | 12 ++++++------ vclib/render/src/vclib/qt/mesh_viewer.cpp | 12 ++++++------ 8 files changed, 39 insertions(+), 36 deletions(-) diff --git a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h index 616a763423..055ae8e9ac 100644 --- a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h +++ b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h @@ -57,7 +57,7 @@ class MeshRenderSettingsFrame : public QFrame bool changeCurrentTab = false); signals: - void settingsUpdated(); + void meshRenderSettingsUpdated(); private: enum { POINTS_FRAME = 0, SURFACE_FRAME, WIREFRAME_FRAME, EDGES_FRAME }; diff --git a/vclib/render/include/vclib/qt/mesh_viewer.h b/vclib/render/include/vclib/qt/mesh_viewer.h index 46f4db8655..95ebd2d195 100644 --- a/vclib/render/include/vclib/qt/mesh_viewer.h +++ b/vclib/render/include/vclib/qt/mesh_viewer.h @@ -94,7 +94,7 @@ public slots: void selectedDrawableObjectChanged(uint i); - void renderSettingsUpdated(); + void meshRenderSettingsUpdated(); void fitScene(); diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp index d40622673a..a7e5cf6a97 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp @@ -57,7 +57,10 @@ MeshRenderSettingsFrame::MeshRenderSettingsFrame(QWidget* parent) : for (auto* frame : frames) { connect( - frame, SIGNAL(settingsUpdated()), this, SIGNAL(settingsUpdated())); + frame, + SIGNAL(meshRenderSettingsUpdated()), + this, + SIGNAL(meshRenderSettingsUpdated())); } } diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/edges_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/edges_frame.cpp index a184e8f8a8..215af93620 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/edges_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/edges_frame.cpp @@ -154,14 +154,14 @@ void EdgesFrame::updateColorComboBoxFromSettings() void EdgesFrame::onVisibilityChanged(Qt::CheckState arg1) { mMRS.setEdges(VISIBLE, arg1 == Qt::CheckState::Checked); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } void EdgesFrame::onShadingSmoothToggled(bool checked) { if (checked) { mMRS.setEdges(SHADING_SMOOTH); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } @@ -169,7 +169,7 @@ void EdgesFrame::onShadingFlatToggled(bool checked) { if (checked) { mMRS.setEdges(SHADING_FLAT); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } @@ -177,7 +177,7 @@ void EdgesFrame::onShadingNoneToggled(bool checked) { if (checked) { mMRS.setEdges(SHADING_NONE); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } @@ -190,19 +190,19 @@ void EdgesFrame::onColorComboBoxChanged(int index) case E_USER: mMRS.setEdges(COLOR_USER); break; } mUI->userColorFrame->setEnabled(index == E_USER); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } void EdgesFrame::onUserColorChanged(const QColor& c) { mMRS.setEdgesUserColor(c.redF(), c.greenF(), c.blueF(), c.alphaF()); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } void EdgesFrame::onSizeChanged(int value) { mMRS.setEdgesWidth(value); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } // namespace vcl::qt diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/points_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/points_frame.cpp index 6414d89c9f..649d796754 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/points_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/points_frame.cpp @@ -154,14 +154,14 @@ void PointsFrame::updateColorComboBoxFromSettings() void PointsFrame::onVisibilityChanged(Qt::CheckState arg1) { mMRS.setPoints(VISIBLE, arg1 == Qt::CheckState::Checked); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } void PointsFrame::onShapeCircleToggled(bool checked) { if (checked) { mMRS.setPoints(SHAPE_CIRCLE); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } @@ -169,7 +169,7 @@ void PointsFrame::onShapePixelToggled(bool checked) { if (checked) { mMRS.setPoints(SHAPE_PIXEL); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } @@ -177,7 +177,7 @@ void PointsFrame::onShadingVertexToggled(bool checked) { if (checked) { mMRS.setPoints(SHADING_VERT); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } @@ -185,7 +185,7 @@ void PointsFrame::onShadingNoneToggled(bool checked) { if (checked) { mMRS.setPoints(SHADING_NONE); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } @@ -197,19 +197,19 @@ void PointsFrame::onColorComboBoxChanged(int index) case P_USER: mMRS.setPoints(COLOR_USER); break; } mUI->userColorFrame->setEnabled(index == P_USER); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } void PointsFrame::onUserColorChanged(const QColor& c) { mMRS.setPointsUserColor(c.redF(), c.greenF(), c.blueF(), c.alphaF()); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } void PointsFrame::onSizeChanged(int value) { mMRS.setPointsWidth(value); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } // namespace vcl::qt diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/surface_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/surface_frame.cpp index 86996db711..4ee153c785 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/surface_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/surface_frame.cpp @@ -169,14 +169,14 @@ void SurfaceFrame::updateColorComboBoxFromSettings() void SurfaceFrame::onVisibilityChanged(Qt::CheckState arg1) { mMRS.setSurface(VISIBLE, arg1 == Qt::CheckState::Checked); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } void SurfaceFrame::onShadingSmoothToggled(bool checked) { if (checked) { mMRS.setSurface(SHADING_SMOOTH); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } @@ -184,7 +184,7 @@ void SurfaceFrame::onShadingFlatToggled(bool checked) { if (checked) { mMRS.setSurface(SHADING_FLAT); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } @@ -192,7 +192,7 @@ void SurfaceFrame::onShadingNoneToggled(bool checked) { if (checked) { mMRS.setSurface(SHADING_NONE); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } @@ -207,13 +207,13 @@ void SurfaceFrame::onColorComboBoxChanged(int index) case SC_USER: mMRS.setSurface(COLOR_USER); break; } mUI->userColorFrame->setEnabled(index == SC_USER); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } void SurfaceFrame::onUserColorChanged(const QColor& c) { mMRS.setSurfaceUserColor(c.redF(), c.greenF(), c.blueF(), c.alphaF()); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } // namespace vcl::qt diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/wireframe_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/wireframe_frame.cpp index 073ac80e63..1e5db68376 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/wireframe_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/wireframe_frame.cpp @@ -136,14 +136,14 @@ void WireframeFrame::updateColorComboBoxFromSettings() void WireframeFrame::onVisibilityChanged(Qt::CheckState arg1) { mMRS.setWireframe(VISIBLE, arg1 == Qt::CheckState::Checked); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } void WireframeFrame::onShadingVertexToggled(bool checked) { if (checked) { mMRS.setWireframe(SHADING_VERT); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } @@ -151,7 +151,7 @@ void WireframeFrame::onShadingNoneToggled(bool checked) { if (checked) { mMRS.setWireframe(SHADING_NONE); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } @@ -163,19 +163,19 @@ void WireframeFrame::onColorComboBoxChanged(int index) case W_USER: mMRS.setWireframe(COLOR_USER); break; } mUI->userColorFrame->setEnabled(index == W_USER); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } void WireframeFrame::onUserColorChanged(const QColor& c) { mMRS.setWireframeUserColor(c.redF(), c.greenF(), c.blueF(), c.alphaF()); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } void WireframeFrame::onSizeChanged(int value) { mMRS.setWireframeWidth(value); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } // namespace vcl::qt diff --git a/vclib/render/src/vclib/qt/mesh_viewer.cpp b/vclib/render/src/vclib/qt/mesh_viewer.cpp index d3c7c94543..a429f6b6d2 100644 --- a/vclib/render/src/vclib/qt/mesh_viewer.cpp +++ b/vclib/render/src/vclib/qt/mesh_viewer.cpp @@ -88,12 +88,12 @@ MeshViewer::MeshViewer(QWidget* parent) : mUI->viewerRenderSettingsFrame->setViewer(mUI->viewer); // each time that the RenderSettingsFrame updates its settings, we call the - // renderSettingsUpdated() member function + // meshRenderSettingsUpdated() member function connect( mUI->meshRenderSettingsFrame, - SIGNAL(settingsUpdated()), + SIGNAL(meshRenderSettingsUpdated()), this, - SLOT(renderSettingsUpdated())); + SLOT(meshRenderSettingsUpdated())); // each time that the drawVectorTree changes the visibility of an object, // we update the current settings of the RenderSettingsFrame, and we update @@ -248,13 +248,13 @@ void MeshViewer::selectedDrawableObjectChanged(uint i) /** * @brief Slot called every time that the MeshRenderSettingsFrame emits - * 'settingsUpdated()', that is when the user changes render settings of a - * GeneriDrawableMesh. + * 'meshRenderSettingsUpdated()', that is when the user changes render settings + * of a GeneriDrawableMesh. * * We need to get the selected GeneriDrawableMesh first, and then update the * settings to it. */ -void MeshViewer::renderSettingsUpdated() +void MeshViewer::meshRenderSettingsUpdated() { // The user changed the RenderSettings of the ith object. uint i = mUI->drawVectorTree->selectedDrawableObject(); From 6462ce105f8a2dddc56d9a56ae6c25c23551c99b Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Thu, 2 Apr 2026 09:34:56 +0200 Subject: [PATCH 09/49] [render] CrossSectionSettingsFrame class --- .../vclib/qt/gui/mesh_render_settings_frame.h | 5 ++ .../cross_section_settings_frame.h | 66 +++++++++++++++ .../qt/gui/mesh_render_settings_frame.cpp | 12 +++ .../qt/gui/mesh_render_settings_frame.ui | 73 +++-------------- .../cross_section_settings_frame.cpp | 65 +++++++++++++++ .../cross_section_settings_frame.ui | 81 +++++++++++++++++++ vclib/render/src/vclib/qt/mesh_viewer.cpp | 12 ++- 7 files changed, 249 insertions(+), 65 deletions(-) create mode 100644 vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h create mode 100644 vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp create mode 100644 vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui diff --git a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h index 055ae8e9ac..92e9b2bdb6 100644 --- a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h +++ b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h @@ -25,6 +25,7 @@ #include "mesh_render_settings_frame/generic_mesh_render_settings_frame.h" +#include #include #include @@ -52,10 +53,14 @@ class MeshRenderSettingsFrame : public QFrame const MeshRenderSettings& meshRenderSettings() const; + const CrossSectionSettings& crossSectionSettings() const; + void setMeshRenderSettings( const MeshRenderSettings& settings, bool changeCurrentTab = false); + void setCrossSectionSettings(const CrossSectionSettings& settings); + signals: void meshRenderSettingsUpdated(); diff --git a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h new file mode 100644 index 0000000000..50bba3988d --- /dev/null +++ b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h @@ -0,0 +1,66 @@ +/***************************************************************************** + * VCLib * + * Visual Computing Library * + * * + * Copyright(C) 2021-2026 * + * Visual Computing Lab * + * ISTI - Italian National Research Council * + * * + * All rights reserved. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the Mozilla Public License Version 2.0 as published * + * by the Mozilla Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 * + * Mozilla Public License Version 2.0 * + * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * + ****************************************************************************/ + +#ifndef VCL_QT_GUI_MESH_RENDER_SETTINGS_FRAME_CROSS_SECTION_SETTINGS_FRAME_H +#define VCL_QT_GUI_MESH_RENDER_SETTINGS_FRAME_CROSS_SECTION_SETTINGS_FRAME_H + +#include + +#include + +namespace vcl::qt { + +namespace Ui { +class CrossSectionSettingsFrame; +} // namespace Ui + +class CrossSectionSettingsFrame : public QFrame +{ + Q_OBJECT + + Ui::CrossSectionSettingsFrame* mUI; + + CrossSectionSettings mCSS; + +public: + explicit CrossSectionSettingsFrame( + const CrossSectionSettings css, + QWidget* parent = nullptr); + + explicit CrossSectionSettingsFrame(QWidget* parent = nullptr); + + ~CrossSectionSettingsFrame(); + + const CrossSectionSettings& crossSectionSettings() const; + + void setCrossSectionSettings(const CrossSectionSettings& settings); + +signals: + void crossSectionSettingsUpdated(); + +private: + void updateFrameFromSettings(); +}; + +} // namespace vcl::qt + +#endif // VCL_QT_GUI_MESH_RENDER_SETTINGS_FRAME_CROSS_SECTION_SETTINGS_FRAME_H diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp index a7e5cf6a97..c169928c1d 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp @@ -74,6 +74,12 @@ const MeshRenderSettings& MeshRenderSettingsFrame::meshRenderSettings() const return mMRS; } +const CrossSectionSettings& MeshRenderSettingsFrame::crossSectionSettings() + const +{ + return mUI->crossSectionFrame->crossSectionSettings(); +} + void MeshRenderSettingsFrame::setMeshRenderSettings( const MeshRenderSettings& settings, bool changeCurrentTab /*= false*/) @@ -82,6 +88,12 @@ void MeshRenderSettingsFrame::setMeshRenderSettings( updateGuiFromSettings(changeCurrentTab); } +void MeshRenderSettingsFrame::setCrossSectionSettings( + const CrossSectionSettings& settings) +{ + mUI->crossSectionFrame->setCrossSectionSettings(settings); +} + void MeshRenderSettingsFrame::updateGuiFromSettings(bool changeCurrentTab) { using MRI = MeshRenderInfo; diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui index 7c536e55c4..4a3fc920b5 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui @@ -28,79 +28,26 @@ - + QFrame::Shape::NoFrame QFrame::Shadow::Plain - - - - - false - - - QFrame::Shape::NoFrame - - - QFrame::Shadow::Plain - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Per Vertex - - - true - - - crossSectionTypeButton - - - - - - - Per Fragment - - - crossSectionTypeButton - - - - - - - - - - Enable Cross Section - - - - + + + + vcl::qt::CrossSectionSettingsFrame + QFrame +
vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h
+ 1 +
+
- - - diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp new file mode 100644 index 0000000000..fce73af3ca --- /dev/null +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp @@ -0,0 +1,65 @@ +/***************************************************************************** + * VCLib * + * Visual Computing Library * + * * + * Copyright(C) 2021-2026 * + * Visual Computing Lab * + * ISTI - Italian National Research Council * + * * + * All rights reserved. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the Mozilla Public License Version 2.0 as published * + * by the Mozilla Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 * + * Mozilla Public License Version 2.0 * + * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * + ****************************************************************************/ + +#include + +#include "ui_cross_section_settings_frame.h" + +namespace vcl::qt { + +CrossSectionSettingsFrame::CrossSectionSettingsFrame( + const CrossSectionSettings css, + QWidget* parent) : + QFrame(parent), mUI(new Ui::CrossSectionSettingsFrame), mCSS(css) +{ + mUI->setupUi(this); + updateFrameFromSettings(); +} + +CrossSectionSettingsFrame::CrossSectionSettingsFrame(QWidget* parent) : + CrossSectionSettingsFrame(CrossSectionSettings(), parent) +{ +} + +CrossSectionSettingsFrame::~CrossSectionSettingsFrame() +{ + delete mUI; +} + +const CrossSectionSettings& CrossSectionSettingsFrame::crossSectionSettings() + const +{ + return mCSS; +} + +void CrossSectionSettingsFrame::setCrossSectionSettings( + const CrossSectionSettings& settings) +{ + mCSS = settings; + updateFrameFromSettings(); +} + +void CrossSectionSettingsFrame::updateFrameFromSettings() +{ +} + +} // namespace vcl::qt diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui new file mode 100644 index 0000000000..4cf982d410 --- /dev/null +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui @@ -0,0 +1,81 @@ + + + vcl::qt::CrossSectionSettingsFrame + + + + 0 + 0 + 400 + 300 + + + + Frame + + + + + + Enable Cross Section + + + + + + + false + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Per Vertex + + + true + + + crossSectionTypeButton + + + + + + + Per Fragment + + + crossSectionTypeButton + + + + + + + + + + + + + + diff --git a/vclib/render/src/vclib/qt/mesh_viewer.cpp b/vclib/render/src/vclib/qt/mesh_viewer.cpp index a429f6b6d2..daa948c85f 100644 --- a/vclib/render/src/vclib/qt/mesh_viewer.cpp +++ b/vclib/render/src/vclib/qt/mesh_viewer.cpp @@ -215,6 +215,8 @@ void MeshViewer::visibilityDrawableObjectChanged() if (m) { mUI->meshRenderSettingsFrame->setMeshRenderSettings( m->renderSettings()); + mUI->meshRenderSettingsFrame->setCrossSectionSettings( + m->crossSectionSettings()); } mUI->viewer->update(); } @@ -238,6 +240,8 @@ void MeshViewer::selectedDrawableObjectChanged(uint i) mUI->meshRenderSettingsFrame->setMeshRenderSettings( m->renderSettings()); mUI->meshRenderSettingsFrame->setEnabled(true); + mUI->meshRenderSettingsFrame->setCrossSectionSettings( + m->crossSectionSettings()); } else { // it is not a AbstractDrawableMesh, RenderSettingsFrame must be @@ -249,9 +253,9 @@ void MeshViewer::selectedDrawableObjectChanged(uint i) /** * @brief Slot called every time that the MeshRenderSettingsFrame emits * 'meshRenderSettingsUpdated()', that is when the user changes render settings - * of a GeneriDrawableMesh. + * of an AbstractDrawableMesh. * - * We need to get the selected GeneriDrawableMesh first, and then update the + * We need to get the selected AbstractDrawableMesh first, and then update the * settings to it. */ void MeshViewer::meshRenderSettingsUpdated() @@ -268,6 +272,8 @@ void MeshViewer::meshRenderSettingsUpdated() // AbstractDrawableMesh m->setRenderSettings( mUI->meshRenderSettingsFrame->meshRenderSettings()); + m->setCrossSectionSettings( + mUI->meshRenderSettingsFrame->crossSectionSettings()); mUI->viewer->update(); } } @@ -297,6 +303,8 @@ void MeshViewer::updateGUI() mUI->meshRenderSettingsFrame->setMeshRenderSettings( m->renderSettings(), true); mUI->meshRenderSettingsFrame->setEnabled(true); + mUI->meshRenderSettingsFrame->setCrossSectionSettings( + m->crossSectionSettings()); } else { mUI->meshRenderSettingsFrame->setEnabled(false); From aba1c42d823e510d28fa52d4663765e99fece796 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Thu, 2 Apr 2026 09:53:49 +0200 Subject: [PATCH 10/49] [render] Add cross-section controls and signal handlers to settings frame --- .../cross_section_settings_frame.h | 5 ++ .../qt/gui/mesh_render_settings_frame.cpp | 6 ++ .../qt/gui/mesh_render_settings_frame.ui | 1 - .../cross_section_settings_frame.cpp | 69 +++++++++++++++++++ .../cross_section_settings_frame.ui | 6 +- 5 files changed, 83 insertions(+), 4 deletions(-) diff --git a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h index 50bba3988d..b104aeee5c 100644 --- a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h +++ b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h @@ -59,6 +59,11 @@ class CrossSectionSettingsFrame : public QFrame private: void updateFrameFromSettings(); + +private slots: + void onCrossSectionEnabledChanged(Qt::CheckState arg1); + void onPerVertexToggled(bool checked); + void onPerFragmentToggled(bool checked); }; } // namespace vcl::qt diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp index c169928c1d..72379c0f81 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp @@ -62,6 +62,12 @@ MeshRenderSettingsFrame::MeshRenderSettingsFrame(QWidget* parent) : this, SIGNAL(meshRenderSettingsUpdated())); } + + connect( + mUI->crossSectionFrame, + SIGNAL(crossSectionSettingsUpdated()), + this, + SIGNAL(meshRenderSettingsUpdated())); } MeshRenderSettingsFrame::~MeshRenderSettingsFrame() diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui index 4a3fc920b5..6eefbd7d3e 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui @@ -35,7 +35,6 @@ QFrame::Shadow::Plain - diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp index fce73af3ca..76cb91b636 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp @@ -33,6 +33,24 @@ CrossSectionSettingsFrame::CrossSectionSettingsFrame( { mUI->setupUi(this); updateFrameFromSettings(); + + connect( + mUI->crosSectionEnabledCheckBox, + SIGNAL(checkStateChanged(Qt::CheckState)), + this, + SLOT(onCrossSectionEnabledChanged(Qt::CheckState))); + + connect( + mUI->perVertexRadioButton, + SIGNAL(toggled(bool)), + this, + SLOT(onPerVertexToggled(bool))); + + connect( + mUI->perFragmentRadioButton, + SIGNAL(toggled(bool)), + this, + SLOT(onPerFragmentToggled(bool))); } CrossSectionSettingsFrame::CrossSectionSettingsFrame(QWidget* parent) : @@ -60,6 +78,57 @@ void CrossSectionSettingsFrame::setCrossSectionSettings( void CrossSectionSettingsFrame::updateFrameFromSettings() { + using enum CrossSectionSettings::CrossSectionType; + + // radio buttons + if (mCSS.type() == PER_VERTEX) + mUI->perVertexRadioButton->setChecked(true); + else if (mCSS.type() == PER_FRAGMENT) + mUI->perFragmentRadioButton->setChecked(true); + + // checkbox, last thing to do + if (mCSS.type() == NONE) + mUI->crosSectionEnabledCheckBox->setCheckState( + Qt::CheckState::Unchecked); + else + mUI->crosSectionEnabledCheckBox->setCheckState(Qt::CheckState::Checked); +} + +void CrossSectionSettingsFrame::onCrossSectionEnabledChanged( + Qt::CheckState arg1) +{ + using enum CrossSectionSettings::CrossSectionType; + + if (arg1 != Qt::CheckState::Checked) { + mCSS.type() = NONE; + } + else { + if (mUI->perVertexRadioButton->isChecked()) + mCSS.type() = PER_VERTEX; + else + mCSS.type() = PER_FRAGMENT; + } + + mUI->controlsFrame->setEnabled(arg1 == Qt::CheckState::Checked); + emit crossSectionSettingsUpdated(); +} + +void CrossSectionSettingsFrame::onPerVertexToggled(bool checked) +{ + using enum CrossSectionSettings::CrossSectionType; + if (checked) { + mCSS.type() = PER_VERTEX; + emit crossSectionSettingsUpdated(); + } +} + +void CrossSectionSettingsFrame::onPerFragmentToggled(bool checked) +{ + using enum CrossSectionSettings::CrossSectionType; + if (checked) { + mCSS.type() = PER_FRAGMENT; + emit crossSectionSettingsUpdated(); + } } } // namespace vcl::qt diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui index 4cf982d410..5ee7b13c9d 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui @@ -54,7 +54,7 @@ true - crossSectionTypeButton + crossSectionTypeButtonGroup @@ -64,7 +64,7 @@ Per Fragment - crossSectionTypeButton + crossSectionTypeButtonGroup @@ -76,6 +76,6 @@ - + From b0ecf7250c5e6b13a331cd9701c71c05698e4266 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Thu, 2 Apr 2026 11:38:09 +0200 Subject: [PATCH 11/49] [render] add X slider to CrossSectionFrame --- .../vclib/bgfx/drawable/drawable_mesh_bgfx.h | 1 + .../vclib/qt/gui/mesh_render_settings_frame.h | 2 + .../cross_section_settings_frame.h | 2 + vclib/render/include/vclib/qt/mesh_viewer.h | 2 + .../drawable/mesh/cross_section_settings.h | 7 ++ .../drawable_mesh/surface/fs_surface_uber.sc | 2 +- .../qt/gui/mesh_render_settings_frame.cpp | 2 +- .../cross_section_settings_frame.cpp | 41 +++++++ .../cross_section_settings_frame.ui | 101 ++++++++++++++++++ vclib/render/src/vclib/qt/mesh_viewer.cpp | 24 ++++- 10 files changed, 181 insertions(+), 3 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h index e975d4beed..1b95a99026 100644 --- a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h +++ b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h @@ -138,6 +138,7 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType mMRB.update(*this, buffersToUpdate); mMRS.setRenderCapabilityFrom(*this); setRenderSettings(mMRS); + mCSS = CrossSectionSettings(*this); } void setRenderSettings(const MeshRenderSettings& rs) override diff --git a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h index 92e9b2bdb6..8938762374 100644 --- a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h +++ b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h @@ -64,6 +64,8 @@ class MeshRenderSettingsFrame : public QFrame signals: void meshRenderSettingsUpdated(); + void crossSectionSettingsUpdated(); + private: enum { POINTS_FRAME = 0, SURFACE_FRAME, WIREFRAME_FRAME, EDGES_FRAME }; diff --git a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h index b104aeee5c..22f350e7b2 100644 --- a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h +++ b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h @@ -64,6 +64,8 @@ private slots: void onCrossSectionEnabledChanged(Qt::CheckState arg1); void onPerVertexToggled(bool checked); void onPerFragmentToggled(bool checked); + void onXFloatRangeSliderLowerValueChanged(float value); + void onXFloatRangeSliderUpperValueChanged(float value); }; } // namespace vcl::qt diff --git a/vclib/render/include/vclib/qt/mesh_viewer.h b/vclib/render/include/vclib/qt/mesh_viewer.h index 95ebd2d195..67e5b365e8 100644 --- a/vclib/render/include/vclib/qt/mesh_viewer.h +++ b/vclib/render/include/vclib/qt/mesh_viewer.h @@ -96,6 +96,8 @@ public slots: void meshRenderSettingsUpdated(); + void crossSectionSettingsUpdated(); + void fitScene(); void fitView(); diff --git a/vclib/render/include/vclib/render/drawable/mesh/cross_section_settings.h b/vclib/render/include/vclib/render/drawable/mesh/cross_section_settings.h index fd2769fe97..2eef9000a0 100644 --- a/vclib/render/include/vclib/render/drawable/mesh/cross_section_settings.h +++ b/vclib/render/include/vclib/render/drawable/mesh/cross_section_settings.h @@ -53,6 +53,13 @@ class CrossSectionSettings Matrix44f transform = m.transformMatrix().template cast(); mBBox = transformBox(mBBox, transform); } + + // inflate bbox by epsilon of its diagonal + float eps = mBBox.diagonal() * 0.02f; + + mBBox.min() -= eps; + mBBox.max() += eps; + mLower = mBBox.min(); mUpper = mBBox.max(); } diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc index 491962e84b..3c2121492c 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc @@ -34,7 +34,7 @@ BUFFER_RO(primitiveNormals, float, VCL_MRB_PRIMITIVE_NORMAL_BUFFER); // normal o void main() { - if (v_discardFlag > 0.0001) { // if vertex is marked as discard, discard the fragment + if (v_discardFlag > 0.0) { // if vertex is marked as discard, discard the fragment discard; } diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp index 72379c0f81..c0129cbd77 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp @@ -67,7 +67,7 @@ MeshRenderSettingsFrame::MeshRenderSettingsFrame(QWidget* parent) : mUI->crossSectionFrame, SIGNAL(crossSectionSettingsUpdated()), this, - SIGNAL(meshRenderSettingsUpdated())); + SIGNAL(crossSectionSettingsUpdated())); } MeshRenderSettingsFrame::~MeshRenderSettingsFrame() diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp index 76cb91b636..b62f92f739 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp @@ -51,6 +51,18 @@ CrossSectionSettingsFrame::CrossSectionSettingsFrame( SIGNAL(toggled(bool)), this, SLOT(onPerFragmentToggled(bool))); + + connect( + mUI->xFloatRangeSlider, + SIGNAL(lowerValueChanged(float)), + this, + SLOT(onXFloatRangeSliderLowerValueChanged(float))); + + connect( + mUI->xFloatRangeSlider, + SIGNAL(upperValueChanged(float)), + this, + SLOT(onXFloatRangeSliderUpperValueChanged(float))); } CrossSectionSettingsFrame::CrossSectionSettingsFrame(QWidget* parent) : @@ -86,6 +98,17 @@ void CrossSectionSettingsFrame::updateFrameFromSettings() else if (mCSS.type() == PER_FRAGMENT) mUI->perFragmentRadioButton->setChecked(true); + mUI->xFloatRangeSlider->blockSignals(true); + auto bb = mCSS.boundingBox(); + mUI->xFloatRangeSlider->setRange(bb.min().x(), bb.max().x()); + + auto l = mCSS.lower(); + auto u = mCSS.upper(); + + mUI->xFloatRangeSlider->setLowerValue(l.x()); + mUI->xFloatRangeSlider->setUpperValue(u.x()); + mUI->xFloatRangeSlider->blockSignals(false); + // checkbox, last thing to do if (mCSS.type() == NONE) mUI->crosSectionEnabledCheckBox->setCheckState( @@ -131,4 +154,22 @@ void CrossSectionSettingsFrame::onPerFragmentToggled(bool checked) } } +void CrossSectionSettingsFrame::onXFloatRangeSliderLowerValueChanged( + float value) +{ + auto l = mCSS.lower(); + l.x() = value; + mCSS.setLower(l); + emit crossSectionSettingsUpdated(); +} + +void CrossSectionSettingsFrame::onXFloatRangeSliderUpperValueChanged( + float value) +{ + auto u = mCSS.upper(); + u.x() = value; + mCSS.setUpper(u); + emit crossSectionSettingsUpdated(); +} + } // namespace vcl::qt diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui index 5ee7b13c9d..cfeaace0c4 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui @@ -68,11 +68,112 @@ + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Y: + + + + + + + + 0 + 0 + + + + Z: + + + + + + + + 0 + 0 + + + + X: + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + + + + + + vcl::qt::FloatRangeSlider + QWidget +
vclib/qt/gui/float_range_slider.h
+ 1 +
+
diff --git a/vclib/render/src/vclib/qt/mesh_viewer.cpp b/vclib/render/src/vclib/qt/mesh_viewer.cpp index daa948c85f..51a97f0fbe 100644 --- a/vclib/render/src/vclib/qt/mesh_viewer.cpp +++ b/vclib/render/src/vclib/qt/mesh_viewer.cpp @@ -95,6 +95,12 @@ MeshViewer::MeshViewer(QWidget* parent) : this, SLOT(meshRenderSettingsUpdated())); + connect( + mUI->meshRenderSettingsFrame, + SIGNAL(crossSectionSettingsUpdated()), + this, + SLOT(crossSectionSettingsUpdated())); + // each time that the drawVectorTree changes the visibility of an object, // we update the current settings of the RenderSettingsFrame, and we update // the glArea @@ -239,9 +245,9 @@ void MeshViewer::selectedDrawableObjectChanged(uint i) // set it enabled mUI->meshRenderSettingsFrame->setMeshRenderSettings( m->renderSettings()); - mUI->meshRenderSettingsFrame->setEnabled(true); mUI->meshRenderSettingsFrame->setCrossSectionSettings( m->crossSectionSettings()); + mUI->meshRenderSettingsFrame->setEnabled(true); } else { // it is not a AbstractDrawableMesh, RenderSettingsFrame must be @@ -272,6 +278,22 @@ void MeshViewer::meshRenderSettingsUpdated() // AbstractDrawableMesh m->setRenderSettings( mUI->meshRenderSettingsFrame->meshRenderSettings()); + mUI->viewer->update(); + } +} + +void MeshViewer::crossSectionSettingsUpdated() +{ + // The user changed the CrossSectionSettings of the ith object. + uint i = mUI->drawVectorTree->selectedDrawableObject(); + if (i != UINT_NULL && mDrawableObjectVector->size() > 0) { + // The selected object must always be a AbstractDrawableMesh, because + // the CrossSectionSettingsFrame (which called this member function) is + // visible only when the selected Object is a AbstractDrawableMesh + auto m = std::dynamic_pointer_cast( + mDrawableObjectVector->at(i)); + // get CrossSectionSettings from the CrossSectionSettingsFrame, and set + // it to the AbstractDrawableMesh m->setCrossSectionSettings( mUI->meshRenderSettingsFrame->crossSectionSettings()); mUI->viewer->update(); From 1d11f09c3f965eaab616353b4db887a5f40b5d60 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Thu, 2 Apr 2026 14:20:18 +0200 Subject: [PATCH 12/49] [render] CrossSectionFrame x slider working as expected --- .../cross_section_settings_frame.h | 12 +++ .../cross_section_settings_frame.cpp | 82 +++++++++++++++++-- .../cross_section_settings_frame.ui | 6 ++ 3 files changed, 94 insertions(+), 6 deletions(-) diff --git a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h index 22f350e7b2..cde77d54d5 100644 --- a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h +++ b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h @@ -23,8 +23,10 @@ #ifndef VCL_QT_GUI_MESH_RENDER_SETTINGS_FRAME_CROSS_SECTION_SETTINGS_FRAME_H #define VCL_QT_GUI_MESH_RENDER_SETTINGS_FRAME_CROSS_SECTION_SETTINGS_FRAME_H +#include #include +#include #include namespace vcl::qt { @@ -59,6 +61,14 @@ class CrossSectionSettingsFrame : public QFrame private: void updateFrameFromSettings(); + void updateSlider( + FloatRangeSlider* slider, + QDoubleSpinBox* minSpinBox, + QDoubleSpinBox* maxSpinBox, + float min, + float max, + float lower, + float upper); private slots: void onCrossSectionEnabledChanged(Qt::CheckState arg1); @@ -66,6 +76,8 @@ private slots: void onPerFragmentToggled(bool checked); void onXFloatRangeSliderLowerValueChanged(float value); void onXFloatRangeSliderUpperValueChanged(float value); + void onXMinSpinBoxValueChanged(double value); + void onXMaxSpinBoxValueChanged(double value); }; } // namespace vcl::qt diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp index b62f92f739..4aeea0f44a 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp @@ -63,6 +63,18 @@ CrossSectionSettingsFrame::CrossSectionSettingsFrame( SIGNAL(upperValueChanged(float)), this, SLOT(onXFloatRangeSliderUpperValueChanged(float))); + + connect( + mUI->xMinSpinBox, + SIGNAL(valueChanged(double)), + this, + SLOT(onXMinSpinBoxValueChanged(double))); + + connect( + mUI->xMaxSpinBox, + SIGNAL(valueChanged(double)), + this, + SLOT(onXMaxSpinBoxValueChanged(double))); } CrossSectionSettingsFrame::CrossSectionSettingsFrame(QWidget* parent) : @@ -98,16 +110,18 @@ void CrossSectionSettingsFrame::updateFrameFromSettings() else if (mCSS.type() == PER_FRAGMENT) mUI->perFragmentRadioButton->setChecked(true); - mUI->xFloatRangeSlider->blockSignals(true); auto bb = mCSS.boundingBox(); - mUI->xFloatRangeSlider->setRange(bb.min().x(), bb.max().x()); - auto l = mCSS.lower(); auto u = mCSS.upper(); - mUI->xFloatRangeSlider->setLowerValue(l.x()); - mUI->xFloatRangeSlider->setUpperValue(u.x()); - mUI->xFloatRangeSlider->blockSignals(false); + updateSlider( + mUI->xFloatRangeSlider, + mUI->xMinSpinBox, + mUI->xMaxSpinBox, + bb.min().x(), + bb.max().x(), + l.x(), + u.x()); // checkbox, last thing to do if (mCSS.type() == NONE) @@ -117,6 +131,40 @@ void CrossSectionSettingsFrame::updateFrameFromSettings() mUI->crosSectionEnabledCheckBox->setCheckState(Qt::CheckState::Checked); } +void CrossSectionSettingsFrame::updateSlider( + FloatRangeSlider* slider, + QDoubleSpinBox* minSpinBox, + QDoubleSpinBox* maxSpinBox, + float min, + float max, + float lower, + float upper) +{ + slider->blockSignals(true); + + slider->setRange(min, max); + slider->setLowerValue(lower); + slider->setUpperValue(upper); + + slider->blockSignals(false); + + minSpinBox->blockSignals(true); + + minSpinBox->setMinimum(min); + minSpinBox->setMaximum(upper); + minSpinBox->setValue(lower); + + minSpinBox->blockSignals(false); + + maxSpinBox->blockSignals(true); + + maxSpinBox->setMinimum(lower); + maxSpinBox->setMaximum(max); + maxSpinBox->setValue(upper); + + maxSpinBox->blockSignals(false); +} + void CrossSectionSettingsFrame::onCrossSectionEnabledChanged( Qt::CheckState arg1) { @@ -160,6 +208,12 @@ void CrossSectionSettingsFrame::onXFloatRangeSliderLowerValueChanged( auto l = mCSS.lower(); l.x() = value; mCSS.setLower(l); + + mUI->xMinSpinBox->blockSignals(true); + mUI->xMinSpinBox->setValue(value); + mUI->xMinSpinBox->blockSignals(false); + mUI->xMaxSpinBox->setMinimum(value); + emit crossSectionSettingsUpdated(); } @@ -169,7 +223,23 @@ void CrossSectionSettingsFrame::onXFloatRangeSliderUpperValueChanged( auto u = mCSS.upper(); u.x() = value; mCSS.setUpper(u); + + mUI->xMaxSpinBox->blockSignals(true); + mUI->xMaxSpinBox->setValue(value); + mUI->xMaxSpinBox->blockSignals(false); + mUI->xMinSpinBox->setMaximum(value); + emit crossSectionSettingsUpdated(); } +void CrossSectionSettingsFrame::onXMinSpinBoxValueChanged(double value) +{ + mUI->xFloatRangeSlider->setLowerValue(static_cast(value)); +} + +void CrossSectionSettingsFrame::onXMaxSpinBoxValueChanged(double value) +{ + mUI->xFloatRangeSlider->setUpperValue(static_cast(value)); +} + } // namespace vcl::qt diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui index cfeaace0c4..b4e886d97f 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui @@ -97,6 +97,9 @@ 0 + + 0.010000000000000 + @@ -146,6 +149,9 @@ 0 + + 0.010000000000000 + From 5720c7df07df98adcebe6a3da7f098dcbc7b9085 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Thu, 2 Apr 2026 14:46:59 +0200 Subject: [PATCH 13/49] [render] CrossSectionSettingsFrame working --- .../cross_section_settings_frame.h | 15 ++- .../cross_section_settings_frame.cpp | 121 +++++++++++------- .../cross_section_settings_frame.ui | 58 ++++++--- 3 files changed, 121 insertions(+), 73 deletions(-) diff --git a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h index cde77d54d5..ceddd08f82 100644 --- a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h +++ b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h @@ -39,8 +39,14 @@ class CrossSectionSettingsFrame : public QFrame { Q_OBJECT + enum Axis {X= 0, Y, Z, AXIS_COUNT}; + Ui::CrossSectionSettingsFrame* mUI; + std::array mFloatSliders; + std::array mMinSpinBoxes; + std::array mMaxSpinBoxes; + CrossSectionSettings mCSS; public: @@ -74,10 +80,11 @@ private slots: void onCrossSectionEnabledChanged(Qt::CheckState arg1); void onPerVertexToggled(bool checked); void onPerFragmentToggled(bool checked); - void onXFloatRangeSliderLowerValueChanged(float value); - void onXFloatRangeSliderUpperValueChanged(float value); - void onXMinSpinBoxValueChanged(double value); - void onXMaxSpinBoxValueChanged(double value); + + void onFloatRangeSliderLowerValueChanged(Axis axis, float value); + void onFloatRangeSliderUpperValueChanged(Axis axis, float value); + void onMinSpinBoxValueChanged(Axis axis, double value); + void onMaxSpinBoxValueChanged(Axis axis, double value); }; } // namespace vcl::qt diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp index 4aeea0f44a..6ba9fd029a 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp @@ -32,6 +32,19 @@ CrossSectionSettingsFrame::CrossSectionSettingsFrame( QFrame(parent), mUI(new Ui::CrossSectionSettingsFrame), mCSS(css) { mUI->setupUi(this); + + mFloatSliders[X] = mUI->xFloatRangeSlider; + mFloatSliders[Y] = mUI->yFloatRangeSlider; + mFloatSliders[Z] = mUI->zFloatRangeSlider; + + mMinSpinBoxes[X] = mUI->xMinSpinBox; + mMinSpinBoxes[Y] = mUI->yMinSpinBox; + mMinSpinBoxes[Z] = mUI->zMinSpinBox; + + mMaxSpinBoxes[X] = mUI->xMaxSpinBox; + mMaxSpinBoxes[Y] = mUI->yMaxSpinBox; + mMaxSpinBoxes[Z] = mUI->zMaxSpinBox; + updateFrameFromSettings(); connect( @@ -52,29 +65,37 @@ CrossSectionSettingsFrame::CrossSectionSettingsFrame( this, SLOT(onPerFragmentToggled(bool))); - connect( - mUI->xFloatRangeSlider, - SIGNAL(lowerValueChanged(float)), - this, - SLOT(onXFloatRangeSliderLowerValueChanged(float))); - - connect( - mUI->xFloatRangeSlider, - SIGNAL(upperValueChanged(float)), - this, - SLOT(onXFloatRangeSliderUpperValueChanged(float))); - - connect( - mUI->xMinSpinBox, - SIGNAL(valueChanged(double)), - this, - SLOT(onXMinSpinBoxValueChanged(double))); - - connect( - mUI->xMaxSpinBox, - SIGNAL(valueChanged(double)), - this, - SLOT(onXMaxSpinBoxValueChanged(double))); + for (uint i = X; i < AXIS_COUNT; ++i) { + connect( + mFloatSliders[i], + &FloatRangeSlider::lowerValueChanged, + [this, i](float value) { + onFloatRangeSliderLowerValueChanged( + static_cast(i), value); + }); + + connect( + mFloatSliders[i], + &FloatRangeSlider::upperValueChanged, + [this, i](float value) { + onFloatRangeSliderUpperValueChanged( + static_cast(i), value); + }); + + connect( + mMinSpinBoxes[i], + &QDoubleSpinBox::valueChanged, + [this, i](double value) { + onMinSpinBoxValueChanged(static_cast(i), value); + }); + + connect( + mMaxSpinBoxes[i], + &QDoubleSpinBox::valueChanged, + [this, i](double value) { + onMaxSpinBoxValueChanged(static_cast(i), value); + }); + } } CrossSectionSettingsFrame::CrossSectionSettingsFrame(QWidget* parent) : @@ -114,14 +135,16 @@ void CrossSectionSettingsFrame::updateFrameFromSettings() auto l = mCSS.lower(); auto u = mCSS.upper(); - updateSlider( - mUI->xFloatRangeSlider, - mUI->xMinSpinBox, - mUI->xMaxSpinBox, - bb.min().x(), - bb.max().x(), - l.x(), - u.x()); + for (uint i = X; i < AXIS_COUNT; ++i) { + updateSlider( + mFloatSliders[i], + mMinSpinBoxes[i], + mMaxSpinBoxes[i], + bb.min()[i], + bb.max()[i], + l[i], + u[i]); + } // checkbox, last thing to do if (mCSS.type() == NONE) @@ -202,44 +225,44 @@ void CrossSectionSettingsFrame::onPerFragmentToggled(bool checked) } } -void CrossSectionSettingsFrame::onXFloatRangeSliderLowerValueChanged( - float value) +void CrossSectionSettingsFrame::onFloatRangeSliderLowerValueChanged( + Axis axis, float value) { auto l = mCSS.lower(); - l.x() = value; + l[axis] = value; mCSS.setLower(l); - mUI->xMinSpinBox->blockSignals(true); - mUI->xMinSpinBox->setValue(value); - mUI->xMinSpinBox->blockSignals(false); - mUI->xMaxSpinBox->setMinimum(value); + mMinSpinBoxes[axis]->blockSignals(true); + mMinSpinBoxes[axis]->setValue(value); + mMinSpinBoxes[axis]->blockSignals(false); + mMaxSpinBoxes[axis]->setMinimum(value); emit crossSectionSettingsUpdated(); } -void CrossSectionSettingsFrame::onXFloatRangeSliderUpperValueChanged( - float value) +void CrossSectionSettingsFrame::onFloatRangeSliderUpperValueChanged( + Axis axis, float value) { auto u = mCSS.upper(); - u.x() = value; + u[axis] = value; mCSS.setUpper(u); - mUI->xMaxSpinBox->blockSignals(true); - mUI->xMaxSpinBox->setValue(value); - mUI->xMaxSpinBox->blockSignals(false); - mUI->xMinSpinBox->setMaximum(value); + mMaxSpinBoxes[axis]->blockSignals(true); + mMaxSpinBoxes[axis]->setValue(value); + mMaxSpinBoxes[axis]->blockSignals(false); + mMinSpinBoxes[axis]->setMaximum(value); emit crossSectionSettingsUpdated(); } -void CrossSectionSettingsFrame::onXMinSpinBoxValueChanged(double value) +void CrossSectionSettingsFrame::onMinSpinBoxValueChanged(Axis axis, double value) { - mUI->xFloatRangeSlider->setLowerValue(static_cast(value)); + mFloatSliders[axis]->setLowerValue(static_cast(value)); } -void CrossSectionSettingsFrame::onXMaxSpinBoxValueChanged(double value) +void CrossSectionSettingsFrame::onMaxSpinBoxValueChanged(Axis axis, double value) { - mUI->xFloatRangeSlider->setUpperValue(static_cast(value)); + mFloatSliders[axis]->setUpperValue(static_cast(value)); } } // namespace vcl::qt diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui index b4e886d97f..c7f73e816d 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui @@ -89,6 +89,19 @@ 0 + + + + + 0 + 0 + + + + + + + @@ -102,7 +115,10 @@ - + + + + @@ -115,19 +131,22 @@ - - + + - + 0 0 - - Z: + + 0.010000000000000 + + + @@ -141,28 +160,27 @@ - - + + - + 0 0 - - 0.010000000000000 + + Z: - - - - - 0 - 0 - - - + + + + + + + + From efdaa00ed83bae1a0403021d178671987f924af4 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 10 Apr 2026 14:54:43 +0200 Subject: [PATCH 14/49] [render] Refactor cross-section discard logic into reusable shader macros --- .../drawable_mesh/surface/fs_surface_uber.sc | 12 +----- .../drawable_mesh/surface/vs_surface.sc | 11 +----- .../uniforms/cross_section_uniforms.sh | 39 +++++++++++++++++++ 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc index 3c2121492c..7764af51ec 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc @@ -34,17 +34,7 @@ BUFFER_RO(primitiveNormals, float, VCL_MRB_PRIMITIVE_NORMAL_BUFFER); // normal o void main() { - if (v_discardFlag > 0.0) { // if vertex is marked as discard, discard the fragment - discard; - } - - // discard the fragment if it is outside the cross-section box - if (v_worldPos.x < u_crossSectionMinX || v_worldPos.x > u_crossSectionMaxX || - v_worldPos.y < u_crossSectionMinY || v_worldPos.y > u_crossSectionMaxY || - v_worldPos.z < u_crossSectionMinZ || v_worldPos.z > u_crossSectionMaxZ) - { - discard; - } + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); // depth offset - avoid z-fighting float depthOffset = 0.0; diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc index d54570be1d..603c968ddf 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc @@ -41,14 +41,5 @@ void main() v_color = a_color0; // discard flag - used to discard the whole vertex, but in fragment shader - if (!u_crossSectionPerFragment) { - bool isOutside = v_worldPos.x < u_crossSectionMinX || v_worldPos.x > u_crossSectionMaxX || - v_worldPos.y < u_crossSectionMinY || v_worldPos.y > u_crossSectionMaxY || - v_worldPos.z < u_crossSectionMinZ || v_worldPos.z > u_crossSectionMaxZ; - - v_discardFlag = isOutside ? 1.0 : 0.0; - } - else { - v_discardFlag = 0.0; // no discard - } + v_discardFlag = computeDiscardFlag(v_worldPos); } diff --git a/vclib/render/shaders/vclib/bgfx/drawable/uniforms/cross_section_uniforms.sh b/vclib/render/shaders/vclib/bgfx/drawable/uniforms/cross_section_uniforms.sh index ce08426eda..f11a5bb1a8 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/uniforms/cross_section_uniforms.sh +++ b/vclib/render/shaders/vclib/bgfx/drawable/uniforms/cross_section_uniforms.sh @@ -36,4 +36,43 @@ uniform vec4 u_crossSectionMaxData; #define u_crossSectionMaxY u_crossSectionMaxData.y #define u_crossSectionMaxZ u_crossSectionMaxData.z +/** + * Given the world position of the vertex, compute and returns the discard flag + * for the cross section test (1.0 if vertex should be discarded, 0.0 otherwise) + * + * This macro is meant to be used in the vertex shader. The result of this test + * can be passed to the fragment shader as a varying, so that the fragment + * shader can also discard fragments based on the same test + * + * pos: the world position of the vertex (vec3) + * returns: the discard flag (float) + */ +#define computeDiscardFlag(pos) ( \ + (!u_crossSectionPerFragment && ( \ + (pos).x < u_crossSectionMinX || (pos).x > u_crossSectionMaxX || \ + (pos).y < u_crossSectionMinY || (pos).y > u_crossSectionMaxY || \ + (pos).z < u_crossSectionMinZ || (pos).z > u_crossSectionMaxZ \ + )) ? 1.0 : 0.0 \ +) + +/** + * Given the discard flag computed by computeDiscardFlag and the world position + * of the fragment, discard the fragment if the flag is greater than 0.0 or if + * the fragment is outside the cross section bounds (if + * u_crossSectionPerFragment is true). + * + * This macro is meant to be used in the fragment shader. + * + * flag: the discard flag computed by computeDiscardFlag and passed as a varying + * from the vertex shader (float) + * pos: the world position of the fragment (vec3) + */ +#define discardIfCrossSectionClipped(flag, pos) \ + if (flag > 0.0) discard; \ + else if (u_crossSectionPerFragment && ( \ + (pos).x < u_crossSectionMinX || (pos).x > u_crossSectionMaxX || \ + (pos).y < u_crossSectionMinY || (pos).y > u_crossSectionMaxY || \ + (pos).z < u_crossSectionMinZ || (pos).z > u_crossSectionMaxZ \ + )) discard; + #endif // VCL_EXT_BGFX_UNIFORMS_CROSS_SECTION_UNIFORMS_SH From e6e2f7b9e85fb9ccbc17958e44a86dcb9a524fe1 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 10 Apr 2026 15:30:29 +0200 Subject: [PATCH 15/49] [render] Add cross-section clipping to mesh surface pbr and surface ID shaders --- .../vclib/bgfx/drawable/drawable_mesh_bgfx.h | 13 ++++++++++++- .../drawable_mesh/surface/fs_surface_uber_pbr.sc | 7 +++++-- .../drawable_mesh/surface/surface_uber_in.sh | 6 ++++-- .../drawable_mesh/surface_id/fs_surface_id.sc | 5 +++++ .../drawable_mesh/surface_id/varying.def.sc | 5 ++++- .../drawable_mesh/surface_id/vs_surface_id.sc | 7 +++++++ 6 files changed, 37 insertions(+), 6 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h index 1b95a99026..f45e3d1a51 100644 --- a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h +++ b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h @@ -228,7 +228,6 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType else { CrossSectionUniforms::set(); } - CrossSectionUniforms::bind(); if (mMRS.isSurface(MRI::Surface::VISIBLE)) { const PBRViewerSettings& pbrSettings = settings.pbrSettings; @@ -351,6 +350,17 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType model = MeshType::transformMatrix().template cast(); } + if (AbstractDrawableMesh::mCSS.isEnabled()) { + using enum CrossSectionSettings::CrossSectionType; + CrossSectionUniforms::set( + AbstractDrawableMesh::mCSS.lower(), + AbstractDrawableMesh::mCSS.upper(), + AbstractDrawableMesh::mCSS.type() == PER_FRAGMENT); + } + else { + CrossSectionUniforms::set(); + } + if (mMRS.isSurface(MRI::Surface::VISIBLE)) { mMRB.bindVertexBuffers(mMRS); mMRB.bindIndexBuffers(mMRS); @@ -440,6 +450,7 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType { MeshRenderSettingsUniforms::bind(); DrawableMeshUniforms::bind(); + CrossSectionUniforms::bind(); } /** diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber_pbr.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber_pbr.sc index b9e0989ded..b25bd18c26 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber_pbr.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber_pbr.sc @@ -22,14 +22,15 @@ $input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +#include + #include +#include #include #include #include -#include - #define primitiveID (u_firstChunkPrimitiveID + gl_PrimitiveID) BUFFER_RO(primitiveColors, uint, VCL_MRB_PRIMITIVE_COLOR_BUFFER); // color of each face / edge @@ -40,6 +41,8 @@ SAMPLERCUBE(s_specular, VCL_MRB_CUBEMAP1); void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + // texcoord to use bool useTexture = bool(u_surfaceMode & posToBitFlag(VCL_MRS_SURF_TEX_VERTEX)) || diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface_uber_in.sh b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface_uber_in.sh index 00c8641fe3..787a8e8241 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface_uber_in.sh +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface_uber_in.sh @@ -23,9 +23,9 @@ $input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 #include -#include - #include +#include +#include #define primitiveID (u_firstChunkPrimitiveID + gl_PrimitiveID) @@ -34,6 +34,8 @@ BUFFER_RO(primitiveNormals, float, VCL_MRB_PRIMITIVE_NORMAL_BUFFER); // normal o void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + // depth offset - avoid z-fighting float depthOffset = 0.0; diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/fs_surface_id.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/fs_surface_id.sc index e5f18faa30..b89bee2244 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/fs_surface_id.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/fs_surface_id.sc @@ -20,12 +20,17 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ +$input v_worldPos, v_discardFlag + #include +#include #include void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + /***** render ID to color ******/ vec4 color = uintABGRToVec4Color(u_meshId); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/varying.def.sc index 32f30d13a5..cbaadbb6fc 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/varying.def.sc @@ -1 +1,4 @@ -vec3 a_position : POSITION; \ No newline at end of file +vec3 a_position : POSITION; + +vec3 v_worldPos : TEXCOORD3; // unused texcoord semantic +float v_discardFlag : TEXCOORD4; // unused texcoord semantic diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/vs_surface_id.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/vs_surface_id.sc index 7c3bf73d47..c5f39afd11 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/vs_surface_id.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/vs_surface_id.sc @@ -21,10 +21,17 @@ ****************************************************************************/ $input a_position +$output v_worldPos, v_discardFlag #include +#include + void main() { + v_worldPos = mul(u_model[0], vec4(a_position, 1.0)).xyz; gl_Position = mul(u_modelViewProj, vec4(a_position, 1.0)); + + // discard flag - used to discard the whole vertex, but in fragment shader + v_discardFlag = computeDiscardFlag(v_worldPos); } From cbf32a3e37dc71ac99c36cc5c4111a4dfbaf9204 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 10 Apr 2026 15:40:40 +0200 Subject: [PATCH 16/49] [render] DrawableMesh: Refactor cross-section uniform updates into updateCrossSectionUniforms() --- .../vclib/bgfx/drawable/drawable_mesh_bgfx.h | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h index f45e3d1a51..ba55255192 100644 --- a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h +++ b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h @@ -218,16 +218,7 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType DrawableMeshUniforms::setColor(*this); MeshRenderSettingsUniforms::set(mMRS); - if (AbstractDrawableMesh::mCSS.isEnabled()) { - using enum CrossSectionSettings::CrossSectionType; - CrossSectionUniforms::set( - AbstractDrawableMesh::mCSS.lower(), - AbstractDrawableMesh::mCSS.upper(), - AbstractDrawableMesh::mCSS.type() == PER_FRAGMENT); - } - else { - CrossSectionUniforms::set(); - } + updateCrossSectionUniforms(); if (mMRS.isSurface(MRI::Surface::VISIBLE)) { const PBRViewerSettings& pbrSettings = settings.pbrSettings; @@ -260,6 +251,8 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType mMRB.bindIndexBuffers(mMRS, i); /* UNIFORMS */ + CrossSectionUniforms::bind(); + DrawableMeshUniforms::setFirstChunkIndex( mMRB.triangleChunk(i).startIndex); uint64_t materialState = @@ -350,16 +343,7 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType model = MeshType::transformMatrix().template cast(); } - if (AbstractDrawableMesh::mCSS.isEnabled()) { - using enum CrossSectionSettings::CrossSectionType; - CrossSectionUniforms::set( - AbstractDrawableMesh::mCSS.lower(), - AbstractDrawableMesh::mCSS.upper(), - AbstractDrawableMesh::mCSS.type() == PER_FRAGMENT); - } - else { - CrossSectionUniforms::set(); - } + updateCrossSectionUniforms(); if (mMRS.isSurface(MRI::Surface::VISIBLE)) { mMRB.bindVertexBuffers(mMRS); @@ -450,7 +434,20 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType { MeshRenderSettingsUniforms::bind(); DrawableMeshUniforms::bind(); - CrossSectionUniforms::bind(); + } + + void updateCrossSectionUniforms() const + { + if (AbstractDrawableMesh::mCSS.isEnabled()) { + using enum CrossSectionSettings::CrossSectionType; + CrossSectionUniforms::set( + AbstractDrawableMesh::mCSS.lower(), + AbstractDrawableMesh::mCSS.upper(), + AbstractDrawableMesh::mCSS.type() == PER_FRAGMENT); + } + else { + CrossSectionUniforms::set(); + } } /** From c468fbdc0f1fcbc108b35bb28944dd796feb98f5 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 10 Apr 2026 16:01:05 +0200 Subject: [PATCH 17/49] [render] Add cross-section clipping support to points_instance shaders --- .../include/vclib/bgfx/drawable/drawable_mesh_bgfx.h | 2 ++ .../drawable_mesh/points_instance/fs_points_instance.sc | 5 ++++- .../drawable/drawable_mesh/points_instance/varying.def.sc | 8 +++++--- .../drawable_mesh/points_instance/vs_points_instance.sc | 7 ++++++- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h index ba55255192..12d4917a8a 100644 --- a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h +++ b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h @@ -299,6 +299,7 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType // 1 px vertices mMRB.bindVertexBuffers(mMRS); bindUniforms(); + CrossSectionUniforms::bind(); bgfx::setState(state | BGFX_STATE_PT_POINTS); bgfx::setTransform(model.data()); @@ -314,6 +315,7 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType // render splats mMRB.bindVertexQuadBuffer(); bindUniforms(); + CrossSectionUniforms::bind(); bgfx::setState(state); bgfx::setTransform(model.data()); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc index a85d5e9f0e..5941309da9 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc @@ -20,12 +20,15 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ -$input v_normal, v_color, v_texcoord1 +$input v_worldPos, v_discardFlag, v_normal, v_color, v_texcoord1 #include +#include void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + // defaul color and light vec4 light = vec4(1, 1, 1, 1); vec4 color = uintABGRToVec4Color(floatBitsToUint(u_userPointColorFloat)); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc index 1fec272da6..0d3d60004f 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc @@ -2,6 +2,8 @@ vec3 a_position : POSITION; vec4 a_color0 : COLOR0; vec3 a_normal : NORMAL; -vec3 v_normal : NORMAL; -vec4 v_color : COLOR0; -vec2 v_texcoord1 : TEXCOORD1; +vec3 v_worldPos : TEXCOORD3; // unused texcoord semantic +float v_discardFlag : TEXCOORD4; // unused texcoord semantic +vec3 v_normal : NORMAL; +vec4 v_color : COLOR0; +vec2 v_texcoord1 : TEXCOORD1; diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc index f48ec1419b..19c0bbd92c 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc @@ -21,9 +21,10 @@ ****************************************************************************/ $input a_position, a_color0, a_normal, a_texcoord0 -$output v_normal, v_color, v_texcoord1 +$output v_worldPos, v_discardFlag, v_normal, v_color, v_texcoord1 #include +#include void main() { @@ -37,6 +38,7 @@ void main() (2.0 * quadUv.y - 1.0) * u_pointWidth * u_viewTexel.y, // is divided by 2 0, 0); + v_worldPos = mul(u_model[0], vec4(a_position, 1.0)).xyz; pos = pos / pos.w; gl_Position = pos + offset; v_normal = normalize(mul(u_normalMatrix, a_normal)); @@ -46,4 +48,7 @@ void main() // quad parametrization v_texcoord1 = quadUv; + + // discard flag - used to discard the whole vertex, but in fragment shader + v_discardFlag = computeDiscardFlag(v_worldPos); } From 72739a088fe95f074b6324b386d6caa65881ecf0 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Mon, 13 Apr 2026 10:05:22 +0200 Subject: [PATCH 18/49] [render] Move CrossSectionSettings from drawable/mesh to render/settings directory --- .../include/vclib/qt/gui/mesh_render_settings_frame.h | 2 +- .../cross_section_settings_frame.h | 2 +- .../include/vclib/render/drawable/abstract_drawable_mesh.h | 2 +- .../{drawable/mesh => settings}/cross_section_settings.h | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) rename vclib/render/include/vclib/render/{drawable/mesh => settings}/cross_section_settings.h (95%) diff --git a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h index 8938762374..c972c61240 100644 --- a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h +++ b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h @@ -25,8 +25,8 @@ #include "mesh_render_settings_frame/generic_mesh_render_settings_frame.h" -#include #include +#include #include diff --git a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h index ceddd08f82..63538fecbd 100644 --- a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h +++ b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h @@ -24,7 +24,7 @@ #define VCL_QT_GUI_MESH_RENDER_SETTINGS_FRAME_CROSS_SECTION_SETTINGS_FRAME_H #include -#include +#include #include #include diff --git a/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h b/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h index 77644a5c78..4a824b4f75 100644 --- a/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h +++ b/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h @@ -24,9 +24,9 @@ #define VCL_RENDER_DRAWABLE_ABSTRACT_DRAWABLE_MESH_H #include "drawable_object.h" -#include "mesh/cross_section_settings.h" #include "mesh/mesh_render_settings.h" +#include #include namespace vcl { diff --git a/vclib/render/include/vclib/render/drawable/mesh/cross_section_settings.h b/vclib/render/include/vclib/render/settings/cross_section_settings.h similarity index 95% rename from vclib/render/include/vclib/render/drawable/mesh/cross_section_settings.h rename to vclib/render/include/vclib/render/settings/cross_section_settings.h index 2eef9000a0..8e98590b6e 100644 --- a/vclib/render/include/vclib/render/drawable/mesh/cross_section_settings.h +++ b/vclib/render/include/vclib/render/settings/cross_section_settings.h @@ -20,8 +20,8 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ -#ifndef VCL_RENDER_DRAWABLE_MESH_CROSS_SECTION_SETTINGS_H -#define VCL_RENDER_DRAWABLE_MESH_CROSS_SECTION_SETTINGS_H +#ifndef VCL_RENDER_SETTINGS_CROSS_SECTION_SETTINGS_H +#define VCL_RENDER_SETTINGS_CROSS_SECTION_SETTINGS_H #include #include @@ -105,4 +105,4 @@ class CrossSectionSettings } // namespace vcl -#endif // VCL_RENDER_DRAWABLE_MESH_CROSS_SECTION_SETTINGS_H +#endif // VCL_RENDER_SETTINGS_CROSS_SECTION_SETTINGS_H From 81802a24f5c529a14e4e0d430ae4c8a1eee973c5 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Mon, 13 Apr 2026 12:26:24 +0200 Subject: [PATCH 19/49] [render] Add CrossSection clipping to lines --- .../vclib/bgfx/drawable/drawable_mesh_bgfx.h | 6 ++++ .../bgfx/drawable/mesh/mesh_render_buffers.h | 6 ++++ .../include/vclib/bgfx/primitives/lines.h | 28 +++++++++++++++++++ .../render/drawable/abstract_drawable_mesh.h | 10 +++---- .../fs_cpu_generated_lines.sc | 10 +++++-- .../lines/cpu_generated_lines/varying.def.sc | 8 ++++-- .../vs_cpu_generated_lines.sc | 12 +++++++- 7 files changed, 69 insertions(+), 11 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h index 12d4917a8a..6bb4a1fca5 100644 --- a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h +++ b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h @@ -148,6 +148,12 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType mMRB.updateWireframeSettings(rs); } + void setCrossSectionSettings(const CrossSectionSettings& css) override + { + AbstractDrawableMesh::setCrossSectionSettings(css); + mMRB.updateCrossSectionSettings(css); + } + uint vertexCount() const override { return MeshType::vertexCount(); } uint faceCount() const override diff --git a/vclib/render/include/vclib/bgfx/drawable/mesh/mesh_render_buffers.h b/vclib/render/include/vclib/bgfx/drawable/mesh/mesh_render_buffers.h index cbb52a708c..185e478ba0 100644 --- a/vclib/render/include/vclib/bgfx/drawable/mesh/mesh_render_buffers.h +++ b/vclib/render/include/vclib/bgfx/drawable/mesh/mesh_render_buffers.h @@ -326,6 +326,12 @@ class MeshRenderBuffers : public MeshRenderData> } } + void updateCrossSectionSettings(const CrossSectionSettings& css) + { + mEdgeLines.crossSectionSettings() = css; + mWireframeLines.crossSectionSettings() = css; + } + private: void setVertexPositionsBuffer(const MeshType& mesh) // override { diff --git a/vclib/render/include/vclib/bgfx/primitives/lines.h b/vclib/render/include/vclib/bgfx/primitives/lines.h index 08e955b03b..9b0d7a7cdb 100644 --- a/vclib/render/include/vclib/bgfx/primitives/lines.h +++ b/vclib/render/include/vclib/bgfx/primitives/lines.h @@ -23,10 +23,13 @@ #ifndef VCL_BGFX_PRIMITIVES_LINES_H #define VCL_BGFX_PRIMITIVES_LINES_H +#include #include #include #include +#include + #include #include @@ -90,6 +93,8 @@ class Lines ColorToUse mColorToUse = ColorToUse::GENERAL; Color mGeneralColor = Color::ColorABGR::LightGray; + CrossSectionSettings mCrossSectionSettings; + ImplementationType mType = ImplementationType::COUNT; std::variant @@ -385,6 +390,16 @@ class Lines */ Color& generalColor() { return mGeneralColor; } + CrossSectionSettings crossSectionSettings() const + { + return mCrossSectionSettings; + } + + CrossSectionSettings& crossSectionSettings() + { + return mCrossSectionSettings; + } + /** * @brief Returns the current implementation type that is used to render * the lines. @@ -533,6 +548,19 @@ class Lines std::bit_cast(mGeneralColor.abgr()), mDepthOffset}; sSettingUH.bind(data); + + // cross section settings + if (mCrossSectionSettings.isEnabled()) { + using enum CrossSectionSettings::CrossSectionType; + CrossSectionUniforms::set( + mCrossSectionSettings.lower(), + mCrossSectionSettings.upper(), + mCrossSectionSettings.type() == PER_FRAGMENT); + } + else { + CrossSectionUniforms::set(); + } + CrossSectionUniforms::bind(); } }; diff --git a/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h b/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h index 4a824b4f75..006521b8af 100644 --- a/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h +++ b/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h @@ -68,17 +68,17 @@ class AbstractDrawableMesh : public vcl::DrawableObject const CrossSectionSettings& crossSectionSettings() const { return mCSS; } - void setCrossSectionSettings(const CrossSectionSettings& css) - { - mCSS = css; - } - virtual void updateBuffers( MeshRenderInfo::BuffersBitSet buffersToUpdate = MeshRenderInfo::BUFFERS_ALL) = 0; virtual void setRenderSettings(const MeshRenderSettings& rs) { mMRS = rs; } + virtual void setCrossSectionSettings(const CrossSectionSettings& css) + { + mCSS = css; + } + virtual uint vertexCount() const = 0; virtual uint faceCount() const = 0; diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/fs_cpu_generated_lines.sc b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/fs_cpu_generated_lines.sc index eaf0f9fbb3..2100579769 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/fs_cpu_generated_lines.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/fs_cpu_generated_lines.sc @@ -20,8 +20,9 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ -$input v_color, v_normal +$input v_worldPos0, v_worldPos1, v_discardFlag, v_t, v_color, v_normal +#include #include #include #include @@ -35,11 +36,14 @@ BUFFER_RO(edgesColors, uint, 0); #define vertexColor v_color void main() { + vec3 fragPos = mix(v_worldPos0, v_worldPos1, v_t); + discardIfCrossSectionClipped(v_discardFlag, fragPos); + vec4 color; if (colorToUse == 0) color = vertexColor; else if (colorToUse == 1) color = edgeColor; - else color = generalColor; - + else color = generalColor; + if (u_shadingPerVertex) { color *= computeLight(u_lightDir, u_lightColor, v_normal); } diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc index 4de0fe029d..1e671a7a68 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc @@ -5,5 +5,9 @@ vec4 a_color1 : COLOR1; // c1 vec3 a_normal : NORMAL; // n0 vec3 a_texcoord1 : TEXCOORD1; // n1 -vec4 v_color : COLOR0; -vec3 v_normal : NORMAL; +vec4 v_color : COLOR0; +vec3 v_normal : NORMAL; +vec3 v_worldPos0 : TEXCOORD2; // unused texcoord semantic +vec3 v_worldPos1 : TEXCOORD3; // unused texcoord semantic +float v_discardFlag : TEXCOORD4; // unused texcoord semantic +float v_t : TEXCOORD5; // interpolation parameter along segment diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/vs_cpu_generated_lines.sc b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/vs_cpu_generated_lines.sc index 2859e6275a..18d59b5edc 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/vs_cpu_generated_lines.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/vs_cpu_generated_lines.sc @@ -21,11 +21,12 @@ ****************************************************************************/ $input a_position, a_texcoord0, a_color0, a_color1, a_normal, a_texcoord1 -$output v_color, v_normal +$output v_worldPos0, v_worldPos1, v_discardFlag, v_t, v_color, v_normal #include #include +#include #include #include @@ -48,6 +49,9 @@ void main() { int generalIndex = gl_VertexID % 4; vec2 uv = vec2((generalIndex >> 1) & 0x1, generalIndex & 0x1); + v_worldPos0 = mul(u_model[0], vec4(p0, 1.0)).xyz; + v_worldPos1 = mul(u_model[0], vec4(p1, 1.0)).xyz; + // segment points in clip space vec4 p0_NDC = mul(u_modelViewProj, vec4(p0, 1.0)); vec4 p1_NDC = mul(u_modelViewProj, vec4(p1, 1.0)); @@ -108,7 +112,13 @@ void main() { v_color = color; v_normal = normal; + v_t = uv.x; // nudge towards camera to avoid z-fighting with other geometry p.z += -u_depthOffset * p.w; gl_Position = p; + + // discard flag - 1.0 if either endpoint is outside the cross section + // (only used in per-vertex mode; in per-fragment mode computeDiscardFlag returns 0.0) + v_discardFlag = + max(computeDiscardFlag(v_worldPos0), computeDiscardFlag(v_worldPos1)); } From 42cb29beb613bc1705d6e05de4c7addad5420c6c Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Mon, 13 Apr 2026 14:54:05 +0200 Subject: [PATCH 20/49] [render] Move CrossSectionSettingsFrame to tab widget and update UI structure --- .../vclib/qt/gui/mesh_render_settings_frame.h | 4 +- .../qt/gui/mesh_render_settings_frame.cpp | 21 ++-- .../qt/gui/mesh_render_settings_frame.ui | 18 --- .../cross_section_settings_frame.ui | 104 ++++++++++++------ 4 files changed, 84 insertions(+), 63 deletions(-) diff --git a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h index c972c61240..fd9df70e3e 100644 --- a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h +++ b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h @@ -23,6 +23,7 @@ #ifndef VCL_QT_GUI_MESH_RENDER_SETTINGS_FRAME_H #define VCL_QT_GUI_MESH_RENDER_SETTINGS_FRAME_H +#include "mesh_render_settings_frame/cross_section_settings_frame.h" #include "mesh_render_settings_frame/generic_mesh_render_settings_frame.h" #include @@ -45,7 +46,8 @@ class MeshRenderSettingsFrame : public QFrame Ui::MeshRenderSettingsFrame* mUI; MeshRenderSettings mMRS; - std::vector frames; + std::vector mFrames; + CrossSectionSettingsFrame* mCrossSectionFrame; public: explicit MeshRenderSettingsFrame(QWidget* parent = nullptr); diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp index c0129cbd77..7bc116ad3f 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.cpp @@ -41,21 +41,21 @@ MeshRenderSettingsFrame::MeshRenderSettingsFrame(QWidget* parent) : auto* pointsFrame = new PointsFrame(mMRS, this); mUI->tabWidget->addTab(pointsFrame, "Points"); - frames.push_back(pointsFrame); + mFrames.push_back(pointsFrame); auto* surfaceFrame = new SurfaceFrame(mMRS, this); mUI->tabWidget->addTab(surfaceFrame, "Surface"); - frames.push_back(surfaceFrame); + mFrames.push_back(surfaceFrame); auto* wireframeFrame = new WireframeFrame(mMRS, this); mUI->tabWidget->addTab(wireframeFrame, "Wireframe"); - frames.push_back(wireframeFrame); + mFrames.push_back(wireframeFrame); auto* edgesFrame = new EdgesFrame(mMRS, this); mUI->tabWidget->addTab(edgesFrame, "Edges"); - frames.push_back(edgesFrame); + mFrames.push_back(edgesFrame); - for (auto* frame : frames) { + for (auto* frame : mFrames) { connect( frame, SIGNAL(meshRenderSettingsUpdated()), @@ -63,8 +63,11 @@ MeshRenderSettingsFrame::MeshRenderSettingsFrame(QWidget* parent) : SIGNAL(meshRenderSettingsUpdated())); } + mCrossSectionFrame = new CrossSectionSettingsFrame(this); + mUI->tabWidget->addTab(mCrossSectionFrame, "Cross Section"); + connect( - mUI->crossSectionFrame, + mCrossSectionFrame, SIGNAL(crossSectionSettingsUpdated()), this, SIGNAL(crossSectionSettingsUpdated())); @@ -83,7 +86,7 @@ const MeshRenderSettings& MeshRenderSettingsFrame::meshRenderSettings() const const CrossSectionSettings& MeshRenderSettingsFrame::crossSectionSettings() const { - return mUI->crossSectionFrame->crossSectionSettings(); + return mCrossSectionFrame->crossSectionSettings(); } void MeshRenderSettingsFrame::setMeshRenderSettings( @@ -97,14 +100,14 @@ void MeshRenderSettingsFrame::setMeshRenderSettings( void MeshRenderSettingsFrame::setCrossSectionSettings( const CrossSectionSettings& settings) { - mUI->crossSectionFrame->setCrossSectionSettings(settings); + mCrossSectionFrame->setCrossSectionSettings(settings); } void MeshRenderSettingsFrame::updateGuiFromSettings(bool changeCurrentTab) { using MRI = MeshRenderInfo; - for (auto* frame : frames) { + for (auto* frame : mFrames) { frame->updateFrameFromSettings(); } diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui index 6eefbd7d3e..a660187262 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame.ui @@ -27,26 +27,8 @@ - - - - QFrame::Shape::NoFrame - - - QFrame::Shadow::Plain - - - - - - vcl::qt::CrossSectionSettingsFrame - QFrame -
vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h
- 1 -
-
diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui index c7f73e816d..8428ce13b0 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui @@ -89,21 +89,30 @@ 0 - - + + + + + + + + + + + - + 0 0 + + 0.010000000000000 + - - - - - + + 0 @@ -115,10 +124,14 @@ - - + + + + Qt::Orientation::Horizontal + + - + @@ -131,37 +144,47 @@ - - + + - + 0 0 - - 0.010000000000000 + + Z: - - + + - - + + + + + + + + + + Qt::Orientation::Horizontal + + + + + - + 0 0 - - X: - - - + + 0 @@ -169,18 +192,16 @@ - Z: + X: - - - - - - - - + + + + Qt::Orientation::Horizontal + + @@ -188,6 +209,19 @@ + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + From 88eb9672b801080e4b02630fb7e05ed7a98e6eab Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Mon, 13 Apr 2026 15:15:35 +0200 Subject: [PATCH 21/49] [render] separate cross section inputs/outputs in all the shaders --- .../render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h | 5 +---- .../drawable_mesh/points_instance/fs_points_instance.sc | 5 ++++- .../drawable/drawable_mesh/points_instance/varying.def.sc | 5 +++-- .../drawable_mesh/points_instance/vs_points_instance.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_flat_color_face.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_flat_color_mesh.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_flat_color_user.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_flat_color_vertex.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_flat_tex_vertex.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_flat_tex_wedge.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_none_color_face.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_none_color_mesh.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_none_color_user.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_none_color_vertex.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_none_tex_vertex.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_none_tex_wedge.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_smooth_color_face.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_smooth_color_mesh.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_smooth_color_user.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_smooth_color_vertex.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_smooth_tex_vertex.sc | 5 ++++- .../drawable_mesh/surface/fs_surface_smooth_tex_wedge.sc | 5 ++++- .../bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc | 5 ++++- .../drawable/drawable_mesh/surface/fs_surface_uber_pbr.sc | 5 ++++- .../bgfx/drawable/drawable_mesh/surface/surface_uber_in.sh | 5 ++++- .../vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc | 5 +++-- .../vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc | 5 ++++- .../bgfx/drawable/drawable_mesh/surface_id/fs_surface_id.sc | 1 + .../bgfx/drawable/drawable_mesh/surface_id/vs_surface_id.sc | 2 ++ .../lines/cpu_generated_lines/fs_cpu_generated_lines.sc | 5 ++++- .../bgfx/primitives/lines/cpu_generated_lines/varying.def.sc | 3 ++- .../lines/cpu_generated_lines/vs_cpu_generated_lines.sc | 5 ++++- 32 files changed, 116 insertions(+), 35 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h index 6bb4a1fca5..0b67c5d354 100644 --- a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h +++ b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h @@ -257,8 +257,6 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType mMRB.bindIndexBuffers(mMRS, i); /* UNIFORMS */ - CrossSectionUniforms::bind(); - DrawableMeshUniforms::setFirstChunkIndex( mMRB.triangleChunk(i).startIndex); uint64_t materialState = @@ -305,7 +303,6 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType // 1 px vertices mMRB.bindVertexBuffers(mMRS); bindUniforms(); - CrossSectionUniforms::bind(); bgfx::setState(state | BGFX_STATE_PT_POINTS); bgfx::setTransform(model.data()); @@ -321,7 +318,6 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType // render splats mMRB.bindVertexQuadBuffer(); bindUniforms(); - CrossSectionUniforms::bind(); bgfx::setState(state); bgfx::setTransform(model.data()); @@ -442,6 +438,7 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType { MeshRenderSettingsUniforms::bind(); DrawableMeshUniforms::bind(); + CrossSectionUniforms::bind(); } void updateCrossSectionUniforms() const diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc index 5941309da9..606946edae 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc @@ -20,7 +20,10 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ -$input v_worldPos, v_discardFlag, v_normal, v_color, v_texcoord1 +$input v_normal, v_color, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include #include diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc index 0d3d60004f..ee363e7dc6 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc @@ -2,8 +2,9 @@ vec3 a_position : POSITION; vec4 a_color0 : COLOR0; vec3 a_normal : NORMAL; -vec3 v_worldPos : TEXCOORD3; // unused texcoord semantic -float v_discardFlag : TEXCOORD4; // unused texcoord semantic vec3 v_normal : NORMAL; vec4 v_color : COLOR0; vec2 v_texcoord1 : TEXCOORD1; + +vec3 v_worldPos : TEXCOORD3; // unused texcoord semantic +float v_discardFlag : TEXCOORD4; // unused texcoord semantic diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc index 19c0bbd92c..1222a3fb64 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc @@ -21,7 +21,10 @@ ****************************************************************************/ $input a_position, a_color0, a_normal, a_texcoord0 -$output v_worldPos, v_discardFlag, v_normal, v_color, v_texcoord1 +$output v_normal, v_color, v_texcoord1 + +// cross section +$output v_worldPos, v_discardFlag #include #include diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_face.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_face.sc index 59715944b0..d46c8a2af4 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_face.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_face.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "flat.sh" #include "color_face.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_mesh.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_mesh.sc index e82658be79..c2da6fc330 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_mesh.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_mesh.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "flat.sh" #include "color_mesh.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_user.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_user.sc index 28e612c6de..efe2774f3d 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_user.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_user.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "flat.sh" #include "color_user.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_vertex.sc index 075b1dedf2..55935ba1f9 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_vertex.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "flat.sh" #include "color_vertex.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_vertex.sc index a08e2d66e1..7ff513c9b0 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_vertex.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "flat.sh" #include "tex_vertex.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_wedge.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_wedge.sc index ca01b58446..11c3ff4dfc 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_wedge.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_wedge.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "flat.sh" #include "tex_wedge.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_face.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_face.sc index f1da27f761..13d3dbe4cd 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_face.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_face.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "none.sh" #include "color_face.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_mesh.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_mesh.sc index 816ffef4bb..c8fd419b50 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_mesh.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_mesh.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "none.sh" #include "color_mesh.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_user.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_user.sc index 16a97f8b41..3bdc461d4d 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_user.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_user.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "none.sh" #include "color_user.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_vertex.sc index e5779a82ee..ceda415289 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_vertex.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "none.sh" #include "color_vertex.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_vertex.sc index 730cb527db..8f20ac1903 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_vertex.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "none.sh" #include "tex_vertex.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_wedge.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_wedge.sc index 3255e57a5d..2757ef4b3f 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_wedge.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_wedge.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "none.sh" #include "tex_wedge.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_face.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_face.sc index 75db62cd07..db9605af55 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_face.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_face.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "smooth.sh" #include "color_face.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_mesh.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_mesh.sc index 3c1b47a1c4..dcacfc7b15 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_mesh.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_mesh.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "smooth.sh" #include "color_mesh.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_user.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_user.sc index 928c90292b..7aeb11d1c1 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_user.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_user.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "smooth.sh" #include "color_user.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_vertex.sc index 79a328a218..40e456f3c8 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_vertex.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "smooth.sh" #include "color_vertex.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_vertex.sc index 7780305822..2a5da58c93 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_vertex.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "smooth.sh" #include "tex_vertex.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_wedge.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_wedge.sc index 136b595878..eb3d09c04e 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_wedge.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_wedge.sc @@ -1,4 +1,7 @@ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include "smooth.sh" #include "tex_wedge.sh" diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc index 7764af51ec..c48bcf72af 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber.sc @@ -20,7 +20,10 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include #include diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber_pbr.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber_pbr.sc index b25bd18c26..20555df9b4 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber_pbr.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_uber_pbr.sc @@ -20,7 +20,10 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface_uber_in.sh b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface_uber_in.sh index 787a8e8241..d6718320c8 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface_uber_in.sh +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface_uber_in.sh @@ -20,7 +20,10 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ -$input v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$input v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$input v_worldPos, v_discardFlag #include #include diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc index c31ee4f3fc..27c245fcc2 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc @@ -6,10 +6,11 @@ vec2 a_texcoord0 : TEXCOORD0; vec2 a_texcoord1 : TEXCOORD1; vec3 v_position : TEXCOORD2; // unused texcoord semantic -vec3 v_worldPos : TEXCOORD3; // unused texcoord semantic -float v_discardFlag : TEXCOORD4; // unused texcoord semantic vec3 v_normal : NORMAL; vec4 v_tangent : TANGENT; vec4 v_color : COLOR0; vec2 v_texcoord0 : TEXCOORD0; vec2 v_texcoord1 : TEXCOORD1; + +vec3 v_worldPos : TEXCOORD3; // unused texcoord semantic +float v_discardFlag : TEXCOORD4; // unused texcoord semantic diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc index 603c968ddf..8df3c3c88c 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc @@ -21,7 +21,10 @@ ****************************************************************************/ $input a_position, a_normal, a_tangent, a_color0, a_texcoord0, a_texcoord1 -$output v_position, v_worldPos, v_discardFlag, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 +$output v_position, v_normal, v_tangent, v_color, v_texcoord0, v_texcoord1 + +// cross section +$output v_worldPos, v_discardFlag #include #include diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/fs_surface_id.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/fs_surface_id.sc index b89bee2244..bb7f733bad 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/fs_surface_id.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/fs_surface_id.sc @@ -20,6 +20,7 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ +// cross section $input v_worldPos, v_discardFlag #include diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/vs_surface_id.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/vs_surface_id.sc index c5f39afd11..8c79002992 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/vs_surface_id.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/vs_surface_id.sc @@ -21,6 +21,8 @@ ****************************************************************************/ $input a_position + +// cross section $output v_worldPos, v_discardFlag #include diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/fs_cpu_generated_lines.sc b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/fs_cpu_generated_lines.sc index 2100579769..2a88f7a990 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/fs_cpu_generated_lines.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/fs_cpu_generated_lines.sc @@ -20,7 +20,10 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ -$input v_worldPos0, v_worldPos1, v_discardFlag, v_t, v_color, v_normal +$input v_color, v_normal + +// cross section +$input v_worldPos0, v_worldPos1, v_discardFlag, v_t #include #include diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc index 1e671a7a68..96762d20bc 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc @@ -8,6 +8,7 @@ vec3 a_texcoord1 : TEXCOORD1; // n1 vec4 v_color : COLOR0; vec3 v_normal : NORMAL; vec3 v_worldPos0 : TEXCOORD2; // unused texcoord semantic -vec3 v_worldPos1 : TEXCOORD3; // unused texcoord semantic + +vec3 v_worldPos1 : TEXCOORD3; // unused texcoord semantic float v_discardFlag : TEXCOORD4; // unused texcoord semantic float v_t : TEXCOORD5; // interpolation parameter along segment diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/vs_cpu_generated_lines.sc b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/vs_cpu_generated_lines.sc index 18d59b5edc..3fa7f3108f 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/vs_cpu_generated_lines.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/vs_cpu_generated_lines.sc @@ -21,7 +21,10 @@ ****************************************************************************/ $input a_position, a_texcoord0, a_color0, a_color1, a_normal, a_texcoord1 -$output v_worldPos0, v_worldPos1, v_discardFlag, v_t, v_color, v_normal +$output v_color, v_normal + +// cross section +$output v_worldPos0, v_worldPos1, v_discardFlag, v_t #include #include From 1f13489c147fcf72a3c9fe77785930ea8162603e Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Mon, 13 Apr 2026 15:26:55 +0200 Subject: [PATCH 22/49] [render] varying.def indent fixes --- .../drawable_mesh/points_instance/varying.def.sc | 6 +++--- .../drawable/drawable_mesh/surface/varying.def.sc | 12 ++++++------ .../lines/cpu_generated_lines/varying.def.sc | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc index ee363e7dc6..82f6451ece 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc @@ -2,9 +2,9 @@ vec3 a_position : POSITION; vec4 a_color0 : COLOR0; vec3 a_normal : NORMAL; -vec3 v_normal : NORMAL; -vec4 v_color : COLOR0; -vec2 v_texcoord1 : TEXCOORD1; +vec3 v_normal : NORMAL; +vec4 v_color : COLOR0; +vec2 v_texcoord1 : TEXCOORD1; vec3 v_worldPos : TEXCOORD3; // unused texcoord semantic float v_discardFlag : TEXCOORD4; // unused texcoord semantic diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc index 27c245fcc2..cfe20c1ad3 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc @@ -5,12 +5,12 @@ vec4 a_color0 : COLOR0; vec2 a_texcoord0 : TEXCOORD0; vec2 a_texcoord1 : TEXCOORD1; -vec3 v_position : TEXCOORD2; // unused texcoord semantic -vec3 v_normal : NORMAL; -vec4 v_tangent : TANGENT; -vec4 v_color : COLOR0; -vec2 v_texcoord0 : TEXCOORD0; -vec2 v_texcoord1 : TEXCOORD1; +vec3 v_position : TEXCOORD2; // unused texcoord semantic +vec3 v_normal : NORMAL; +vec4 v_tangent : TANGENT; +vec4 v_color : COLOR0; +vec2 v_texcoord0 : TEXCOORD0; +vec2 v_texcoord1 : TEXCOORD1; vec3 v_worldPos : TEXCOORD3; // unused texcoord semantic float v_discardFlag : TEXCOORD4; // unused texcoord semantic diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc index 96762d20bc..44b27c1499 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc @@ -5,10 +5,10 @@ vec4 a_color1 : COLOR1; // c1 vec3 a_normal : NORMAL; // n0 vec3 a_texcoord1 : TEXCOORD1; // n1 -vec4 v_color : COLOR0; -vec3 v_normal : NORMAL; -vec3 v_worldPos0 : TEXCOORD2; // unused texcoord semantic +vec4 v_color : COLOR0; +vec3 v_normal : NORMAL; +vec3 v_worldPos0 : TEXCOORD2; // unused texcoord semantic vec3 v_worldPos1 : TEXCOORD3; // unused texcoord semantic float v_discardFlag : TEXCOORD4; // unused texcoord semantic float v_t : TEXCOORD5; // interpolation parameter along segment From 74d8efe96a1936917ded441a44204c52d40686bf Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Wed, 15 Apr 2026 15:30:54 +0200 Subject: [PATCH 23/49] [render] cross section on primitve points shader --- .../bgfx/drawable/drawable_mesh/points/fs_points.sc | 6 ++++++ .../drawable/drawable_mesh/points/varying.def.sc | 3 +++ .../bgfx/drawable/drawable_mesh/points/vs_points.sc | 8 ++++++++ .../drawable/drawable_mesh/points_id/fs_points_id.sc | 6 ++++++ .../drawable/drawable_mesh/points_id/varying.def.sc | 3 +++ .../drawable/drawable_mesh/points_id/vs_points_id.sc | 10 ++++++++++ .../points_instance/fs_points_instance.sc | 4 ++-- .../drawable_mesh/points_instance/varying.def.sc | 6 +++--- .../points_instance/vs_points_instance.sc | 4 ++-- .../points_instance_id/fs_points_instance_id.sc | 10 ++++++++-- .../drawable_mesh/points_instance_id/varying.def.sc | 5 ++++- .../points_instance_id/vs_points_instance_id.sc | 12 ++++++++++-- .../drawable/drawable_mesh/surface_id/varying.def.sc | 4 ++-- .../lines/cpu_generated_lines/varying.def.sc | 2 +- 14 files changed, 68 insertions(+), 15 deletions(-) diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/fs_points.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/fs_points.sc index cc13691c32..7c76a514c3 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/fs_points.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/fs_points.sc @@ -23,10 +23,16 @@ // TODO: remove v_position $input v_position, v_normal, v_color +// cross section +$input v_worldPos, v_discardFlag + #include +#include void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + // color vec4 color = vec4(0.0, 0.0, 0.0, 1.0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/varying.def.sc index eeb932d98a..55298cd165 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/varying.def.sc @@ -5,3 +5,6 @@ vec4 a_color0 : COLOR0; vec3 v_position : TANGENT; vec3 v_normal : NORMAL; vec4 v_color : COLOR0; + +vec3 v_worldPos : TEXCOORD0; // unused texcoord semantic +float v_discardFlag : TEXCOORD1; // unused texcoord semantic diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/vs_points.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/vs_points.sc index c23f8d10bf..547c2c875f 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/vs_points.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/vs_points.sc @@ -23,14 +23,22 @@ $input a_position, a_normal, a_color0 $output v_position, v_normal, v_color +// cross section +$output v_worldPos, v_discardFlag + #include +#include void main() { gl_Position = mul(u_modelViewProj, vec4(a_position, 1.0)); v_position = mul(u_modelView, vec4(a_position, 1.0)).xyz; v_normal = normalize(mul(u_normalMatrix, a_normal)); + v_worldPos = mul(u_model[0], vec4(a_position, 1.0)).xyz; // default case - color is taken from buffer v_color = a_color0; + + // discard flag - used to discard the whole vertex, but in fragment shader + v_discardFlag = computeDiscardFlag(v_worldPos); } diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/fs_points_id.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/fs_points_id.sc index e5f18faa30..c9c568f664 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/fs_points_id.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/fs_points_id.sc @@ -20,12 +20,18 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ +// cross section +$input v_worldPos, v_discardFlag + #include #include +#include void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + /***** render ID to color ******/ vec4 color = uintABGRToVec4Color(u_meshId); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/varying.def.sc index 76522d5fc7..ca20ef2a35 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/varying.def.sc @@ -1 +1,4 @@ vec3 a_position : POSITION; + +vec3 v_worldPos : TEXCOORD0; // unused texcoord semantic +float v_discardFlag : TEXCOORD1; // unused texcoord semantic diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/vs_points_id.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/vs_points_id.sc index 7c3bf73d47..4223956c56 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/vs_points_id.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/vs_points_id.sc @@ -22,9 +22,19 @@ $input a_position +// cross section +$output v_worldPos, v_discardFlag + #include +#include + void main() { gl_Position = mul(u_modelViewProj, vec4(a_position, 1.0)); + + v_worldPos = mul(u_model[0], vec4(a_position, 1.0)).xyz; + + // discard flag - used to discard the whole vertex, but in fragment shader + v_discardFlag = computeDiscardFlag(v_worldPos); } diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc index 606946edae..6d4a965439 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc @@ -20,7 +20,7 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ -$input v_normal, v_color, v_texcoord1 +$input v_normal, v_color, v_texcoord0 // cross section $input v_worldPos, v_discardFlag @@ -40,7 +40,7 @@ void main() bool isCircle = bool(u_pointsMode & posToBitFlag(VCL_MRS_POINTS_CIRCLE)); bool isSphere = bool(u_pointsMode & posToBitFlag(VCL_MRS_POINTS_SPHERE)); if (isCircle || isSphere) { - vec2 uv = v_texcoord1 * 2.0 - vec2(1.0, 1.0); + vec2 uv = v_texcoord0 * 2.0 - vec2(1.0, 1.0); if (length(uv) > 1.0) { discard; } diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc index 82f6451ece..e768545285 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc @@ -4,7 +4,7 @@ vec3 a_normal : NORMAL; vec3 v_normal : NORMAL; vec4 v_color : COLOR0; -vec2 v_texcoord1 : TEXCOORD1; +vec2 v_texcoord0 : TEXCOORD0; -vec3 v_worldPos : TEXCOORD3; // unused texcoord semantic -float v_discardFlag : TEXCOORD4; // unused texcoord semantic +vec3 v_worldPos : TEXCOORD1; // unused texcoord semantic +float v_discardFlag : TEXCOORD2; // unused texcoord semantic diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc index 1222a3fb64..28e09939f1 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc @@ -21,7 +21,7 @@ ****************************************************************************/ $input a_position, a_color0, a_normal, a_texcoord0 -$output v_normal, v_color, v_texcoord1 +$output v_normal, v_color, v_texcoord0 // cross section $output v_worldPos, v_discardFlag @@ -50,7 +50,7 @@ void main() v_color = a_color0; // quad parametrization - v_texcoord1 = quadUv; + v_texcoord0 = quadUv; // discard flag - used to discard the whole vertex, but in fragment shader v_discardFlag = computeDiscardFlag(v_worldPos); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/fs_points_instance_id.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/fs_points_instance_id.sc index 69f803804a..c7468ffc6c 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/fs_points_instance_id.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/fs_points_instance_id.sc @@ -20,15 +20,21 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ -$input v_texcoord1 +$input v_texcoord0 + +// cross section +$input v_worldPos, v_discardFlag #include +#include void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + // circle mode (if outside of the circle, discard) if (bool(u_pointsMode & posToBitFlag(VCL_MRS_POINTS_CIRCLE))) { - if (length(2.0 * v_texcoord1 - vec2(1.0, 1.0)) > 1.0) { + if (length(2.0 * v_texcoord0 - vec2(1.0, 1.0)) > 1.0) { discard; } } diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/varying.def.sc index f2d31c1f10..93b6434c18 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/varying.def.sc @@ -1,3 +1,6 @@ vec3 a_position : POSITION; -vec2 v_texcoord1 : TEXCOORD1; +vec2 v_texcoord0 : TEXCOORD0; + +vec3 v_worldPos : TEXCOORD1; // unused texcoord semantic +float v_discardFlag : TEXCOORD2; // unused texcoord semantic diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/vs_points_instance_id.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/vs_points_instance_id.sc index 07e656561d..c1b75ad86c 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/vs_points_instance_id.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/vs_points_instance_id.sc @@ -21,9 +21,13 @@ ****************************************************************************/ $input a_position -$output v_texcoord1 +$output v_texcoord0 + +// cross section +$output v_worldPos, v_discardFlag #include +#include void main() { @@ -37,9 +41,13 @@ void main() (2.0 * quadUv.y - 1.0) * u_pointWidth * u_viewTexel.y, // is divided by 2 0, 0); + v_worldPos = mul(u_model[0], vec4(a_position, 1.0)).xyz; pos = pos / pos.w; gl_Position = pos + offset; // quad parametrization - v_texcoord1 = quadUv; + v_texcoord0 = quadUv; + + // discard flag - used to discard the whole vertex, but in fragment shader + v_discardFlag = computeDiscardFlag(v_worldPos); } diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/varying.def.sc index cbaadbb6fc..ca20ef2a35 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/varying.def.sc @@ -1,4 +1,4 @@ vec3 a_position : POSITION; -vec3 v_worldPos : TEXCOORD3; // unused texcoord semantic -float v_discardFlag : TEXCOORD4; // unused texcoord semantic +vec3 v_worldPos : TEXCOORD0; // unused texcoord semantic +float v_discardFlag : TEXCOORD1; // unused texcoord semantic diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc index 44b27c1499..6ef87da284 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc @@ -8,7 +8,7 @@ vec3 a_texcoord1 : TEXCOORD1; // n1 vec4 v_color : COLOR0; vec3 v_normal : NORMAL; -vec3 v_worldPos0 : TEXCOORD2; // unused texcoord semantic +vec3 v_worldPos0 : TEXCOORD2; // unused texcoord semantic vec3 v_worldPos1 : TEXCOORD3; // unused texcoord semantic float v_discardFlag : TEXCOORD4; // unused texcoord semantic float v_t : TEXCOORD5; // interpolation parameter along segment From 8203e19d8c16bb58da11c717532e3933d4bdb64a Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Wed, 15 Apr 2026 16:09:46 +0200 Subject: [PATCH 24/49] [render] cross section on primitive lines --- .../lines/primitive_lines/fs_primitive_lines.sc | 6 ++++++ .../bgfx/primitives/lines/primitive_lines/varying.def.sc | 3 +++ .../lines/primitive_lines/vs_primitive_lines.sc | 8 ++++++++ 3 files changed, 17 insertions(+) diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/fs_primitive_lines.sc b/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/fs_primitive_lines.sc index af7d51edcf..3591b60d60 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/fs_primitive_lines.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/fs_primitive_lines.sc @@ -22,6 +22,10 @@ $input v_color, v_normal +// cross section +$input v_worldPos, v_discardFlag + +#include #include #include #include @@ -35,6 +39,8 @@ BUFFER_RO(edgesColors, uint, 0); #define vertexColor v_color void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + vec4 color; if (colorToUse == 0) color = vertexColor; else if (colorToUse == 1) color = edgeColor; diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/varying.def.sc b/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/varying.def.sc index 06ab60c5c5..162152b0c5 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/varying.def.sc @@ -4,3 +4,6 @@ vec4 a_color0 : COLOR0; // c0 vec4 v_color : COLOR0; // vertex color vec3 v_normal : NORMAL; + +vec3 v_worldPos : TEXCOORD0; // unused texcoord semantic +float v_discardFlag : TEXCOORD1; // unused texcoord semantic diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/vs_primitive_lines.sc b/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/vs_primitive_lines.sc index cbc7d002e2..845e03f203 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/vs_primitive_lines.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/vs_primitive_lines.sc @@ -23,9 +23,13 @@ $input a_position, a_normal, a_color0 $output v_color, v_normal +// cross section +$output v_worldPos, v_discardFlag + #include #include +#include #include #include @@ -36,7 +40,11 @@ $output v_color, v_normal void main() { v_color = color; v_normal = normalize(mul(u_normalMatrix, normal)); + v_worldPos = mul(u_model[0], vec4(a_position, 1.0)).xyz; vec4 pos = mul(u_modelViewProj, vec4(p, 1.0)); pos.z += -u_depthOffset * pos.w; gl_Position = pos; + + // discard flag - used to discard the whole vertex, but in fragment shader + v_discardFlag = computeDiscardFlag(v_worldPos); } From 4b4d736fa2f3d5d7331375eb8ec0a1a1524089a7 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Wed, 15 Apr 2026 16:20:46 +0200 Subject: [PATCH 25/49] [render] small fixes --- vclib/render/include/vclib/bgfx/primitives/lines.h | 11 ++++++++++- .../vclib/render/drawable/abstract_drawable_mesh.h | 4 ---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/primitives/lines.h b/vclib/render/include/vclib/bgfx/primitives/lines.h index 9b0d7a7cdb..0609dc21ad 100644 --- a/vclib/render/include/vclib/bgfx/primitives/lines.h +++ b/vclib/render/include/vclib/bgfx/primitives/lines.h @@ -390,11 +390,20 @@ class Lines */ Color& generalColor() { return mGeneralColor; } - CrossSectionSettings crossSectionSettings() const + /** + * @brief Returns the current cross section settings. + * @return The current cross section settings. + */ + const CrossSectionSettings& crossSectionSettings() const { return mCrossSectionSettings; } + /** + * @brief Returns a reference to the current cross section settings. This + * allows to modify the settings directly. + * @return A reference to the current cross section settings. + */ CrossSectionSettings& crossSectionSettings() { return mCrossSectionSettings; diff --git a/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h b/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h index 006521b8af..bb7cbb4fb6 100644 --- a/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h +++ b/vclib/render/include/vclib/render/drawable/abstract_drawable_mesh.h @@ -40,10 +40,6 @@ namespace vcl { */ class AbstractDrawableMesh : public vcl::DrawableObject { -public: - enum class CrossSectionType { NONE, PER_VERTEX, PER_FRAGMENT }; - -private: inline static const Image EMPTY_IMAGE; protected: From 6e5c88a607dc54467470a2fa4cf3a1072d370327 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Wed, 15 Apr 2026 16:28:54 +0200 Subject: [PATCH 26/49] [render] enable cross section for all remaining surface shaders --- .../shaders/vclib/bgfx/drawable/drawable_mesh/surface/flat.sh | 1 + .../drawable_mesh/surface/fs_surface_flat_color_face.sc | 2 ++ .../drawable_mesh/surface/fs_surface_flat_color_mesh.sc | 2 ++ .../drawable_mesh/surface/fs_surface_flat_color_user.sc | 2 ++ .../drawable_mesh/surface/fs_surface_flat_color_vertex.sc | 2 ++ .../drawable_mesh/surface/fs_surface_flat_tex_vertex.sc | 2 ++ .../drawable/drawable_mesh/surface/fs_surface_flat_tex_wedge.sc | 2 ++ .../drawable_mesh/surface/fs_surface_none_color_face.sc | 2 ++ .../drawable_mesh/surface/fs_surface_none_color_mesh.sc | 2 ++ .../drawable_mesh/surface/fs_surface_none_color_user.sc | 2 ++ .../drawable_mesh/surface/fs_surface_none_color_vertex.sc | 2 ++ .../drawable_mesh/surface/fs_surface_none_tex_vertex.sc | 2 ++ .../drawable/drawable_mesh/surface/fs_surface_none_tex_wedge.sc | 2 ++ .../drawable_mesh/surface/fs_surface_smooth_color_face.sc | 2 ++ .../drawable_mesh/surface/fs_surface_smooth_color_mesh.sc | 2 ++ .../drawable_mesh/surface/fs_surface_smooth_color_user.sc | 2 ++ .../drawable_mesh/surface/fs_surface_smooth_color_vertex.sc | 2 ++ .../drawable_mesh/surface/fs_surface_smooth_tex_vertex.sc | 2 ++ .../drawable_mesh/surface/fs_surface_smooth_tex_wedge.sc | 2 ++ .../shaders/vclib/bgfx/drawable/drawable_mesh/surface/none.sh | 1 + .../shaders/vclib/bgfx/drawable/drawable_mesh/surface/smooth.sh | 1 + 21 files changed, 39 insertions(+) diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/flat.sh b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/flat.sh index ae567e3f1c..19f68eb489 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/flat.sh +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/flat.sh @@ -1,5 +1,6 @@ #include #include +#include BUFFER_RO(primitiveNormals, float, VCL_MRB_PRIMITIVE_NORMAL_BUFFER); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_face.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_face.sc index d46c8a2af4..cbd7bfa4ac 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_face.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_face.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_mesh.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_mesh.sc index c2da6fc330..31ab7568c6 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_mesh.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_mesh.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_user.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_user.sc index efe2774f3d..0c67178c82 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_user.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_user.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_vertex.sc index 55935ba1f9..9aa8fb331d 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_color_vertex.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_vertex.sc index 7ff513c9b0..88a7558b71 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_vertex.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_wedge.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_wedge.sc index 11c3ff4dfc..c7f57f11c4 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_wedge.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_flat_tex_wedge.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_face.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_face.sc index 13d3dbe4cd..1f16dc5eff 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_face.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_face.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_mesh.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_mesh.sc index c8fd419b50..2e5e3a0fab 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_mesh.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_mesh.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_user.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_user.sc index 3bdc461d4d..189eef640b 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_user.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_user.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_vertex.sc index ceda415289..9fcd06a5c6 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_color_vertex.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_vertex.sc index 8f20ac1903..8e857906e8 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_vertex.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_wedge.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_wedge.sc index 2757ef4b3f..77c339e7c7 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_wedge.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_none_tex_wedge.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_face.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_face.sc index db9605af55..6607fd7f67 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_face.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_face.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_mesh.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_mesh.sc index dcacfc7b15..5a96f28166 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_mesh.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_mesh.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_user.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_user.sc index 7aeb11d1c1..7aa0045fd8 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_user.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_user.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_vertex.sc index 40e456f3c8..038f6821a0 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_color_vertex.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_vertex.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_vertex.sc index 2a5da58c93..d666f73aae 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_vertex.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_vertex.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_wedge.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_wedge.sc index eb3d09c04e..0d9a3abb5b 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_wedge.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_smooth_tex_wedge.sc @@ -8,6 +8,8 @@ $input v_worldPos, v_discardFlag void main() { + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); + SHADING COLORING gl_FragColor = light * color + vec4(specular, 0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/none.sh b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/none.sh index e510d9ba14..f942228f4f 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/none.sh +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/none.sh @@ -1,4 +1,5 @@ #include +#include #define SHADING float depthOffset = 0.0;\ vec3 specular = vec3(0.0, 0.0, 0.0);\ diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/smooth.sh b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/smooth.sh index bc1e2cbd13..4fa6004743 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/smooth.sh +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/smooth.sh @@ -1,4 +1,5 @@ #include +#include #define SHADING float depthOffset = 0.0;\ vec3 normal = normalize(v_normal);\ From 5519c7427664446f8af812424002b984241c4493 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 17 Apr 2026 09:11:52 +0200 Subject: [PATCH 27/49] [space] add isStrictlyBelow and isStrictlyAbove member functions to Point class --- vclib/core/include/vclib/space/core/point.h | 34 +++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/vclib/core/include/vclib/space/core/point.h b/vclib/core/include/vclib/space/core/point.h index 50bb628179..a75d6c2b8a 100644 --- a/vclib/core/include/vclib/space/core/point.h +++ b/vclib/core/include/vclib/space/core/point.h @@ -412,6 +412,40 @@ class Point : public Eigen::Matrix at(i) = args[i]; } + /** + * @brief Returns true if this point is strictly below another point p1, + * meaning that all the components of this point are strictly less than the + * corresponding components of p1. + * + * @param[in] p1: The Point object to compare with. + * @return true if this point is strictly below p1, false otherwise. + */ + bool isStrictlyBelow(const Point& p1) const + { + for (uint i = 0; i < N; i++) { + if (at(i) >= p1(i)) + return false; + } + return true; + } + + /** + * @brief Returns true if this point is strictly above another point p1, + * meaning that all the components of this point are strictly greater than + * the corresponding components of p1. + * + * @param[in] p1: The Point object to compare with. + * @return true if this point is strictly above p1, false otherwise. + */ + bool isStrictlyAbove(const Point& p1) const + { + for (uint i = 0; i < N; i++) { + if (at(i) <= p1(i)) + return false; + } + return true; + } + /** * @brief Returns the outer product between this point p and p1, which is * p * p1^T From e969dfb213452f7ad6fdb420d8d38b2d5cbc321e Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 17 Apr 2026 09:12:59 +0200 Subject: [PATCH 28/49] [render] Add bounds checks and signal blocking to cross-section UI settings --- .../vclib/bgfx/drawable/drawable_mesh_bgfx.h | 3 + .../cross_section_settings_frame.h | 6 +- .../render/settings/cross_section_settings.h | 55 ++++++++++++++++--- .../cross_section_settings_frame.cpp | 53 ++++++++++++------ 4 files changed, 89 insertions(+), 28 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h index a6484c815a..ce054b4933 100644 --- a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h +++ b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h @@ -138,7 +138,10 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType mMRB.update(*this, buffersToUpdate); mMRS.setRenderCapabilityFrom(*this); setRenderSettings(mMRS); + + auto tmpType = mCSS.type(); mCSS = CrossSectionSettings(*this); + mCSS.type() = tmpType; } void setRenderSettings(const MeshRenderSettings& rs) override diff --git a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h index 63538fecbd..8e020d7c98 100644 --- a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h +++ b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h @@ -29,6 +29,8 @@ #include #include +#include + namespace vcl::qt { namespace Ui { @@ -51,8 +53,8 @@ class CrossSectionSettingsFrame : public QFrame public: explicit CrossSectionSettingsFrame( - const CrossSectionSettings css, - QWidget* parent = nullptr); + const CrossSectionSettings& css, + QWidget* parent = nullptr); explicit CrossSectionSettingsFrame(QWidget* parent = nullptr); diff --git a/vclib/render/include/vclib/render/settings/cross_section_settings.h b/vclib/render/include/vclib/render/settings/cross_section_settings.h index 8e98590b6e..5322d75331 100644 --- a/vclib/render/include/vclib/render/settings/cross_section_settings.h +++ b/vclib/render/include/vclib/render/settings/cross_section_settings.h @@ -76,28 +76,67 @@ class CrossSectionSettings const Point3f& upper() const { return mUpper; } + /** + * @brief Sets the bounding box for the cross-section settings. + * + * The bounding box is updated to the provided value, and the lower and + * upper points are adjusted to be within the new bounding box if necessary. + * + * @param[in] bbox: The new bounding box to set. + */ void setBoundingBox(const Box3f& bbox) { mBBox = bbox; - if (mLower < bbox.min()) { - mLower = bbox.min(); - } - if (mUpper > bbox.max()) { - mUpper = bbox.max(); - } + mLower = mLower.cwiseMax(bbox.min()); + mUpper = mUpper.cwiseMin(bbox.max()); } + /** + * @brief Sets the lower point of the cross-section settings. + * + * The lower point is updated only if it is inside the bounding box and + * strictly below the upper point. + * + * @param[in] lower: The new lower point to set. + */ void setLower(const Point3f& lower) { - if (mBBox.isInside(lower)) { + if (mBBox.isInside(lower) && lower.isStrictlyBelow(mUpper)) { mLower = lower; } } + /** + * @brief Sets the upper point of the cross-section settings. + * + * The upper point is updated only if it is inside the bounding box and + * strictly above the lower point. + * + * @param[in] upper: The new upper point to set. + */ void setUpper(const Point3f& upper) { - if (mBBox.isInside(upper)) { + if (mBBox.isInside(upper) && upper.isStrictlyAbove(mLower)) { + mUpper = upper; + } + } + + /** + * @brief Sets both the lower and upper points of the cross-section + * settings. + * + * Both points are updated only if they are inside the bounding box and + * the lower point is strictly below the upper point. + * + * @param[in] lower: The new lower point to set. + * @param[in] upper: The new upper point to set. + */ + void setLowerUpper(const Point3f& lower, const Point3f& upper) + { + if (mBBox.isInside(lower) && mBBox.isInside(upper) && + lower.isStrictlyBelow(upper)) { + mLower = lower; mUpper = upper; } } diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp index 6ba9fd029a..3788ff3eb1 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp @@ -27,8 +27,8 @@ namespace vcl::qt { CrossSectionSettingsFrame::CrossSectionSettingsFrame( - const CrossSectionSettings css, - QWidget* parent) : + const CrossSectionSettings& css, + QWidget* parent) : QFrame(parent), mUI(new Ui::CrossSectionSettingsFrame), mCSS(css) { mUI->setupUi(this); @@ -125,15 +125,33 @@ void CrossSectionSettingsFrame::updateFrameFromSettings() { using enum CrossSectionSettings::CrossSectionType; + // checkbox + mUI->crosSectionEnabledCheckBox->blockSignals(true); + + if (mCSS.type() == NONE) + mUI->crosSectionEnabledCheckBox->setCheckState( + Qt::CheckState::Unchecked); + else + mUI->crosSectionEnabledCheckBox->setCheckState(Qt::CheckState::Checked); + + mUI->crosSectionEnabledCheckBox->blockSignals(false); + // radio buttons + mUI->perVertexRadioButton->blockSignals(true); + mUI->perFragmentRadioButton->blockSignals(true); + if (mCSS.type() == PER_VERTEX) mUI->perVertexRadioButton->setChecked(true); else if (mCSS.type() == PER_FRAGMENT) mUI->perFragmentRadioButton->setChecked(true); - auto bb = mCSS.boundingBox(); - auto l = mCSS.lower(); - auto u = mCSS.upper(); + mUI->perVertexRadioButton->blockSignals(false); + mUI->perFragmentRadioButton->blockSignals(false); + + // sliders and spinboxes + const auto& bb = mCSS.boundingBox(); + const auto& l = mCSS.lower(); + const auto& u = mCSS.upper(); for (uint i = X; i < AXIS_COUNT; ++i) { updateSlider( @@ -145,13 +163,6 @@ void CrossSectionSettingsFrame::updateFrameFromSettings() l[i], u[i]); } - - // checkbox, last thing to do - if (mCSS.type() == NONE) - mUI->crosSectionEnabledCheckBox->setCheckState( - Qt::CheckState::Unchecked); - else - mUI->crosSectionEnabledCheckBox->setCheckState(Qt::CheckState::Checked); } void CrossSectionSettingsFrame::updateSlider( @@ -226,9 +237,10 @@ void CrossSectionSettingsFrame::onPerFragmentToggled(bool checked) } void CrossSectionSettingsFrame::onFloatRangeSliderLowerValueChanged( - Axis axis, float value) + Axis axis, + float value) { - auto l = mCSS.lower(); + auto l = mCSS.lower(); l[axis] = value; mCSS.setLower(l); @@ -241,9 +253,10 @@ void CrossSectionSettingsFrame::onFloatRangeSliderLowerValueChanged( } void CrossSectionSettingsFrame::onFloatRangeSliderUpperValueChanged( - Axis axis, float value) + Axis axis, + float value) { - auto u = mCSS.upper(); + auto u = mCSS.upper(); u[axis] = value; mCSS.setUpper(u); @@ -255,12 +268,16 @@ void CrossSectionSettingsFrame::onFloatRangeSliderUpperValueChanged( emit crossSectionSettingsUpdated(); } -void CrossSectionSettingsFrame::onMinSpinBoxValueChanged(Axis axis, double value) +void CrossSectionSettingsFrame::onMinSpinBoxValueChanged( + Axis axis, + double value) { mFloatSliders[axis]->setLowerValue(static_cast(value)); } -void CrossSectionSettingsFrame::onMaxSpinBoxValueChanged(Axis axis, double value) +void CrossSectionSettingsFrame::onMaxSpinBoxValueChanged( + Axis axis, + double value) { mFloatSliders[axis]->setUpperValue(static_cast(value)); } From 5d7b8b5a72f348398ab5e09d0b2ba37b339d1863 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 17 Apr 2026 12:30:57 +0200 Subject: [PATCH 29/49] [render] Improve cross section settings handling and UI state synchronization --- .../vclib/bgfx/drawable/drawable_mesh_bgfx.h | 1 + .../render/settings/cross_section_settings.h | 21 +++++++++++++--- .../cross_section_settings_frame.cpp | 8 +++++-- vclib/render/src/vclib/qt/mesh_viewer.cpp | 24 +++++++++++-------- 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h index ce054b4933..b067cbaa41 100644 --- a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h +++ b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h @@ -142,6 +142,7 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType auto tmpType = mCSS.type(); mCSS = CrossSectionSettings(*this); mCSS.type() = tmpType; + mMRB.updateCrossSectionSettings(mCSS); } void setRenderSettings(const MeshRenderSettings& rs) override diff --git a/vclib/render/include/vclib/render/settings/cross_section_settings.h b/vclib/render/include/vclib/render/settings/cross_section_settings.h index 5322d75331..1af6f0ab1e 100644 --- a/vclib/render/include/vclib/render/settings/cross_section_settings.h +++ b/vclib/render/include/vclib/render/settings/cross_section_settings.h @@ -86,10 +86,25 @@ class CrossSectionSettings */ void setBoundingBox(const Box3f& bbox) { - mBBox = bbox; + if (!bbox.isNull()) { + mBBox = bbox; - mLower = mLower.cwiseMax(bbox.min()); - mUpper = mUpper.cwiseMin(bbox.max()); + Point3f clampedLower = + mLower.cwiseMax(bbox.min()).cwiseMin(bbox.max()); + Point3f clampedUpper = + mUpper.cwiseMax(bbox.min()).cwiseMin(bbox.max()); + + mLower = clampedLower.cwiseMin(clampedUpper); + mUpper = clampedLower.cwiseMax(clampedUpper); + + if (!mLower.isStrictlyBelow(mUpper)) { + mLower = bbox.min(); + mUpper = bbox.max(); + } + } + else { + mBBox = Box3f(Point3f::min(), Point3f::max()); + } } /** diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp index 3788ff3eb1..82bc748b63 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp @@ -128,11 +128,15 @@ void CrossSectionSettingsFrame::updateFrameFromSettings() // checkbox mUI->crosSectionEnabledCheckBox->blockSignals(true); - if (mCSS.type() == NONE) + if (mCSS.type() == NONE) { mUI->crosSectionEnabledCheckBox->setCheckState( Qt::CheckState::Unchecked); - else + mUI->controlsFrame->setEnabled(false); + } + else { mUI->crosSectionEnabledCheckBox->setCheckState(Qt::CheckState::Checked); + mUI->controlsFrame->setEnabled(true); + } mUI->crosSectionEnabledCheckBox->blockSignals(false); diff --git a/vclib/render/src/vclib/qt/mesh_viewer.cpp b/vclib/render/src/vclib/qt/mesh_viewer.cpp index 51a97f0fbe..6ab67fcbb4 100644 --- a/vclib/render/src/vclib/qt/mesh_viewer.cpp +++ b/vclib/render/src/vclib/qt/mesh_viewer.cpp @@ -274,11 +274,13 @@ void MeshViewer::meshRenderSettingsUpdated() // visible only when the selected Object is a AbstractDrawableMesh auto m = std::dynamic_pointer_cast( mDrawableObjectVector->at(i)); - // get RenderSettings from the RenderSettingsFrame, and set it to the - // AbstractDrawableMesh - m->setRenderSettings( - mUI->meshRenderSettingsFrame->meshRenderSettings()); - mUI->viewer->update(); + if (m) { // just to be sure, but it should always be true + // get RenderSettings from the RenderSettingsFrame, and set it to + // the AbstractDrawableMesh + m->setRenderSettings( + mUI->meshRenderSettingsFrame->meshRenderSettings()); + mUI->viewer->update(); + } } } @@ -292,11 +294,13 @@ void MeshViewer::crossSectionSettingsUpdated() // visible only when the selected Object is a AbstractDrawableMesh auto m = std::dynamic_pointer_cast( mDrawableObjectVector->at(i)); - // get CrossSectionSettings from the CrossSectionSettingsFrame, and set - // it to the AbstractDrawableMesh - m->setCrossSectionSettings( - mUI->meshRenderSettingsFrame->crossSectionSettings()); - mUI->viewer->update(); + if (m) { // just to be sure, but it should always be true + // get CrossSectionSettings from the CrossSectionSettingsFrame, and + // set it to the AbstractDrawableMesh + m->setCrossSectionSettings( + mUI->meshRenderSettingsFrame->crossSectionSettings()); + mUI->viewer->update(); + } } } From 2a75ff48c57c359d07ecb6115ea2f93bed727f65 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 17 Apr 2026 14:18:17 +0200 Subject: [PATCH 30/49] [space] isStrictly[Below/Above] member functions renamed to all[Less/Greater]Than, and add all[Less/Greater]EqualThan member functions to Point class --- vclib/core/include/vclib/space/core/point.h | 46 +++++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/vclib/core/include/vclib/space/core/point.h b/vclib/core/include/vclib/space/core/point.h index a75d6c2b8a..1cb5b542c8 100644 --- a/vclib/core/include/vclib/space/core/point.h +++ b/vclib/core/include/vclib/space/core/point.h @@ -413,14 +413,13 @@ class Point : public Eigen::Matrix } /** - * @brief Returns true if this point is strictly below another point p1, - * meaning that all the components of this point are strictly less than the - * corresponding components of p1. + * @brief Returns true if all the components of this point are strictly less + * than the corresponding components of another point p1. * * @param[in] p1: The Point object to compare with. * @return true if this point is strictly below p1, false otherwise. */ - bool isStrictlyBelow(const Point& p1) const + bool allLessThan(const Point& p1) const { for (uint i = 0; i < N; i++) { if (at(i) >= p1(i)) @@ -430,14 +429,29 @@ class Point : public Eigen::Matrix } /** - * @brief Returns true if this point is strictly above another point p1, - * meaning that all the components of this point are strictly greater than - * the corresponding components of p1. + * @brief Returns true if all the components of this point are less than or + * equal to the corresponding components of another point p1. + * + * @param[in] p1: The Point object to compare with. + * @return true if this point is below or equal to p1, false otherwise. + */ + bool allLessEqualThan(const Point& p1) const + { + for (uint i = 0; i < N; i++) { + if (at(i) > p1(i)) + return false; + } + return true; + } + + /** + * @brief Returns true if all the components of this point are strictly + * greater than the corresponding components of another point p1. * * @param[in] p1: The Point object to compare with. * @return true if this point is strictly above p1, false otherwise. */ - bool isStrictlyAbove(const Point& p1) const + bool allGreaterThan(const Point& p1) const { for (uint i = 0; i < N; i++) { if (at(i) <= p1(i)) @@ -446,6 +460,22 @@ class Point : public Eigen::Matrix return true; } + /** + * @brief Returns true if all the components of this point are greater than + * or equal to the corresponding components of another point p1. + * + * @param[in] p1: The Point object to compare with. + * @return true if this point is above or equal to p1, false otherwise. + */ + bool allGreaterEqualThan(const Point& p1) const + { + for (uint i = 0; i < N; i++) { + if (at(i) < p1(i)) + return false; + } + return true; + } + /** * @brief Returns the outer product between this point p and p1, which is * p * p1^T From b6fb4d6614372526208640e6c0e5c90da8ac2efd Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 17 Apr 2026 14:18:34 +0200 Subject: [PATCH 31/49] [render] Refactor CrossSectionSettings bbox logic and fix UI checkbox/spinbox names --- .../vclib/bgfx/drawable/drawable_mesh_bgfx.h | 4 +- .../render/settings/cross_section_settings.h | 62 +++++++++++++------ .../cross_section_settings_frame.cpp | 10 +-- .../cross_section_settings_frame.ui | 26 ++++++-- 4 files changed, 70 insertions(+), 32 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h index b067cbaa41..ac96a2d097 100644 --- a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h +++ b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h @@ -139,9 +139,7 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType mMRS.setRenderCapabilityFrom(*this); setRenderSettings(mMRS); - auto tmpType = mCSS.type(); - mCSS = CrossSectionSettings(*this); - mCSS.type() = tmpType; + mCSS.setBoundingBox(*this); mMRB.updateCrossSectionSettings(mCSS); } diff --git a/vclib/render/include/vclib/render/settings/cross_section_settings.h b/vclib/render/include/vclib/render/settings/cross_section_settings.h index 1af6f0ab1e..7271a861ac 100644 --- a/vclib/render/include/vclib/render/settings/cross_section_settings.h +++ b/vclib/render/include/vclib/render/settings/cross_section_settings.h @@ -46,22 +46,9 @@ class CrossSectionSettings CrossSectionSettings() = default; template - CrossSectionSettings(const MeshType& m) + CrossSectionSettings(const MeshType& m, float epsilon = 0.02f) { - mBBox = vcl::boundingBox(m).template cast(); - if constexpr (HasTransformMatrix) { - Matrix44f transform = m.transformMatrix().template cast(); - mBBox = transformBox(mBBox, transform); - } - - // inflate bbox by epsilon of its diagonal - float eps = mBBox.diagonal() * 0.02f; - - mBBox.min() -= eps; - mBBox.max() += eps; - - mLower = mBBox.min(); - mUpper = mBBox.max(); + setBoundingBox(m, epsilon); } bool isEnabled() const { return mType != CrossSectionType::NONE; } @@ -97,7 +84,7 @@ class CrossSectionSettings mLower = clampedLower.cwiseMin(clampedUpper); mUpper = clampedLower.cwiseMax(clampedUpper); - if (!mLower.isStrictlyBelow(mUpper)) { + if (!mLower.allLessEqualThan(mUpper)) { mLower = bbox.min(); mUpper = bbox.max(); } @@ -107,6 +94,43 @@ class CrossSectionSettings } } + /** + * @brief Sets the bounding box for the cross-section settings based on the + * given mesh. + * + * The bounding box is computed from the vertices of the provided mesh, and + * the lower and upper points are adjusted to be within the new bounding box + * if necessary. Bounding box is inflated by an epsilon of its diagonal to + * avoid numerical issues when the cross-section plane is very close to the + * bounding box. + * + * Model transformation is applied to the bounding box if the mesh has a + * transform matrix. + * + * @tparam MeshType: The type of the mesh for which to compute the bounding + * box. The mesh type must satisfy the MeshConcept. + * + * @param[in] m: The mesh from which to compute the bounding box. + * @param[in] epsilon: The epsilon value used to inflate the bounding box. + * It is expressed as a fraction of the bounding box diagonal. + */ + template + void setBoundingBox(const MeshType& m, float epsilon = 0.02f) + { + auto bbox = vcl::boundingBox(m).template cast(); + if constexpr (HasTransformMatrix) { + Matrix44f transform = m.transformMatrix().template cast(); + bbox = transformBox(bbox, transform); + } + // inflate bbox by epsilon of its diagonal + float eps = bbox.diagonal() * epsilon; + + bbox.min() -= eps; + bbox.max() += eps; + + setBoundingBox(bbox); + } + /** * @brief Sets the lower point of the cross-section settings. * @@ -117,7 +141,7 @@ class CrossSectionSettings */ void setLower(const Point3f& lower) { - if (mBBox.isInside(lower) && lower.isStrictlyBelow(mUpper)) { + if (mBBox.isInside(lower) && lower.allLessEqualThan(mUpper)) { mLower = lower; } } @@ -132,7 +156,7 @@ class CrossSectionSettings */ void setUpper(const Point3f& upper) { - if (mBBox.isInside(upper) && upper.isStrictlyAbove(mLower)) { + if (mBBox.isInside(upper) && upper.allGreaterEqualThan(mLower)) { mUpper = upper; } } @@ -150,7 +174,7 @@ class CrossSectionSettings void setLowerUpper(const Point3f& lower, const Point3f& upper) { if (mBBox.isInside(lower) && mBBox.isInside(upper) && - lower.isStrictlyBelow(upper)) { + lower.allLessEqualThan(upper)) { mLower = lower; mUpper = upper; } diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp index 82bc748b63..a03ba3c254 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp @@ -48,7 +48,7 @@ CrossSectionSettingsFrame::CrossSectionSettingsFrame( updateFrameFromSettings(); connect( - mUI->crosSectionEnabledCheckBox, + mUI->crossSectionEnabledCheckBox, SIGNAL(checkStateChanged(Qt::CheckState)), this, SLOT(onCrossSectionEnabledChanged(Qt::CheckState))); @@ -126,19 +126,19 @@ void CrossSectionSettingsFrame::updateFrameFromSettings() using enum CrossSectionSettings::CrossSectionType; // checkbox - mUI->crosSectionEnabledCheckBox->blockSignals(true); + mUI->crossSectionEnabledCheckBox->blockSignals(true); if (mCSS.type() == NONE) { - mUI->crosSectionEnabledCheckBox->setCheckState( + mUI->crossSectionEnabledCheckBox->setCheckState( Qt::CheckState::Unchecked); mUI->controlsFrame->setEnabled(false); } else { - mUI->crosSectionEnabledCheckBox->setCheckState(Qt::CheckState::Checked); + mUI->crossSectionEnabledCheckBox->setCheckState(Qt::CheckState::Checked); mUI->controlsFrame->setEnabled(true); } - mUI->crosSectionEnabledCheckBox->blockSignals(false); + mUI->crossSectionEnabledCheckBox->blockSignals(false); // radio buttons mUI->perVertexRadioButton->blockSignals(true); diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui index 8428ce13b0..5433f7b026 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui @@ -15,7 +15,7 @@ - + Enable Cross Section @@ -96,7 +96,11 @@ - + + + 0.010000000000000 + + @@ -158,13 +162,25 @@ - + + + 0.010000000000000 + + - + + + 0.010000000000000 + + - + + + 0.010000000000000 + + From 5f14a1522aaca1be15905aac4f30b63a98f08895 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 17 Apr 2026 14:49:32 +0200 Subject: [PATCH 32/49] [render] Add per-fragment flag to uniforms and handle null bounding boxes --- .../uniforms/cross_section_uniforms.h | 9 ++++---- .../render/settings/cross_section_settings.h | 21 +++++++++++-------- .../cross_section_settings_frame.cpp | 2 -- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h b/vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h index c2b4217cec..0bf9b5a6e1 100644 --- a/vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h +++ b/vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h @@ -35,7 +35,8 @@ class CrossSectionUniforms Point3f::min().x(), // x min Point3f::min().y(), // y min Point3f::min().z(), // z min - 0.0 // unused + 0.0 // per-fragment flag + // (0.0 = per-vertex, 1.0 = per-fragment) }; inline static std::array sCrossSectionMaxData = { @@ -52,9 +53,9 @@ class CrossSectionUniforms CrossSectionUniforms() = delete; static void set( - const Point3f& minPoint = Point3f::min(), - const Point3f& maxPoint = Point3f::max(), - bool perFragment = false) + const Point3f& minPoint = Point3f::min(), + const Point3f& maxPoint = Point3f::max(), + bool perFragment = false) { sCrossSectionMinData[0] = minPoint.x(); sCrossSectionMinData[1] = minPoint.y(); diff --git a/vclib/render/include/vclib/render/settings/cross_section_settings.h b/vclib/render/include/vclib/render/settings/cross_section_settings.h index 7271a861ac..6155f5e024 100644 --- a/vclib/render/include/vclib/render/settings/cross_section_settings.h +++ b/vclib/render/include/vclib/render/settings/cross_section_settings.h @@ -118,17 +118,20 @@ class CrossSectionSettings void setBoundingBox(const MeshType& m, float epsilon = 0.02f) { auto bbox = vcl::boundingBox(m).template cast(); - if constexpr (HasTransformMatrix) { - Matrix44f transform = m.transformMatrix().template cast(); - bbox = transformBox(bbox, transform); - } - // inflate bbox by epsilon of its diagonal - float eps = bbox.diagonal() * epsilon; - bbox.min() -= eps; - bbox.max() += eps; + if (!bbox.isNull()) { + if constexpr (HasTransformMatrix) { + Matrix44f transform = m.transformMatrix().template cast(); + bbox = transformBox(bbox, transform); + } + // inflate bbox by epsilon of its diagonal + float eps = bbox.diagonal() * epsilon; - setBoundingBox(bbox); + bbox.min() -= eps; + bbox.max() += eps; + + setBoundingBox(bbox); + } } /** diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp index a03ba3c254..1979ddf19c 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp @@ -45,8 +45,6 @@ CrossSectionSettingsFrame::CrossSectionSettingsFrame( mMaxSpinBoxes[Y] = mUI->yMaxSpinBox; mMaxSpinBoxes[Z] = mUI->zMaxSpinBox; - updateFrameFromSettings(); - connect( mUI->crossSectionEnabledCheckBox, SIGNAL(checkStateChanged(Qt::CheckState)), From 3dae7d0854342763de789d296b2529b8347d8c28 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 17 Apr 2026 16:28:32 +0200 Subject: [PATCH 33/49] [render] Initialize mCrossSectionFrame to nullptr and update frame on construction --- .../include/vclib/qt/gui/mesh_render_settings_frame.h | 2 +- .../include/vclib/render/settings/cross_section_settings.h | 6 +++--- .../cross_section_settings_frame.cpp | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h index fd9df70e3e..71d22d3cfd 100644 --- a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h +++ b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame.h @@ -47,7 +47,7 @@ class MeshRenderSettingsFrame : public QFrame MeshRenderSettings mMRS; std::vector mFrames; - CrossSectionSettingsFrame* mCrossSectionFrame; + CrossSectionSettingsFrame* mCrossSectionFrame = nullptr; public: explicit MeshRenderSettingsFrame(QWidget* parent = nullptr); diff --git a/vclib/render/include/vclib/render/settings/cross_section_settings.h b/vclib/render/include/vclib/render/settings/cross_section_settings.h index 6155f5e024..55e8f5c5b8 100644 --- a/vclib/render/include/vclib/render/settings/cross_section_settings.h +++ b/vclib/render/include/vclib/render/settings/cross_section_settings.h @@ -138,7 +138,7 @@ class CrossSectionSettings * @brief Sets the lower point of the cross-section settings. * * The lower point is updated only if it is inside the bounding box and - * strictly below the upper point. + * strictly below or equal to the upper point. * * @param[in] lower: The new lower point to set. */ @@ -153,7 +153,7 @@ class CrossSectionSettings * @brief Sets the upper point of the cross-section settings. * * The upper point is updated only if it is inside the bounding box and - * strictly above the lower point. + * strictly above or equal to the lower point. * * @param[in] upper: The new upper point to set. */ @@ -169,7 +169,7 @@ class CrossSectionSettings * settings. * * Both points are updated only if they are inside the bounding box and - * the lower point is strictly below the upper point. + * the lower point is strictly below or equal to the upper point. * * @param[in] lower: The new lower point to set. * @param[in] upper: The new upper point to set. diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp index 1979ddf19c..704bf37798 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp @@ -94,6 +94,8 @@ CrossSectionSettingsFrame::CrossSectionSettingsFrame( onMaxSpinBoxValueChanged(static_cast(i), value); }); } + + updateFrameFromSettings(); } CrossSectionSettingsFrame::CrossSectionSettingsFrame(QWidget* parent) : From 4b59cfe1dc0325c1e1f83e10c62474dc144f2218 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Sat, 18 Apr 2026 10:34:42 +0200 Subject: [PATCH 34/49] [render] cpu lines - Apply near-plane clipping to v_worldPos0 and v_worldPos1 variables --- .../cpu_generated_lines/vs_cpu_generated_lines.sc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/vs_cpu_generated_lines.sc b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/vs_cpu_generated_lines.sc index 3fa7f3108f..e14753872c 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/vs_cpu_generated_lines.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/vs_cpu_generated_lines.sc @@ -52,8 +52,9 @@ void main() { int generalIndex = gl_VertexID % 4; vec2 uv = vec2((generalIndex >> 1) & 0x1, generalIndex & 0x1); - v_worldPos0 = mul(u_model[0], vec4(p0, 1.0)).xyz; - v_worldPos1 = mul(u_model[0], vec4(p1, 1.0)).xyz; + // compute initial world positions + vec3 worldP0 = mul(u_model[0], vec4(p0, 1.0)).xyz; + vec3 worldP1 = mul(u_model[0], vec4(p1, 1.0)).xyz; // segment points in clip space vec4 p0_NDC = mul(u_modelViewProj, vec4(p0, 1.0)); @@ -72,12 +73,16 @@ void main() { vec3 clippedNormal0 = n0_NDC; vec3 clippedNormal1 = n1_NDC; + vec3 clippedWorldP0 = worldP0; + vec3 clippedWorldP1 = worldP1; + if (p0_NDC.w < NEAR_EPSILON) { float t = (NEAR_EPSILON - p0_NDC.w) / (p1_NDC.w - p0_NDC.w); clippedP0 = mix(p0_NDC, p1_NDC, t); clippedColor0 = mix(color0, color1, t); clippedNormal0 = mix(normal0, normal1, t); + clippedWorldP0 = mix(worldP0, worldP1, t); } if (p1_NDC.w < NEAR_EPSILON) { @@ -86,6 +91,7 @@ void main() { clippedP1 = mix(p1_NDC, p0_NDC, t); clippedColor1 = mix(color1, color0, t); clippedNormal1 = mix(normal1, normal0, t); + clippedWorldP1 = mix(worldP1, worldP0, t); } // pick this vertex's position by uv.x along clipped segment */ @@ -113,6 +119,9 @@ void main() { p.xy = p.xy + (offsetNDC * p.w); } + v_worldPos0 = clippedWorldP0; + v_worldPos1 = clippedWorldP1; + v_color = color; v_normal = normal; v_t = uv.x; From 943c1bc602b94d953749aa4f629e36d9c495aa3a Mon Sep 17 00:00:00 2001 From: Alessandro Muntoni Date: Sun, 7 Jun 2026 09:35:10 +0200 Subject: [PATCH 35/49] [render] fix dx build error --- .../drawable_mesh/points_instance/fs_points_instance.sc | 2 +- .../drawable/drawable_mesh/points_instance/varying.def.sc | 1 - .../drawable_mesh/points_instance/vs_points_instance.sc | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc index 87e0bdcc5e..6c78e433d8 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/fs_points_instance.sc @@ -20,7 +20,7 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ -$input v_normal, v_color, v_texcoord0 +$input v_normal, v_texcoord0 // cross section $input v_worldPos, v_discardFlag diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc index 09e62dce69..8e7052a465 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc @@ -2,7 +2,6 @@ vec3 a_position : POSITION; vec3 a_normal : NORMAL; vec3 v_normal : NORMAL; -vec4 v_color : COLOR0; vec2 v_texcoord0 : TEXCOORD0; vec3 v_worldPos : TEXCOORD1; // unused texcoord semantic diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc index 2f6975042a..d337d8e588 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc @@ -20,8 +20,8 @@ * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. * ****************************************************************************/ -$input a_position, a_color0, a_normal, a_texcoord0 -$output v_normal, v_color, v_texcoord0 +$input a_position, a_normal +$output v_normal, v_texcoord0 // cross section $output v_worldPos, v_discardFlag From f03ff10ccfb8a5583139c70c92f8cdd423300aff Mon Sep 17 00:00:00 2001 From: Alessandro Muntoni Date: Sun, 7 Jun 2026 10:05:54 +0200 Subject: [PATCH 36/49] [render] fix CrossSectionSettings lower and upper reset when bbox set --- .../include/vclib/render/settings/cross_section_settings.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vclib/render/include/vclib/render/settings/cross_section_settings.h b/vclib/render/include/vclib/render/settings/cross_section_settings.h index 55e8f5c5b8..bb1567d74c 100644 --- a/vclib/render/include/vclib/render/settings/cross_section_settings.h +++ b/vclib/render/include/vclib/render/settings/cross_section_settings.h @@ -74,6 +74,9 @@ class CrossSectionSettings void setBoundingBox(const Box3f& bbox) { if (!bbox.isNull()) { + mLower = Point3f::min(); + mUpper = Point3f::max(); + mBBox = bbox; Point3f clampedLower = From a8bd1d9a936f4ad3102066392bd7d34f109075d1 Mon Sep 17 00:00:00 2001 From: Alessandro Muntoni Date: Mon, 29 Jun 2026 10:04:38 +0200 Subject: [PATCH 37/49] [render] fix msvc build error --- .../drawable/drawable_mesh/points_instance/vs_points_instance.sc | 1 + .../drawable_mesh/points_instance_id/vs_points_instance_id.sc | 1 + 2 files changed, 2 insertions(+) diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc index 1ba730f2d8..0c3d6212ba 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/vs_points_instance.sc @@ -28,6 +28,7 @@ void main() gl_Position = pos + offset; v_normal = normalize(mul(u_normalMatrix, a_normal)); + v_worldPos = mul(u_model[0], vec4(a_position, 1.0)).xyz; // quad parametrization v_texcoord0 = quadUv; diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/vs_points_instance_id.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/vs_points_instance_id.sc index 02fb110513..4ec640ff4a 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/vs_points_instance_id.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/vs_points_instance_id.sc @@ -27,6 +27,7 @@ void main() 0, 0); gl_Position = pos + offset; + v_worldPos = mul(u_model[0], vec4(a_position, 1.0)).xyz; // quad parametrization v_texcoord0 = quadUv; From 40aaf4576004930b03ca184f0eddca4ee4a51a97 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Wed, 1 Jul 2026 08:40:24 +0200 Subject: [PATCH 38/49] [render] fix not renamed settings updated signal --- .../vclib/qt/gui/mesh_render_settings_frame/surface_frame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/surface_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/surface_frame.cpp index b3ce56d8ac..12b4e8c334 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/surface_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/surface_frame.cpp @@ -196,7 +196,7 @@ void SurfaceFrame::onShadingNormalMapToggled(bool checked) { if (checked) { mMRS.setSurface(SHADING_NORMAL_MAP); - emit settingsUpdated(); + emit meshRenderSettingsUpdated(); } } From f53ae04bd044125df94c0b95804eba1e505566d0 Mon Sep 17 00:00:00 2001 From: Alessandro Muntoni Date: Wed, 1 Jul 2026 16:15:07 +0200 Subject: [PATCH 39/49] [render] Add section-enabled surface shading options and update shader configs --- .../vclib/bgfx/drawable/drawable_mesh_bgfx.h | 110 ++++++++++++------ .../drawable_mesh/surface/fs_surface_in.sh | 2 + .../drawable_mesh/surface/surface.config | 9 +- .../{vs_surface.sc => vs_surface_in.sh} | 6 +- 4 files changed, 88 insertions(+), 39 deletions(-) rename vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/{vs_surface.sc => vs_surface_in.sh} (95%) diff --git a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h index c297cd74a9..0545fdc946 100644 --- a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h +++ b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h @@ -460,63 +460,101 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType ProgramManager& pm = Context::instance().programManager(); - static const std::array surfaceProgramHandles = - {pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram(), - pm.getProgram()}; - uint mul = 0; - uint off = 0; + static const std::array surfaceProgramHandles = + {pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram(), + pm.getProgram()}; + + + uint section = 0; + uint shading = 0; + uint color = 0; + + const uint N_SECTION_TYPES = 2; + const uint N_SHADING_TYPES = 4; + const uint N_COLOR_TYPES = 6; { + if (mCSS.isEnabled()) { + section = 1; + } + using enum MeshRenderInfo::Surface; if (mMRS.isSurface(SHADING_FLAT)) { - mul = 1; + shading = 1; } if (mMRS.isSurface(SHADING_SMOOTH)) { - mul = 2; + shading = 2; } if (mMRS.isSurface(SHADING_NORMAL_MAP)) { - mul = 3; + shading = 3; } if (mMRS.isSurface(COLOR_MESH)) { - off = 1; + color = 1; } if (mMRS.isSurface(COLOR_FACE)) { - off = 2; + color = 2; } if (mMRS.isSurface(COLOR_USER)) { - off = 3; + color = 3; } if (mMRS.isSurface(COLOR_VERTEX_TEX)) { - off = 4; + color = 4; } if (mMRS.isSurface(COLOR_WEDGE_TEX)) { - off = 5; + color = 5; } } - VertFragProgram p = static_cast(6 * mul + off); + uint program = section * N_SHADING_TYPES * N_COLOR_TYPES + shading * N_COLOR_TYPES + color; + + VertFragProgram p = static_cast(program); return surfaceProgramHandles[toUnderlying(p)]; } diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_in.sh b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_in.sh index 7faf264a3d..175ec36dc8 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_in.sh +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_in.sh @@ -36,7 +36,9 @@ DECLARE_FETCH_VEC3(fetchPrimitiveNormal, primitiveNormals); void main() { +#ifdef SURFACE_SECTION_ENABLED discardIfCrossSectionClipped(v_discardFlag, v_worldPos); +#endif // color vec4 color = vec4(0.0, 0.0, 0.0, 1.0); diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface.config b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface.config index 79223d23b9..b63f77f6f9 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface.config +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface.config @@ -1,13 +1,18 @@ ENUM_PREFIX DRAWABLE_MESH_SURFACE DEFINE_PREFIX SURFACE -# VS is static (not generated for each combination) -VS_FILE vs_surface.sc +# VS is generated dynamically based on combinations +VS_IN vs_surface_in.sh +VS_PREFIX vs_surface # FS is generated dynamically based on combinations FS_IN fs_surface_in.sh FS_PREFIX fs_surface +DIM_BOTH SECTION + ENABLED + DISABLED + DIM_FS SHADING FLAT NONE diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface_in.sh similarity index 95% rename from vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc rename to vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface_in.sh index 7023dc54a0..aea0b71564 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface_in.sh @@ -18,7 +18,6 @@ void main() { gl_Position = mul(u_modelViewProj, vec4(a_position, 1.0)); v_position = mul(u_modelView, vec4(a_position, 1.0)).xyz; - v_worldPos = mul(u_model[0], vec4(a_position, 1.0)).xyz; v_normal = normalize(mul(u_normalMatrix, a_normal)); v_texcoord0 = a_texcoord0; v_texcoord1 = a_texcoord1; @@ -28,6 +27,11 @@ void main() // default case - color is taken from buffer v_color = a_color0; +#ifdef SURFACE_SECTION_ENABLED + v_worldPos = mul(u_model[0], vec4(a_position, 1.0)).xyz; // discard flag - used to discard the whole vertex, but in fragment shader v_discardFlag = computeDiscardFlag(v_worldPos); +#else + v_discardFlag = 1; +#endif } From eea26290a6ade10e307573749190f611a4f92851 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Wed, 1 Jul 2026 17:29:29 +0200 Subject: [PATCH 40/49] [render] fix dx error: initialize unused varying variables with default zero values --- .../bgfx/drawable/drawable_mesh/points/varying.def.sc | 4 ++-- .../bgfx/drawable/drawable_mesh/points_id/varying.def.sc | 4 ++-- .../drawable/drawable_mesh/points_instance/varying.def.sc | 4 ++-- .../drawable_mesh/points_instance_id/varying.def.sc | 4 ++-- .../bgfx/drawable/drawable_mesh/surface/varying.def.sc | 4 ++-- .../bgfx/drawable/drawable_mesh/surface_id/varying.def.sc | 4 ++-- .../drawable/drawable_mesh/surface_pbr/varying.def.sc | 4 ++-- .../primitives/lines/cpu_generated_lines/varying.def.sc | 8 ++++---- .../bgfx/primitives/lines/primitive_lines/varying.def.sc | 4 ++-- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/varying.def.sc index 02aaa3f794..38297e8aba 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points/varying.def.sc @@ -13,5 +13,5 @@ vec3 v_position : TANGENT; vec3 v_normal : NORMAL; vec4 v_color : COLOR0; -vec3 v_worldPos : TEXCOORD0; // unused texcoord semantic -float v_discardFlag : TEXCOORD1; // unused texcoord semantic +vec3 v_worldPos : TEXCOORD0 = vec3(0.0, 0.0, 0.0); +float v_discardFlag : TEXCOORD1 = 0; diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/varying.def.sc index 6bab48c21f..accca66c61 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_id/varying.def.sc @@ -7,5 +7,5 @@ vec3 a_position : POSITION; -vec3 v_worldPos : TEXCOORD0; // unused texcoord semantic -float v_discardFlag : TEXCOORD1; // unused texcoord semantic +vec3 v_worldPos : TEXCOORD0 = vec3(0.0, 0.0, 0.0); +float v_discardFlag : TEXCOORD1 = 0; diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc index c11bd498b8..187d84648c 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance/varying.def.sc @@ -11,5 +11,5 @@ vec3 a_normal : NORMAL; vec3 v_normal : NORMAL; vec2 v_texcoord0 : TEXCOORD0; -vec3 v_worldPos : TEXCOORD1; // unused texcoord semantic -float v_discardFlag : TEXCOORD2; // unused texcoord semantic +vec3 v_worldPos : TEXCOORD1 = vec3(0.0, 0.0, 0.0); +float v_discardFlag : TEXCOORD2 = 0; diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/varying.def.sc index cba34333ea..aa3ee3a154 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/points_instance_id/varying.def.sc @@ -9,5 +9,5 @@ vec3 a_position : POSITION; vec2 v_texcoord0 : TEXCOORD0; -vec3 v_worldPos : TEXCOORD1; // unused texcoord semantic -float v_discardFlag : TEXCOORD2; // unused texcoord semantic +vec3 v_worldPos : TEXCOORD1 = vec3(0.0, 0.0, 0.0); +float v_discardFlag : TEXCOORD2 = 0; diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc index 53b09298e3..c35f7fcb34 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/varying.def.sc @@ -19,5 +19,5 @@ vec4 v_color : COLOR0; vec2 v_texcoord0 : TEXCOORD0; vec2 v_texcoord1 : TEXCOORD1; -vec3 v_worldPos : TEXCOORD3; // unused texcoord semantic -float v_discardFlag : TEXCOORD4; // unused texcoord semantic +vec3 v_worldPos : TEXCOORD3 = vec3(0.0, 0.0, 0.0); +float v_discardFlag : TEXCOORD4 = 0; diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/varying.def.sc index 6bab48c21f..accca66c61 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_id/varying.def.sc @@ -7,5 +7,5 @@ vec3 a_position : POSITION; -vec3 v_worldPos : TEXCOORD0; // unused texcoord semantic -float v_discardFlag : TEXCOORD1; // unused texcoord semantic +vec3 v_worldPos : TEXCOORD0 = vec3(0.0, 0.0, 0.0); +float v_discardFlag : TEXCOORD1 = 0; diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_pbr/varying.def.sc b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_pbr/varying.def.sc index 53b09298e3..c35f7fcb34 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_pbr/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface_pbr/varying.def.sc @@ -19,5 +19,5 @@ vec4 v_color : COLOR0; vec2 v_texcoord0 : TEXCOORD0; vec2 v_texcoord1 : TEXCOORD1; -vec3 v_worldPos : TEXCOORD3; // unused texcoord semantic -float v_discardFlag : TEXCOORD4; // unused texcoord semantic +vec3 v_worldPos : TEXCOORD3 = vec3(0.0, 0.0, 0.0); +float v_discardFlag : TEXCOORD4 = 0; diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc index 6c1b80edf4..c3e38521a1 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/cpu_generated_lines/varying.def.sc @@ -15,7 +15,7 @@ vec3 a_texcoord1 : TEXCOORD1; // n1 vec4 v_color : COLOR0; vec3 v_normal : NORMAL; -vec3 v_worldPos0 : TEXCOORD2; // unused texcoord semantic -vec3 v_worldPos1 : TEXCOORD3; // unused texcoord semantic -float v_discardFlag : TEXCOORD4; // unused texcoord semantic -float v_t : TEXCOORD5; // interpolation parameter along segment +vec3 v_worldPos0 : TEXCOORD2 = vec3(0.0, 0.0, 0.0); +vec3 v_worldPos1 : TEXCOORD3 = vec3(0.0, 0.0, 0.0); +float v_discardFlag : TEXCOORD4 = 0; +float v_t : TEXCOORD5 = 0; // interpolation parameter along segment diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/varying.def.sc b/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/varying.def.sc index 4aeba197ca..e01f116bec 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/primitive_lines/varying.def.sc @@ -12,5 +12,5 @@ vec4 a_color0 : COLOR0; // c0 vec4 v_color : COLOR0; // vertex color vec3 v_normal : NORMAL; -vec3 v_worldPos : TEXCOORD0; // unused texcoord semantic -float v_discardFlag : TEXCOORD1; // unused texcoord semantic +vec3 v_worldPos : TEXCOORD0 = vec3(0.0, 0.0, 0.0); +float v_discardFlag : TEXCOORD1 = 0; From b7ba307a49d040b5643a2ac5aeedaf587665a013 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Thu, 2 Jul 2026 16:00:00 +0200 Subject: [PATCH 41/49] [render] Rename SECTION dimension from ENABLED/DISABLED to ON/OFF in shaders --- vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h | 2 +- .../bgfx/drawable/drawable_mesh/surface/fs_surface_in.sh | 2 +- .../vclib/bgfx/drawable/drawable_mesh/surface/surface.config | 4 ++-- .../bgfx/drawable/drawable_mesh/surface/vs_surface_in.sh | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h index 3ff06aa676..dae98ce68e 100644 --- a/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h +++ b/vclib/render/include/vclib/bgfx/drawable/drawable_mesh_bgfx.h @@ -505,7 +505,7 @@ class DrawableMeshBGFX : public AbstractDrawableMesh, public MeshType // the first shader of all the combinations uint base = toUnderlying( VertFragProgram:: - DRAWABLE_MESH_SURFACE_SECTION_ENABLED_SHADING_FLAT_COLOR_FACE); + DRAWABLE_MESH_SURFACE_SECTION_ON_SHADING_FLAT_COLOR_FACE); // matrix is generated from surface.config: // SECTION x SHADING x COLOR x SELECTION diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_in.sh b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_in.sh index 175ec36dc8..49ee2a36bd 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_in.sh +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/fs_surface_in.sh @@ -36,7 +36,7 @@ DECLARE_FETCH_VEC3(fetchPrimitiveNormal, primitiveNormals); void main() { -#ifdef SURFACE_SECTION_ENABLED +#ifdef SURFACE_SECTION_ON discardIfCrossSectionClipped(v_discardFlag, v_worldPos); #endif diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface.config b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface.config index b63f77f6f9..cabe63772d 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface.config +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/surface.config @@ -10,8 +10,8 @@ FS_IN fs_surface_in.sh FS_PREFIX fs_surface DIM_BOTH SECTION - ENABLED - DISABLED + ON + OFF DIM_FS SHADING FLAT diff --git a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface_in.sh b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface_in.sh index aea0b71564..a6db274983 100644 --- a/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface_in.sh +++ b/vclib/render/shaders/vclib/bgfx/drawable/drawable_mesh/surface/vs_surface_in.sh @@ -27,7 +27,7 @@ void main() // default case - color is taken from buffer v_color = a_color0; -#ifdef SURFACE_SECTION_ENABLED +#ifdef SURFACE_SECTION_ON v_worldPos = mul(u_model[0], vec4(a_position, 1.0)).xyz; // discard flag - used to discard the whole vertex, but in fragment shader v_discardFlag = computeDiscardFlag(v_worldPos); From 115faa8b2e395109b50cbdffea4c6f608b059c61 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Wed, 15 Jul 2026 17:40:18 +0200 Subject: [PATCH 42/49] [render] Synchronize cross section settings and bounding boxes in apply-to-all mode --- .../render/settings/cross_section_settings.h | 10 ++- vclib/render/src/vclib/qt/mesh_viewer.cpp | 72 +++++++++++++++++-- 2 files changed, 74 insertions(+), 8 deletions(-) diff --git a/vclib/render/include/vclib/render/settings/cross_section_settings.h b/vclib/render/include/vclib/render/settings/cross_section_settings.h index bb1567d74c..d486d347aa 100644 --- a/vclib/render/include/vclib/render/settings/cross_section_settings.h +++ b/vclib/render/include/vclib/render/settings/cross_section_settings.h @@ -74,8 +74,14 @@ class CrossSectionSettings void setBoundingBox(const Box3f& bbox) { if (!bbox.isNull()) { - mLower = Point3f::min(); - mUpper = Point3f::max(); + for (int i = 0; i < 3; ++i) { + if (mLower[i] <= mBBox.min()[i]) { + mLower[i] = bbox.min()[i]; + } + if (mUpper[i] >= mBBox.max()[i]) { + mUpper[i] = bbox.max()[i]; + } + } mBBox = bbox; diff --git a/vclib/render/src/vclib/qt/mesh_viewer.cpp b/vclib/render/src/vclib/qt/mesh_viewer.cpp index f906c74046..a08174cd05 100644 --- a/vclib/render/src/vclib/qt/mesh_viewer.cpp +++ b/vclib/render/src/vclib/qt/mesh_viewer.cpp @@ -292,10 +292,23 @@ void MeshViewer::updateGUI() if (!mUI->meshRenderSettingsFrame->isApplyToAllEnabled()) { mUI->meshRenderSettingsFrame->setMeshRenderSettings( m->renderSettings(), true); + mUI->meshRenderSettingsFrame->setCrossSectionSettings( + m->crossSectionSettings()); + } + else { + vcl::Box3f globalBox; + for (uint j = 0; j < mDrawableObjectVector->size(); ++j) { + auto mesh = std::dynamic_pointer_cast( + mDrawableObjectVector->at(j)); + if (mesh) { + globalBox.add(mesh->crossSectionSettings().boundingBox()); + } + } + auto css = mUI->meshRenderSettingsFrame->crossSectionSettings(); + css.setBoundingBox(globalBox); + mUI->meshRenderSettingsFrame->setCrossSectionSettings(css); } mUI->meshRenderSettingsFrame->setEnabled(true); - mUI->meshRenderSettingsFrame->setCrossSectionSettings( - m->crossSectionSettings()); } else { mUI->meshRenderSettingsFrame->setEnabled(false); @@ -324,6 +337,8 @@ void MeshViewer::visibilityDrawableObjectChanged() if (!mUI->meshRenderSettingsFrame->isApplyToAllEnabled()) { mUI->meshRenderSettingsFrame->setMeshRenderSettings( m->renderSettings()); + mUI->meshRenderSettingsFrame->setCrossSectionSettings( + m->crossSectionSettings()); } } mUI->viewer->update(); @@ -348,6 +363,8 @@ void MeshViewer::selectedDrawableObjectChanged(uint i) if (!mUI->meshRenderSettingsFrame->isApplyToAllEnabled()) { mUI->meshRenderSettingsFrame->setMeshRenderSettings( m->renderSettings()); + mUI->meshRenderSettingsFrame->setCrossSectionSettings( + m->crossSectionSettings()); } mUI->meshRenderSettingsFrame->setEnabled(true); } @@ -412,10 +429,21 @@ void MeshViewer::crossSectionSettingsUpdated() auto m = std::dynamic_pointer_cast( mDrawableObjectVector->at(i)); if (m) { // just to be sure, but it should always be true - // get CrossSectionSettings from the CrossSectionSettingsFrame, and - // set it to the AbstractDrawableMesh - m->setCrossSectionSettings( - mUI->meshRenderSettingsFrame->crossSectionSettings()); + bool applyToAll = mUI->meshRenderSettingsFrame->isApplyToAllEnabled(); + const auto& newSettings = mUI->meshRenderSettingsFrame->crossSectionSettings(); + + if (applyToAll) { + for (uint j = 0; j < mDrawableObjectVector->size(); ++j) { + auto mesh = std::dynamic_pointer_cast( + mDrawableObjectVector->at(j)); + if (mesh) { + mesh->setCrossSectionSettings(newSettings); + } + } + } + else { + m->setCrossSectionSettings(newSettings); + } mUI->viewer->update(); } } @@ -424,6 +452,23 @@ void MeshViewer::crossSectionSettingsUpdated() void MeshViewer::applyToAllToggled(bool checked) { if (!checked) { + // Restore individual bounding boxes for all meshes + for (uint j = 0; j < mDrawableObjectVector->size(); ++j) { + auto mesh = std::dynamic_pointer_cast( + mDrawableObjectVector->at(j)); + if (mesh) { + auto css = mesh->crossSectionSettings(); + auto bbox = mesh->boundingBox().cast(); + if (!bbox.isNull()) { + float eps = bbox.diagonal() * 0.02f; + bbox.min() -= eps; + bbox.max() += eps; + } + css.setBoundingBox(bbox); + mesh->setCrossSectionSettings(css); + } + } + uint i = mUI->drawVectorTree->selectedDrawableObject(); if (i != UINT_NULL && mDrawableObjectVector->size() > 0) { auto m = std::dynamic_pointer_cast( @@ -431,11 +476,26 @@ void MeshViewer::applyToAllToggled(bool checked) if (m) { mUI->meshRenderSettingsFrame->setMeshRenderSettings( m->renderSettings()); + mUI->meshRenderSettingsFrame->setCrossSectionSettings( + m->crossSectionSettings()); } } } else { + vcl::Box3f globalBox; + for (uint j = 0; j < mDrawableObjectVector->size(); ++j) { + auto mesh = std::dynamic_pointer_cast( + mDrawableObjectVector->at(j)); + if (mesh) { + globalBox.add(mesh->crossSectionSettings().boundingBox()); + } + } + auto css = mUI->meshRenderSettingsFrame->crossSectionSettings(); + css.setBoundingBox(globalBox); + mUI->meshRenderSettingsFrame->setCrossSectionSettings(css); + meshRenderSettingsUpdated(); + crossSectionSettingsUpdated(); } } From 2ad642130577b252f5f1f18b1efe105bc7264705 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Wed, 15 Jul 2026 18:58:10 +0200 Subject: [PATCH 43/49] [render] Add step size control for cross section precision with spin box and logic --- .../cross_section_settings_frame.h | 3 ++ .../cross_section_settings_frame.cpp | 49 +++++++++++++++++++ .../cross_section_settings_frame.ui | 37 ++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h index 8e020d7c98..3d3bd5908b 100644 --- a/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h +++ b/vclib/render/include/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.h @@ -78,10 +78,13 @@ class CrossSectionSettingsFrame : public QFrame float lower, float upper); + float mStep = -1.0f; + private slots: void onCrossSectionEnabledChanged(Qt::CheckState arg1); void onPerVertexToggled(bool checked); void onPerFragmentToggled(bool checked); + void onStepSpinBoxValueChanged(int value); void onFloatRangeSliderLowerValueChanged(Axis axis, float value); void onFloatRangeSliderUpperValueChanged(Axis axis, float value); diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp index 704bf37798..0f050abea6 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.cpp @@ -63,6 +63,12 @@ CrossSectionSettingsFrame::CrossSectionSettingsFrame( this, SLOT(onPerFragmentToggled(bool))); + connect( + mUI->stepSpinBox, + SIGNAL(valueChanged(int)), + this, + SLOT(onStepSpinBoxValueChanged(int))); + for (uint i = X; i < AXIS_COUNT; ++i) { connect( mFloatSliders[i], @@ -95,6 +101,8 @@ CrossSectionSettingsFrame::CrossSectionSettingsFrame( }); } + onStepSpinBoxValueChanged(mUI->stepSpinBox->value()); + updateFrameFromSettings(); } @@ -157,6 +165,20 @@ void CrossSectionSettingsFrame::updateFrameFromSettings() const auto& l = mCSS.lower(); const auto& u = mCSS.upper(); + if (!bb.isNull()) { + float minSize = std::numeric_limits::max(); + for (int i=0; i<3; ++i) { + if (bb.dim(i) > 1e-6f && bb.dim(i) < minSize) { + minSize = bb.dim(i); + } + } + if (minSize != std::numeric_limits::max()) { + int order = static_cast(std::floor(std::log10(minSize))); + int maxExp = order - 1; + mUI->stepSpinBox->setMaximum(maxExp); + } + } + for (uint i = X; i < AXIS_COUNT; ++i) { updateSlider( mFloatSliders[i], @@ -251,7 +273,10 @@ void CrossSectionSettingsFrame::onFloatRangeSliderLowerValueChanged( mMinSpinBoxes[axis]->blockSignals(true); mMinSpinBoxes[axis]->setValue(value); mMinSpinBoxes[axis]->blockSignals(false); + + mMaxSpinBoxes[axis]->blockSignals(true); mMaxSpinBoxes[axis]->setMinimum(value); + mMaxSpinBoxes[axis]->blockSignals(false); emit crossSectionSettingsUpdated(); } @@ -267,7 +292,10 @@ void CrossSectionSettingsFrame::onFloatRangeSliderUpperValueChanged( mMaxSpinBoxes[axis]->blockSignals(true); mMaxSpinBoxes[axis]->setValue(value); mMaxSpinBoxes[axis]->blockSignals(false); + + mMinSpinBoxes[axis]->blockSignals(true); mMinSpinBoxes[axis]->setMaximum(value); + mMinSpinBoxes[axis]->blockSignals(false); emit crossSectionSettingsUpdated(); } @@ -286,4 +314,25 @@ void CrossSectionSettingsFrame::onMaxSpinBoxValueChanged( mFloatSliders[axis]->setUpperValue(static_cast(value)); } +void CrossSectionSettingsFrame::onStepSpinBoxValueChanged(int exp) +{ + mStep = std::pow(10.0f, static_cast(exp)); + + int decimals = std::max(2, -exp + 1); + + for (uint i = X; i < AXIS_COUNT; ++i) { + mFloatSliders[i]->setStep(mStep); + + mMinSpinBoxes[i]->blockSignals(true); + mMinSpinBoxes[i]->setSingleStep(mStep); + mMinSpinBoxes[i]->setDecimals(decimals); + mMinSpinBoxes[i]->blockSignals(false); + + mMaxSpinBoxes[i]->blockSignals(true); + mMaxSpinBoxes[i]->setSingleStep(mStep); + mMaxSpinBoxes[i]->setDecimals(decimals); + mMaxSpinBoxes[i]->blockSignals(false); + } +} + } // namespace vcl::qt diff --git a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui index 5433f7b026..a93f730798 100644 --- a/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui +++ b/vclib/render/src/vclib/qt/gui/mesh_render_settings_frame/cross_section_settings_frame.ui @@ -222,6 +222,43 @@ + + + + + + Precision (10^x): + + + + + + + -6 + + + 6 + + + -2 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + From f7debb626b25d2202078ba32869f7ce67276f3a0 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 17 Jul 2026 19:26:39 +0200 Subject: [PATCH 44/49] [render] Add cross section support to point rendering and relevant shaders --- .../bgfx/drawable/mesh/mesh_render_buffers.h | 1 + .../include/vclib/bgfx/primitives/points.h | 28 ++++++++-- .../bgfx/primitives/points/fs_points_id_in.sh | 6 +++ .../bgfx/primitives/points/fs_points_in.sh | 6 +++ .../bgfx/primitives/points/points.config | 8 +++ .../bgfx/primitives/points/varying.def.sc | 10 ++-- .../bgfx/primitives/points/vs_points_in.sh | 9 ++++ .../src/vclib/bgfx/primitives/points.cpp | 53 ++++++++++++++++--- 8 files changed, 106 insertions(+), 15 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/drawable/mesh/mesh_render_buffers.h b/vclib/render/include/vclib/bgfx/drawable/mesh/mesh_render_buffers.h index 39a6b41912..e55a597aec 100644 --- a/vclib/render/include/vclib/bgfx/drawable/mesh/mesh_render_buffers.h +++ b/vclib/render/include/vclib/bgfx/drawable/mesh/mesh_render_buffers.h @@ -366,6 +366,7 @@ class MeshRenderBuffers : public MeshRenderData> { mEdgeLines.crossSectionSettings() = css; mWireframeLines.crossSectionSettings() = css; + mPoints.setCrossSectionSettings(css); } private: diff --git a/vclib/render/include/vclib/bgfx/primitives/points.h b/vclib/render/include/vclib/bgfx/primitives/points.h index 56d7e3cca1..1b016c6d45 100644 --- a/vclib/render/include/vclib/bgfx/primitives/points.h +++ b/vclib/render/include/vclib/bgfx/primitives/points.h @@ -13,6 +13,8 @@ #include #include +#include + namespace vcl { /** @@ -55,9 +57,10 @@ class Points Shading shading = Shading::NONE; Shape shape = Shape::SQUARE; Color generalColor = Color::Black; - float depthOffset = 0.0f; - Color selectionColor = Color(0x88FF9732, Color::Format::ABGR); - bool selectionVisibility = false; + float depthOffset = 0.0f; + Color selectionColor = Color(0x88FF9732, Color::Format::ABGR); + bool selectionVisibility = false; + CrossSectionSettings crossSectionSettings; }; private: @@ -485,6 +488,25 @@ class Points */ const Settings& settings() const { return mSettings; } + /** + * @brief Returns the current cross section settings. + * @return The current cross section settings. + */ + const CrossSectionSettings& crossSectionSettings() const + { + return mSettings.crossSectionSettings; + } + + /** + * @brief Sets the cross section settings. + * @param[in] settings: The cross section settings to apply. + */ + void setCrossSectionSettings(const CrossSectionSettings& settings) + { + mSettings.crossSectionSettings = settings; + mIsUpdateProgramNeeded = true; + } + void draw(bgfx::ViewId viewId) const; void drawId(bgfx::ViewId viewId, uint32_t id) const; diff --git a/vclib/render/shaders/vclib/bgfx/primitives/points/fs_points_id_in.sh b/vclib/render/shaders/vclib/bgfx/primitives/points/fs_points_id_in.sh index d2768d89f8..f2ecf5d896 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/points/fs_points_id_in.sh +++ b/vclib/render/shaders/vclib/bgfx/primitives/points/fs_points_id_in.sh @@ -6,12 +6,18 @@ // obtain one at https://mozilla.org/MPL/2.0/. $input v_normal, v_texcoord0, v_color, v_selected +$input v_worldPos, v_discardFlag +#include #include #include void main() { +#if POINTS_ID_SECTION_ON + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); +#endif + #if POINTS_ID_SHAPE_CIRCLE // circle mode (if outside of the circle, discard) vec2 uv = v_texcoord0 * 2.0 - vec2(1.0, 1.0); diff --git a/vclib/render/shaders/vclib/bgfx/primitives/points/fs_points_in.sh b/vclib/render/shaders/vclib/bgfx/primitives/points/fs_points_in.sh index e80643e845..1af888788d 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/points/fs_points_in.sh +++ b/vclib/render/shaders/vclib/bgfx/primitives/points/fs_points_in.sh @@ -6,13 +6,19 @@ // obtain one at https://mozilla.org/MPL/2.0/. $input v_normal, v_texcoord0, v_color, v_selected +$input v_worldPos, v_discardFlag +#include #include #include #include void main() { +#if POINTS_SECTION_ON + discardIfCrossSectionClipped(v_discardFlag, v_worldPos); +#endif + vec4 color = v_color; #if POINTS_SHAPE_CIRCLE diff --git a/vclib/render/shaders/vclib/bgfx/primitives/points/points.config b/vclib/render/shaders/vclib/bgfx/primitives/points/points.config index 35a6280466..4d59cc7de5 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/points/points.config +++ b/vclib/render/shaders/vclib/bgfx/primitives/points/points.config @@ -25,6 +25,10 @@ DIM_BOTH SELECTION OFF ON +DIM_BOTH SECTION + OFF + ON + ENUM_PREFIX PRIMITIVE_POINTS_ID DEFINE_PREFIX POINTS_ID @@ -43,3 +47,7 @@ DIM_VS COLOR DIM_FS SHAPE SQUARE CIRCLE + +DIM_BOTH SECTION + OFF + ON diff --git a/vclib/render/shaders/vclib/bgfx/primitives/points/varying.def.sc b/vclib/render/shaders/vclib/bgfx/primitives/points/varying.def.sc index 50c4b0442c..2bdf571b2a 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/points/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/points/varying.def.sc @@ -5,7 +5,9 @@ // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. -vec3 v_normal : NORMAL = vec3(0.0, 0.0, 0.0); -vec2 v_texcoord0 : TEXCOORD0; -flat vec4 v_color : COLOR0; -flat float v_selected : TEXCOORD1 = 0.0; +vec3 v_normal : NORMAL = vec3(0.0, 0.0, 0.0); +vec2 v_texcoord0 : TEXCOORD0; +flat vec4 v_color : COLOR0; +flat float v_selected : TEXCOORD1 = 0.0; +vec3 v_worldPos : TEXCOORD2 = vec3(0.0, 0.0, 0.0); +flat float v_discardFlag : TEXCOORD3 = 1.0; diff --git a/vclib/render/shaders/vclib/bgfx/primitives/points/vs_points_in.sh b/vclib/render/shaders/vclib/bgfx/primitives/points/vs_points_in.sh index 518a50521e..4fdbd58e29 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/points/vs_points_in.sh +++ b/vclib/render/shaders/vclib/bgfx/primitives/points/vs_points_in.sh @@ -6,9 +6,11 @@ // obtain one at https://mozilla.org/MPL/2.0/. $output v_normal, v_texcoord0, v_color, v_selected +$output v_worldPos, v_discardFlag #include #include +#include #include BUFFER_RO(pointsBuffer, vec4, 0); // 3D point positions @@ -75,4 +77,11 @@ void main() #if POINTS_SELECTION_ON v_selected = float(getBoolFromBuffer(vertexSelected, pointIndex)); #endif + +#if defined(POINTS_SECTION_ON) || defined(POINTS_ID_SECTION_ON) + v_worldPos = mul(u_model[0], vec4(centerPos, 1.0)).xyz; + v_discardFlag = computeDiscardFlag(v_worldPos); +#else + v_discardFlag = 1.0; +#endif } diff --git a/vclib/render/src/vclib/bgfx/primitives/points.cpp b/vclib/render/src/vclib/bgfx/primitives/points.cpp index 7c4907f327..81322827b5 100644 --- a/vclib/render/src/vclib/bgfx/primitives/points.cpp +++ b/vclib/render/src/vclib/bgfx/primitives/points.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -163,6 +164,19 @@ void Points::draw(bgfx::ViewId viewId) const // Bind the updated uniforms to the shader stage. PointsUniforms::bind(); + // cross section settings + if (mSettings.crossSectionSettings.isEnabled()) { + using enum CrossSectionSettings::CrossSectionType; + CrossSectionUniforms::set( + mSettings.crossSectionSettings.lower(), + mSettings.crossSectionSettings.upper(), + mSettings.crossSectionSettings.type() == PER_FRAGMENT); + } + else { + CrossSectionUniforms::set(); + } + CrossSectionUniforms::bind(); + bgfx::submit(viewId, mProgram); } @@ -201,6 +215,19 @@ void Points::drawId(bgfx::ViewId viewId, uint32_t id) const // Bind the updated uniforms to the shader stage. PointsUniforms::bind(); + // cross section settings + if (mSettings.crossSectionSettings.isEnabled()) { + using enum CrossSectionSettings::CrossSectionType; + CrossSectionUniforms::set( + mSettings.crossSectionSettings.lower(), + mSettings.crossSectionSettings.upper(), + mSettings.crossSectionSettings.type() == PER_FRAGMENT); + } + else { + CrossSectionUniforms::set(); + } + CrossSectionUniforms::bind(); + bgfx::submit(viewId, mIdProgram); } @@ -273,23 +300,27 @@ bgfx::ProgramHandle Points::pointsProgramSelector() const constexpr uint N_COLOR_MODES = 2; constexpr uint N_SHAPE_MODES = 2; constexpr uint N_SELECTION_MODES = 2; + constexpr uint N_SECTION_MODES = 2; uint shading = toUnderlying(mSettings.shading); uint color = toUnderlying(mSettings.colorSetting); uint shape = toUnderlying(mSettings.shape); uint select = mSettings.selectionVisibility ? 1 : 0; + uint section = mSettings.crossSectionSettings.isEnabled() ? 1 : 0; // the first shader of all the combinations uint base = toUnderlying( - PRIMITIVE_POINTS_SHADING_NONE_COLOR_GENERAL_SHAPE_SQUARE_SELECTION_OFF); + PRIMITIVE_POINTS_SHADING_NONE_COLOR_GENERAL_SHAPE_SQUARE_SELECTION_OFF_SECTION_OFF); // matrix is generated from points.config - // SHADING x COLOR x SHAPE x SELECTION + // SHADING x COLOR x SHAPE x SELECTION x SECTION uint program = base + - shading * N_COLOR_MODES * N_SHAPE_MODES * N_SELECTION_MODES + - color * N_SHAPE_MODES * N_SELECTION_MODES + - shape * N_SELECTION_MODES + select; + shading * N_COLOR_MODES * N_SHAPE_MODES * N_SELECTION_MODES * N_SECTION_MODES + + color * N_SHAPE_MODES * N_SELECTION_MODES * N_SECTION_MODES + + shape * N_SELECTION_MODES * N_SECTION_MODES + + select * N_SECTION_MODES + + section; ProgramManager& pm = Context::instance().programManager(); return pm.getProgram(VertFragProgram(program)); @@ -305,13 +336,19 @@ bgfx::ProgramHandle Points::pointsIdProgramSelector() const { using enum VertFragProgram; - uint shape = toUnderlying(mSettings.shape); + constexpr uint N_SHAPE_MODES = 2; + constexpr uint N_SECTION_MODES = 2; + + uint shape = toUnderlying(mSettings.shape); + uint section = mSettings.crossSectionSettings.isEnabled() ? 1 : 0; // the first shader of all the combinations uint base = toUnderlying( - PRIMITIVE_POINTS_ID_SHADING_NONE_COLOR_GENERAL_SHAPE_SQUARE); + PRIMITIVE_POINTS_ID_SHADING_NONE_COLOR_GENERAL_SHAPE_SQUARE_SECTION_OFF); - uint program = base + shape; + uint program = base + + shape * N_SECTION_MODES + + section; ProgramManager& pm = Context::instance().programManager(); return pm.getProgram(VertFragProgram(program)); From 1e82844e938f695339d074e75e905512185f3b49 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 31 Jul 2026 19:00:05 +0200 Subject: [PATCH 45/49] [tests] add cross section tests --- ...ace-headless_cross_section_fragment_gt.png | Bin 0 -> 8308808 bytes ...rface-headless_cross_section_vertex_gt.png | Bin 0 -> 8308808 bytes .../render/002-mesh-surface-headless/main.cpp | 44 ++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 assets/ground_truth/002-mesh-surface-headless_cross_section_fragment_gt.png create mode 100644 assets/ground_truth/002-mesh-surface-headless_cross_section_vertex_gt.png diff --git a/assets/ground_truth/002-mesh-surface-headless_cross_section_fragment_gt.png b/assets/ground_truth/002-mesh-surface-headless_cross_section_fragment_gt.png new file mode 100644 index 0000000000000000000000000000000000000000..60bdb18d860b8257a243e4cb347284e2ab139962 GIT binary patch literal 8308808 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 literal 0 HcmV?d00001 diff --git a/assets/ground_truth/002-mesh-surface-headless_cross_section_vertex_gt.png b/assets/ground_truth/002-mesh-surface-headless_cross_section_vertex_gt.png new file mode 100644 index 0000000000000000000000000000000000000000..a87f91b8ffc09d8805c8592fa19c638a7bcdfc74 GIT binary patch literal 8308808 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℘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 N9(xVCcDF;m_kV0mr2hZ_ literal 0 HcmV?d00001 diff --git a/tests/render/002-mesh-surface-headless/main.cpp b/tests/render/002-mesh-surface-headless/main.cpp index 404ee5cfdf..c0b75b06d0 100644 --- a/tests/render/002-mesh-surface-headless/main.cpp +++ b/tests/render/002-mesh-surface-headless/main.cpp @@ -287,3 +287,47 @@ TEST_CASE("Mesh Surface Selection") }); } } + +TEST_CASE("Mesh Surface Cross Section") +{ + SECTION("Vertex Cross Section") + { + runRenderTest("cross_section_vertex", [](vcl::HeadlessMeshViewer& mv) { + auto mesh = getDrawableMesh("bimba.obj"); + + vcl::CrossSectionSettings css(mesh); + css.type() = + vcl::CrossSectionSettings::CrossSectionType::PER_VERTEX; + + vcl::Point3f min = css.boundingBox().min(); + vcl::Point3f max = css.boundingBox().max(); + css.setLowerUpper(min + (max - min) * 0.25f, + max - (max - min) * 0.25f); + + mesh.setCrossSectionSettings(css); + + mv.pushDrawableObject(std::move(mesh)); + }); + } + + SECTION("Fragment Cross Section") + { + runRenderTest("cross_section_fragment", [](vcl::HeadlessMeshViewer& mv) { + auto mesh = getDrawableMesh("bimba.obj"); + + vcl::CrossSectionSettings css(mesh); + css.type() = + vcl::CrossSectionSettings::CrossSectionType::PER_FRAGMENT; + + vcl::Point3f min = css.boundingBox().min(); + vcl::Point3f max = css.boundingBox().max(); + css.setLowerUpper(min + (max - min) * 0.25f, + max - (max - min) * 0.25f); + + mesh.setCrossSectionSettings(css); + + mv.pushDrawableObject(std::move(mesh)); + }); + } +} + From 8f628fd2aaee2fbd95741875f607ce45d11dddc1 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Sun, 2 Aug 2026 17:45:44 +0200 Subject: [PATCH 46/49] [render] using StaticUniforms for CrossSectionSettings --- .../drawable/uniforms/cross_section_uniforms.h | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h b/vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h index 0bf9b5a6e1..104687db05 100644 --- a/vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h +++ b/vclib/render/include/vclib/bgfx/drawable/uniforms/cross_section_uniforms.h @@ -23,7 +23,7 @@ #ifndef VCL_BGFX_DRAWABLE_UNIFORMS_CROSS_SECTION_UNIFORMS_H #define VCL_BGFX_DRAWABLE_UNIFORMS_CROSS_SECTION_UNIFORMS_H -#include +#include #include @@ -46,8 +46,12 @@ class CrossSectionUniforms 0.0 // unused }; - inline static Uniform sCrossSectionMinDataUniform; - inline static Uniform sCrossSectionMaxDataUniform; + inline static StaticUniform sCrossSectionMinDataUniform { + "u_crossSectionMinData", + bgfx::UniformType::Vec4}; + inline static StaticUniform sCrossSectionMaxDataUniform { + "u_crossSectionMaxData", + bgfx::UniformType::Vec4}; public: CrossSectionUniforms() = delete; @@ -69,14 +73,6 @@ class CrossSectionUniforms static void bind() { - // lazy initialization - // to avoid creating uniforms before bgfx is initialized - if (!sCrossSectionMinDataUniform.isValid()) - sCrossSectionMinDataUniform = - Uniform("u_crossSectionMinData", bgfx::UniformType::Vec4); - if (!sCrossSectionMaxDataUniform.isValid()) - sCrossSectionMaxDataUniform = - Uniform("u_crossSectionMaxData", bgfx::UniformType::Vec4); sCrossSectionMinDataUniform.bind(sCrossSectionMinData.data()); sCrossSectionMaxDataUniform.bind(sCrossSectionMaxData.data()); } From 28bbd629490e080bff94617ea77c5f6775494eb6 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Tue, 4 Aug 2026 21:15:30 +0200 Subject: [PATCH 47/49] [render] cross section working on new primitive lines --- .../bgfx/drawable/mesh/mesh_render_buffers.h | 4 +- .../include/vclib/bgfx/primitives/lines.h | 10 ++--- .../bgfx/primitives/lines/fs_lines_id_in.sh | 8 ++++ .../bgfx/primitives/lines/fs_lines_in.sh | 13 ++++-- .../vclib/bgfx/primitives/lines/lines.config | 8 ++++ .../bgfx/primitives/lines/varying.def.sc | 5 +++ .../bgfx/primitives/lines/vs_lines_in.sh | 32 +++++++++++++++ .../src/vclib/bgfx/primitives/lines.cpp | 40 +++++++++++++++++-- 8 files changed, 106 insertions(+), 14 deletions(-) diff --git a/vclib/render/include/vclib/bgfx/drawable/mesh/mesh_render_buffers.h b/vclib/render/include/vclib/bgfx/drawable/mesh/mesh_render_buffers.h index 46336fd70c..44fd706ea4 100644 --- a/vclib/render/include/vclib/bgfx/drawable/mesh/mesh_render_buffers.h +++ b/vclib/render/include/vclib/bgfx/drawable/mesh/mesh_render_buffers.h @@ -408,8 +408,8 @@ class MeshRenderBuffers : public MeshRenderData> void updateCrossSectionSettings(const CrossSectionSettings& css) { - mEdgeLines.crossSectionSettings() = css; - mWireframeLines.crossSectionSettings() = css; + mEdgeLines.setCrossSectionSettings(css); + mWireframeLines.setCrossSectionSettings(css); mPoints.setCrossSectionSettings(css); } diff --git a/vclib/render/include/vclib/bgfx/primitives/lines.h b/vclib/render/include/vclib/bgfx/primitives/lines.h index 00238abe7f..52122db114 100644 --- a/vclib/render/include/vclib/bgfx/primitives/lines.h +++ b/vclib/render/include/vclib/bgfx/primitives/lines.h @@ -154,13 +154,13 @@ class Lines } /** - * @brief Returns a reference to the current cross section settings. This - * allows to modify the settings directly. - * @return A reference to the current cross section settings. + * @brief Sets the cross section settings. + * @param[in] settings: The cross section settings to apply. */ - CrossSectionSettings& crossSectionSettings() + void setCrossSectionSettings(const CrossSectionSettings& settings) { - return mCrossSectionSettings; + mCrossSectionSettings = settings; + mIsUpdateProgramNeeded = true; } /** diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/fs_lines_id_in.sh b/vclib/render/shaders/vclib/bgfx/primitives/lines/fs_lines_id_in.sh index 2b365593d4..b951e560c8 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/fs_lines_id_in.sh +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/fs_lines_id_in.sh @@ -7,9 +7,17 @@ $input v_color, v_normal +// cross section +$input v_worldPos0, v_worldPos1, v_discardFlag, v_t + +#include #include #include void main() { +#if LINES_ID_SECTION_ON + vec3 fragPos = mix(v_worldPos0, v_worldPos1, v_t); + discardIfCrossSectionClipped(v_discardFlag, fragPos); +#endif gl_FragColor = u_linesId; } diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/fs_lines_in.sh b/vclib/render/shaders/vclib/bgfx/primitives/lines/fs_lines_in.sh index 454592b8e7..fe2ec1f7d1 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/fs_lines_in.sh +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/fs_lines_in.sh @@ -7,13 +7,20 @@ $input v_color, v_normal -#include -#if LINES_SHADING_PER_VERTEX || LINES_SHADING_PER_LINE +// cross section +$input v_worldPos0, v_worldPos1, v_discardFlag, v_t + +#include #include -#endif + #include void main() { +#if LINES_SECTION_ON + vec3 fragPos = mix(v_worldPos0, v_worldPos1, v_t); + discardIfCrossSectionClipped(v_discardFlag, fragPos); +#endif + vec4 color = v_color; #if LINES_SHADING_PER_VERTEX || LINES_SHADING_PER_LINE diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/lines.config b/vclib/render/shaders/vclib/bgfx/primitives/lines/lines.config index 673f3db715..40f6a99c46 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/lines.config +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/lines.config @@ -27,6 +27,10 @@ DIM_VS COLOR PER_LINE GENERAL +DIM_BOTH SECTION + OFF + ON + ENUM_PREFIX PRIMITIVE_LINES_ID DEFINE_PREFIX LINES @@ -49,3 +53,7 @@ DIM_VS TOPO DIM_VS COLOR GENERAL + +DIM_BOTH SECTION + OFF + ON diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/varying.def.sc b/vclib/render/shaders/vclib/bgfx/primitives/lines/varying.def.sc index 884ddd91e4..fa2a5b7052 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/varying.def.sc +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/varying.def.sc @@ -7,3 +7,8 @@ vec4 v_color : COLOR0; vec3 v_normal : NORMAL; + +vec3 v_worldPos0 : TEXCOORD2 = vec3(0.0, 0.0, 0.0); +vec3 v_worldPos1 : TEXCOORD3 = vec3(0.0, 0.0, 0.0); +float v_discardFlag : TEXCOORD4 = 0; +float v_t : TEXCOORD5 = 0; // interpolation parameter along segment diff --git a/vclib/render/shaders/vclib/bgfx/primitives/lines/vs_lines_in.sh b/vclib/render/shaders/vclib/bgfx/primitives/lines/vs_lines_in.sh index 90fecd4d14..7eedf72bd8 100644 --- a/vclib/render/shaders/vclib/bgfx/primitives/lines/vs_lines_in.sh +++ b/vclib/render/shaders/vclib/bgfx/primitives/lines/vs_lines_in.sh @@ -7,7 +7,11 @@ $output v_color, v_normal +// cross section +$output v_worldPos0, v_worldPos1, v_discardFlag, v_t + #include +#include #include #include @@ -62,6 +66,12 @@ void main() { vec3 p0 = getVertexPos(vertexIndex0); vec3 p1 = getVertexPos(vertexIndex1); +#if defined(LINES_SECTION_ON) || defined(LINES_ID_SECTION_ON) + // compute initial world positions + vec3 worldP0 = mul(u_model[0], vec4(p0, 1.0)).xyz; + vec3 worldP1 = mul(u_model[0], vec4(p1, 1.0)).xyz; +#endif + vec4 p0_NDC = mul(u_modelViewProj, vec4(p0, 1.0)); vec4 p1_NDC = mul(u_modelViewProj, vec4(p1, 1.0)); @@ -98,18 +108,28 @@ void main() { vec4 clippedColor1 = c1; vec3 clippedNormal0 = n0_NDC; vec3 clippedNormal1 = n1_NDC; +#if defined(LINES_SECTION_ON) || defined(LINES_ID_SECTION_ON) + vec3 clippedWorldP0 = worldP0; + vec3 clippedWorldP1 = worldP1; +#endif if (p0_NDC.w < NEAR_EPSILON) { float t = (NEAR_EPSILON - p0_NDC.w) / (p1_NDC.w - p0_NDC.w); clippedP0 = mix(p0_NDC, p1_NDC, t); clippedColor0 = mix(c0, c1, t); clippedNormal0 = mix(n0_NDC, n1_NDC, t); +#if defined(LINES_SECTION_ON) || defined(LINES_ID_SECTION_ON) + clippedWorldP0 = mix(worldP0, worldP1, t); +#endif } if (p1_NDC.w < NEAR_EPSILON) { float t = (NEAR_EPSILON - p1_NDC.w) / (p0_NDC.w - p1_NDC.w); clippedP1 = mix(p1_NDC, p0_NDC, t); clippedColor1 = mix(c1, c0, t); clippedNormal1 = mix(n1_NDC, n0_NDC, t); +#if defined(LINES_SECTION_ON) || defined(LINES_ID_SECTION_ON) + clippedWorldP1 = mix(worldP1, worldP0, t); +#endif } const vec2 offsets[6] = { @@ -144,6 +164,18 @@ void main() { v_color = color; v_normal = normal; +#if defined(LINES_SECTION_ON) || defined(LINES_ID_SECTION_ON) + v_worldPos0 = clippedWorldP0; + v_worldPos1 = clippedWorldP1; + v_t = uv.x; + // discard flag - 1.0 if either endpoint is outside the cross section + // (only used in per-vertex mode; in per-fragment mode computeDiscardFlag returns 0.0) + v_discardFlag = + max(computeDiscardFlag(v_worldPos0), computeDiscardFlag(v_worldPos1)); +#else + v_discardFlag = 1.0; +#endif + // Apply depth offset in clip space. // We scale the offset by w to maintain it consistently after the perspective divide. p.z += -u_depthOffset * p.w; diff --git a/vclib/render/src/vclib/bgfx/primitives/lines.cpp b/vclib/render/src/vclib/bgfx/primitives/lines.cpp index 25d160b3d3..46367af6d7 100644 --- a/vclib/render/src/vclib/bgfx/primitives/lines.cpp +++ b/vclib/render/src/vclib/bgfx/primitives/lines.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -115,6 +116,19 @@ void Lines::draw(bgfx::ViewId viewId) const 0 | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_WRITE_Z | BGFX_STATE_DEPTH_TEST_LESS | BGFX_STATE_BLEND_ALPHA); LinesUniforms::bind(); + + if (mCrossSectionSettings.isEnabled()) { + using enum CrossSectionSettings::CrossSectionType; + CrossSectionUniforms::set( + mCrossSectionSettings.lower(), + mCrossSectionSettings.upper(), + mCrossSectionSettings.type() == PER_FRAGMENT); + } + else { + CrossSectionUniforms::set(); + } + CrossSectionUniforms::bind(); + bgfx::submit(viewId, mProgram); } @@ -143,6 +157,19 @@ void Lines::drawId(bgfx::ViewId viewId, uint32_t id) const BGFX_STATE_DEPTH_TEST_LEQUAL | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_ZERO)); LinesUniforms::bind(); + + if (mCrossSectionSettings.isEnabled()) { + using enum CrossSectionSettings::CrossSectionType; + CrossSectionUniforms::set( + mCrossSectionSettings.lower(), + mCrossSectionSettings.upper(), + mCrossSectionSettings.type() == PER_FRAGMENT); + } + else { + CrossSectionUniforms::set(); + } + CrossSectionUniforms::bind(); + bgfx::submit(viewId, mIdProgram); } @@ -263,21 +290,24 @@ bgfx::ProgramHandle Lines::linesProgramSelector() const constexpr uint N_INDEX_MODES = 2; constexpr uint N_TOPO_MODES = 2; constexpr uint N_COLOR_MODES = 3; + constexpr uint N_SECTION_MODES = 2; uint shading = toUnderlying(mShading); uint indices = mIndices.isValid() ? 0 : 1; uint topology = toUnderlying(mTopology); uint color = toUnderlying(mColorSetting); + uint section = mCrossSectionSettings.isEnabled() ? 1 : 0; // the first shader of all the combinations uint base = toUnderlying( - PRIMITIVE_LINES_SHADING_NONE_INDICES_ON_TOPO_LINES_COLOR_PER_VERTEX); + PRIMITIVE_LINES_SHADING_NONE_INDICES_ON_TOPO_LINES_COLOR_PER_VERTEX_SECTION_OFF); uint offset = linearizeIndex< N_SHADING_MODES, N_INDEX_MODES, N_TOPO_MODES, - N_COLOR_MODES>(shading, indices, topology, color); + N_COLOR_MODES, + N_SECTION_MODES>(shading, indices, topology, color, section); uint program = base + offset; @@ -291,16 +321,18 @@ bgfx::ProgramHandle Lines::linesIdProgramSelector() const constexpr uint N_INDEX_MODES = 2; constexpr uint N_TOPO_MODES = 2; + constexpr uint N_SECTION_MODES = 2; uint indices = mIndices.isValid() ? 0 : 1; uint topology = toUnderlying(mTopology); + uint section = mCrossSectionSettings.isEnabled() ? 1 : 0; // the first shader of all the combinations uint base = toUnderlying( - PRIMITIVE_LINES_ID_SHADING_NONE_INDICES_ON_TOPO_LINES_COLOR_GENERAL); + PRIMITIVE_LINES_ID_SHADING_NONE_INDICES_ON_TOPO_LINES_COLOR_GENERAL_SECTION_OFF); uint offset = - linearizeIndex(indices, topology); + linearizeIndex(indices, topology, section); uint program = base + offset; From f29e54820c7f209b3383609b345551e5e3d954d9 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Tue, 4 Aug 2026 21:24:22 +0200 Subject: [PATCH 48/49] [tests] wireframe cross section test --- ...ame-headless_cross_section_fragment_gt.png | Bin 0 -> 32083 bytes ...frame-headless_cross_section_vertex_gt.png | Bin 0 -> 30291 bytes .../005-mesh-wireframe-headless/main.cpp | 57 ++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 assets/ground_truth/005-mesh-wireframe-headless_cross_section_fragment_gt.png create mode 100644 assets/ground_truth/005-mesh-wireframe-headless_cross_section_vertex_gt.png diff --git a/assets/ground_truth/005-mesh-wireframe-headless_cross_section_fragment_gt.png b/assets/ground_truth/005-mesh-wireframe-headless_cross_section_fragment_gt.png new file mode 100644 index 0000000000000000000000000000000000000000..bd8899d12bda219780fe7d31d2c1548c8796463d GIT binary patch literal 32083 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 literal 0 HcmV?d00001 diff --git a/assets/ground_truth/005-mesh-wireframe-headless_cross_section_vertex_gt.png b/assets/ground_truth/005-mesh-wireframe-headless_cross_section_vertex_gt.png new file mode 100644 index 0000000000000000000000000000000000000000..3f12708e21f467c20601a384b6bdf4011ebe3bca GIT binary patch literal 30291 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

0{E#RKhcyzUer^ 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"bunny_simplified.obj"); + + auto settings = mesh.renderSettings(); + settings.setSurface(vcl::MeshRenderInfo::Surface::VISIBLE, false); + settings.setWireframe( + vcl::MeshRenderInfo::Wireframe::VISIBLE, true); + settings.setWireframeWidth(3); + mesh.setRenderSettings(settings); + + vcl::CrossSectionSettings css(mesh); + css.type() = + vcl::CrossSectionSettings::CrossSectionType::PER_VERTEX; + + vcl::Point3f min = css.boundingBox().min(); + vcl::Point3f max = css.boundingBox().max(); + css.setLowerUpper(min + (max - min) * 0.25f, + max - (max - min) * 0.25f); + + mesh.setCrossSectionSettings(css); + + mv.pushDrawableObject(std::move(mesh)); + }); + } + + SECTION("Fragment Cross Section") + { + runRenderTest("cross_section_fragment", [](vcl::HeadlessMeshViewer& mv) { + auto mesh = getDrawableMesh("bunny_simplified.obj"); + + auto settings = mesh.renderSettings(); + settings.setSurface(vcl::MeshRenderInfo::Surface::VISIBLE, false); + settings.setWireframe( + vcl::MeshRenderInfo::Wireframe::VISIBLE, true); + settings.setWireframeWidth(3); + mesh.setRenderSettings(settings); + + vcl::CrossSectionSettings css(mesh); + css.type() = + vcl::CrossSectionSettings::CrossSectionType::PER_FRAGMENT; + + vcl::Point3f min = css.boundingBox().min(); + vcl::Point3f max = css.boundingBox().max(); + css.setLowerUpper(min + (max - min) * 0.25f, + max - (max - min) * 0.25f); + + mesh.setCrossSectionSettings(css); + + mv.pushDrawableObject(std::move(mesh)); + }); + } +} From c95ce127d0d453f55833bba867ef958ad305954e Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Sun, 9 Aug 2026 12:40:36 +0200 Subject: [PATCH 49/49] [tests] fix build error --- .../005-mesh-wireframe-headless/main.cpp | 88 +++++++++++-------- 1 file changed, 52 insertions(+), 36 deletions(-) diff --git a/tests/render/005-mesh-wireframe-headless/main.cpp b/tests/render/005-mesh-wireframe-headless/main.cpp index 816618d433..2a7b627117 100644 --- a/tests/render/005-mesh-wireframe-headless/main.cpp +++ b/tests/render/005-mesh-wireframe-headless/main.cpp @@ -219,55 +219,71 @@ TEST_CASE("Wireframe Cross Section") { SECTION("Vertex Cross Section") { - runRenderTest("cross_section_vertex", [](vcl::HeadlessMeshViewer& mv) { - auto mesh = getDrawableMesh("bunny_simplified.obj"); + runRenderTest( + TEST_NAME, + "cross_section_vertex", + [](vcl::HeadlessMeshViewer& mv) { + auto mesh = + getDrawableMesh("bunny_simplified.obj"); - auto settings = mesh.renderSettings(); - settings.setSurface(vcl::MeshRenderInfo::Surface::VISIBLE, false); - settings.setWireframe( - vcl::MeshRenderInfo::Wireframe::VISIBLE, true); - settings.setWireframeWidth(3); - mesh.setRenderSettings(settings); + auto settings = mesh.renderSettings(); + settings.setSurface( + vcl::MeshRenderInfo::Surface::VISIBLE, false); + settings.setWireframe( + vcl::MeshRenderInfo::Wireframe::VISIBLE, true); + settings.setWireframeWidth(3); + mesh.setRenderSettings(settings); - vcl::CrossSectionSettings css(mesh); - css.type() = - vcl::CrossSectionSettings::CrossSectionType::PER_VERTEX; + vcl::CrossSectionSettings css(mesh); + css.type() = + vcl::CrossSectionSettings::CrossSectionType::PER_VERTEX; - vcl::Point3f min = css.boundingBox().min(); - vcl::Point3f max = css.boundingBox().max(); - css.setLowerUpper(min + (max - min) * 0.25f, - max - (max - min) * 0.25f); + vcl::Point3f min = css.boundingBox().min(); + vcl::Point3f max = css.boundingBox().max(); + css.setLowerUpper( + min + (max - min) * 0.25f, max - (max - min) * 0.25f); - mesh.setCrossSectionSettings(css); + mesh.setCrossSectionSettings(css); - mv.pushDrawableObject(std::move(mesh)); - }); + mv.pushDrawableObject(std::move(mesh)); + }, + 0.f, + false, + -150.f); } SECTION("Fragment Cross Section") { - runRenderTest("cross_section_fragment", [](vcl::HeadlessMeshViewer& mv) { - auto mesh = getDrawableMesh("bunny_simplified.obj"); + runRenderTest( + TEST_NAME, + "cross_section_fragment", + [](vcl::HeadlessMeshViewer& mv) { + auto mesh = + getDrawableMesh("bunny_simplified.obj"); - auto settings = mesh.renderSettings(); - settings.setSurface(vcl::MeshRenderInfo::Surface::VISIBLE, false); - settings.setWireframe( - vcl::MeshRenderInfo::Wireframe::VISIBLE, true); - settings.setWireframeWidth(3); - mesh.setRenderSettings(settings); + auto settings = mesh.renderSettings(); + settings.setSurface( + vcl::MeshRenderInfo::Surface::VISIBLE, false); + settings.setWireframe( + vcl::MeshRenderInfo::Wireframe::VISIBLE, true); + settings.setWireframeWidth(3); + mesh.setRenderSettings(settings); - vcl::CrossSectionSettings css(mesh); - css.type() = - vcl::CrossSectionSettings::CrossSectionType::PER_FRAGMENT; + vcl::CrossSectionSettings css(mesh); + css.type() = + vcl::CrossSectionSettings::CrossSectionType::PER_FRAGMENT; - vcl::Point3f min = css.boundingBox().min(); - vcl::Point3f max = css.boundingBox().max(); - css.setLowerUpper(min + (max - min) * 0.25f, - max - (max - min) * 0.25f); + vcl::Point3f min = css.boundingBox().min(); + vcl::Point3f max = css.boundingBox().max(); + css.setLowerUpper( + min + (max - min) * 0.25f, max - (max - min) * 0.25f); - mesh.setCrossSectionSettings(css); + mesh.setCrossSectionSettings(css); - mv.pushDrawableObject(std::move(mesh)); - }); + mv.pushDrawableObject(std::move(mesh)); + }, + 0.f, + false, + -150.f); } }