Add section view feature for 3D canvas (#15879)

This commit is contained in:
Ian Bassi
2026-10-02 11:50:43 -03:00
committed by GitHub
parent 6c6e8be43d
commit a1ad2b4425
31 changed files with 599 additions and 612 deletions
+4
View File
@@ -76,6 +76,10 @@ public:
// caller decides which of the two it varies with the realistic view setting.
//
void set_tone(float exposure, float saturation);
//
// ORCA: section view. Cuts away where dot(plane, (x, y, z, 1)) < 0; { 0, 0, 0, 1 } keeps all.
//
void set_clipping_plane(const std::array<float, 4>& plane);
//
// ************************************************************************
+61 -4
View File
@@ -38,6 +38,8 @@ static const char* Segments_Vertex_Shader =
// rejection discards most of the hidden fragments; set when the camera looks down on the print
"uniform int reverse_order;\n"
"uniform int instance_count;\n"
// ORCA: section view
"uniform vec4 clipping_plane;\n"
"in int vertex_id;\n"
"out vec3 color;\n"
"// ORCA: realistic view - the light the shadow map is able to block, kept apart from the\n"
@@ -48,6 +50,9 @@ static const char* Segments_Vertex_Shader =
"// needs its world position and, for the depth bias, its eye space normal.\n"
"out vec3 world_position;\n"
"out vec3 shadow_normal;\n"
// ORCA: section view - the segment and the light on its cut face
"flat out int segment_id;\n"
"out vec3 cut_color_direct;\n"
"vec3 decode_color(float color) {\n"
" int c = int(round(color));\n"
" int r = (c >> 16) & 0xFF;\n"
@@ -159,6 +164,9 @@ static const char* Segments_Vertex_Shader =
" color_direct = color_base * direct_lighting(eye_position, eye_normal);\n"
" world_position = pos;\n"
" shadow_normal = eye_normal;\n"
" segment_id = id_a;\n"
" cut_color_direct = clipping_plane.xyz == vec3(0.0) ? vec3(0.0) :\n"
" color_base * direct_lighting(eye_position, mat3(view_matrix) * -clipping_plane.xyz);\n"
" gl_Position = projection_matrix * vec4(eye_position, 1.0);\n"
"}\n";
@@ -178,23 +186,33 @@ static const char* Segments_Fragment_Shader =
// passes in each mode is decided in GLCanvas3D::_render_gcode, not here.
"uniform float exposure;\n"
"uniform float saturation;\n"
// ORCA: section view - only camera-facing faces are drawn, so cut faces are ray cast
"uniform vec4 clipping_plane;\n"
"uniform mat4 view_matrix;\n"
"uniform mat4 projection_matrix;\n"
"uniform vec3 camera_position;\n"
"uniform samplerBuffer position_tex;\n"
"uniform samplerBuffer height_width_angle_tex;\n"
"const vec3 UP = vec3(0, 0, 1);\n"
"const vec3 LUMA = vec3(0.2126, 0.7152, 0.0722);\n"
"in vec3 color;\n"
"in vec3 color_direct;\n"
"in vec3 world_position;\n"
"in vec3 shadow_normal;\n"
"flat in int segment_id;\n"
"in vec3 cut_color_direct;\n"
"out vec4 fragment_color;\n"
"float shadow_shade() {\n"
"float shadow_shade(vec3 position, vec3 normal) {\n"
" if (shadow_intensity <= 0.0)\n"
" return 1.0;\n"
" vec4 lp = shadow_light_vp * vec4(world_position, 1.0);\n"
" vec4 lp = shadow_light_vp * vec4(position, 1.0);\n"
" vec3 proj = lp.xyz / lp.w;\n"
" proj = proj * 0.5 + 0.5;\n"
" if (proj.z > 1.0)\n"
" return 1.0;\n"
" // Slope-scaled bias, as in gouraud.fs. An extrusion is only a handful of shadow-map texels\n"
" // wide, so grazing faces need the larger bias to keep self-shadow acne off the top surfaces.\n"
" float NdotL = dot(normalize(shadow_normal), SHADOW_LIGHT_DIR);\n"
" float NdotL = dot(normalize(normal), SHADOW_LIGHT_DIR);\n"
" float bias = mix(0.0004, 0.004, clamp(1.0 - NdotL, 0.0, 1.0));\n"
" float sum = 0.0;\n"
" for (int x = -2; x <= 2; ++x) {\n"
@@ -205,8 +223,39 @@ static const char* Segments_Fragment_Shader =
" }\n"
" return 1.0 - shadow_intensity * (sum / 25.0);\n"
"}\n"
// ORCA: section view - where the view ray meets the plane, if inside the extrusion's diamond section
"bool find_cut(out vec3 cut) {\n"
// parallel rays for an orthographic projection
" vec3 ray = projection_matrix[3][3] == 1.0 ? -vec3(view_matrix[0][2], view_matrix[1][2], view_matrix[2][2]) :\n"
" normalize(world_position - camera_position);\n"
" float approach = dot(clipping_plane.xyz, ray);\n"
" if (approach <= 0.0)\n"
" return false;\n"
" cut = world_position - dot(vec4(world_position, 1.0), clipping_plane) / approach * ray;\n"
" vec3 pos_a = texelFetch(position_tex, segment_id).xyz;\n"
" vec3 line = texelFetch(position_tex, segment_id + 1).xyz - pos_a;\n"
" float length_sq = dot(line, line);\n"
" float along = length_sq < 1e-8 ? -1.0 : dot(cut - pos_a, line) / length_sq;\n"
" if (along < 0.0 || along > 1.0)\n"
" return false;\n"
" vec3 line_dir = line * inversesqrt(length_sq);\n"
" vec3 right_dir = abs(dot(line_dir, UP)) > 0.9 ? normalize(cross(vec3(1, 0, 0), line_dir)) : normalize(cross(line_dir, UP));\n"
" vec3 offset = cut - pos_a - along * line;\n"
" vec2 half_height_width = 0.5 * mix(texelFetch(height_width_angle_tex, segment_id).xy,\n"
" texelFetch(height_width_angle_tex, segment_id + 1).xy, along);\n"
" return abs(dot(offset, right_dir)) / half_height_width.y + abs(dot(offset, cross(right_dir, line_dir))) / half_height_width.x <= 1.0;\n"
"}\n"
"void main() {\n"
" vec3 c = (color + color_direct * shadow_shade()) * exposure;\n"
" vec3 c;\n"
" if (dot(vec4(world_position, 1.0), clipping_plane) >= 0.0)\n"
" c = color + color_direct * shadow_shade(world_position, shadow_normal);\n"
" else {\n"
" vec3 cut;\n"
" if (!find_cut(cut))\n"
" discard;\n"
" c = color + cut_color_direct * shadow_shade(cut, mat3(view_matrix) * -clipping_plane.xyz);\n"
" }\n"
" c *= exposure;\n"
" c = mix(vec3(dot(c, LUMA)), c, saturation);\n"
" fragment_color = vec4(clamp(c, 0.0, 1.0), 1.0);\n"
"}\n";
@@ -234,9 +283,12 @@ static const char* Options_Vertex_Shader =
"uniform samplerBuffer height_width_angle_tex;\n"
"uniform samplerBuffer color_tex;\n"
"uniform usamplerBuffer segment_index_tex;\n"
// ORCA: section view
"uniform vec4 clipping_plane;\n"
"in vec3 in_position;\n"
"in vec3 in_normal;\n"
"out vec3 color;\n"
"out float clipping_plane_dot;\n"
"vec3 decode_color(float color) {\n"
" int c = int(round(color));\n"
" int r = (c >> 16) & 0xFF;\n"
@@ -263,6 +315,8 @@ static const char* Options_Vertex_Shader =
" height_width.y, 0.0, 0.0,\n"
" 0.0, height_width.y, 0.0,\n"
" 0.0, 0.0, height_width.x);\n"
// ORCA: section view - markers are cut away whole, by their centre
" clipping_plane_dot = dot(vec4(offset, 1.0), clipping_plane);\n"
" vec3 eye_position = (view_matrix * vec4(scale_matrix * in_position + offset, 1.0)).xyz;\n"
" // ORCA: Apply bias to z-position to avoid z-fighting\n"
" eye_position.z += bias;\n"
@@ -275,8 +329,11 @@ static const char* Options_Vertex_Shader =
static const char* Options_Fragment_Shader =
"#version 150\n"
"in vec3 color;\n"
"in float clipping_plane_dot;\n"
"out vec4 fragment_color;\n"
"void main() {\n"
" if (clipping_plane_dot < 0.0)\n"
" discard;\n"
" fragment_color = vec4(color, 1.0);\n"
"}\n";
+67 -4
View File
@@ -32,6 +32,8 @@ static const char* Segments_Vertex_Shader_ES =
"uniform sampler2D height_width_angle_tex;\n"
"uniform sampler2D color_tex;\n"
"uniform usampler2D segment_index_tex;\n"
// ORCA: section view
"uniform vec4 clipping_plane;\n"
"in float vertex_id_float;\n"
"out vec3 color;\n"
"// ORCA: realistic view - the light the shadow map is able to block, kept apart from the\n"
@@ -42,6 +44,9 @@ static const char* Segments_Vertex_Shader_ES =
"// needs its world position and, for the depth bias, its eye space normal.\n"
"out vec3 world_position;\n"
"out vec3 shadow_normal;\n"
// ORCA: section view - the segment and the light on its cut face
"flat out int segment_id;\n"
"out vec3 cut_color_direct;\n"
"vec3 decode_color(float color) {\n"
" int c = int(round(color));\n"
" int r = (c >> 16) & 0xFF;\n"
@@ -156,12 +161,17 @@ static const char* Segments_Vertex_Shader_ES =
" color_direct = color_base * direct_lighting(eye_position, eye_normal);\n"
" world_position = pos;\n"
" shadow_normal = eye_normal;\n"
" segment_id = id_a;\n"
" cut_color_direct = clipping_plane.xyz == vec3(0.0) ? vec3(0.0) :\n"
" color_base * direct_lighting(eye_position, mat3(view_matrix) * -clipping_plane.xyz);\n"
" gl_Position = projection_matrix * vec4(eye_position, 1.0);\n"
"}\n";
static const char* Segments_Fragment_Shader_ES =
"#version 300 es\n"
"precision highp float;\n"
// ORCA: section view - segment ids exceed mediump
"precision highp int;\n"
"// ORCA: sampler2D defaults to lowp in an ES fragment shader, far too coarse to compare\n"
"// shadow map depths against.\n"
"precision highp sampler2D;\n"
@@ -179,23 +189,33 @@ static const char* Segments_Fragment_Shader_ES =
// passes in each mode is decided in GLCanvas3D::_render_gcode, not here.
"uniform float exposure;\n"
"uniform float saturation;\n"
// ORCA: section view - cut faces are ray cast; data samplers stay lowp as in the vertex shader
"uniform vec4 clipping_plane;\n"
"uniform mat4 view_matrix;\n"
"uniform mat4 projection_matrix;\n"
"uniform vec3 camera_position;\n"
"uniform lowp sampler2D position_tex;\n"
"uniform lowp sampler2D height_width_angle_tex;\n"
"const vec3 UP = vec3(0, 0, 1);\n"
"const vec3 LUMA = vec3(0.2126, 0.7152, 0.0722);\n"
"in vec3 color;\n"
"in vec3 color_direct;\n"
"in vec3 world_position;\n"
"in vec3 shadow_normal;\n"
"flat in int segment_id;\n"
"in vec3 cut_color_direct;\n"
"out vec4 fragment_color;\n"
"float shadow_shade() {\n"
"float shadow_shade(vec3 position, vec3 normal) {\n"
" if (shadow_intensity <= 0.0)\n"
" return 1.0;\n"
" vec4 lp = shadow_light_vp * vec4(world_position, 1.0);\n"
" vec4 lp = shadow_light_vp * vec4(position, 1.0);\n"
" vec3 proj = lp.xyz / lp.w;\n"
" proj = proj * 0.5 + 0.5;\n"
" if (proj.z > 1.0)\n"
" return 1.0;\n"
" // Slope-scaled bias, as in gouraud.fs. An extrusion is only a handful of shadow-map texels\n"
" // wide, so grazing faces need the larger bias to keep self-shadow acne off the top surfaces.\n"
" float NdotL = dot(normalize(shadow_normal), SHADOW_LIGHT_DIR);\n"
" float NdotL = dot(normalize(normal), SHADOW_LIGHT_DIR);\n"
" float bias = mix(0.0004, 0.004, clamp(1.0 - NdotL, 0.0, 1.0));\n"
" float sum = 0.0;\n"
" for (int x = -2; x <= 2; ++x) {\n"
@@ -206,8 +226,43 @@ static const char* Segments_Fragment_Shader_ES =
" }\n"
" return 1.0 - shadow_intensity * (sum / 25.0);\n"
"}\n"
"vec4 fetch(lowp sampler2D sampler, int id) {\n"
" ivec2 tex_size = textureSize(sampler, 0);\n"
" return texelFetch(sampler, (tex_size.y == 1) ? ivec2(id, 0) : ivec2(id % tex_size.x, id / tex_size.x), 0);\n"
"}\n"
// ORCA: section view - where the view ray meets the plane, if inside the extrusion's diamond section
"bool find_cut(out vec3 cut) {\n"
// parallel rays for an orthographic projection
" vec3 ray = projection_matrix[3][3] == 1.0 ? -vec3(view_matrix[0][2], view_matrix[1][2], view_matrix[2][2]) :\n"
" normalize(world_position - camera_position);\n"
" float approach = dot(clipping_plane.xyz, ray);\n"
" if (approach <= 0.0)\n"
" return false;\n"
" cut = world_position - dot(vec4(world_position, 1.0), clipping_plane) / approach * ray;\n"
" vec3 pos_a = fetch(position_tex, segment_id).xyz;\n"
" vec3 line = fetch(position_tex, segment_id + 1).xyz - pos_a;\n"
" float length_sq = dot(line, line);\n"
" float along = length_sq < 1e-8 ? -1.0 : dot(cut - pos_a, line) / length_sq;\n"
" if (along < 0.0 || along > 1.0)\n"
" return false;\n"
" vec3 line_dir = line * inversesqrt(length_sq);\n"
" vec3 right_dir = abs(dot(line_dir, UP)) > 0.9 ? normalize(cross(vec3(1, 0, 0), line_dir)) : normalize(cross(line_dir, UP));\n"
" vec3 offset = cut - pos_a - along * line;\n"
" vec2 half_height_width = 0.5 * mix(fetch(height_width_angle_tex, segment_id).xy,\n"
" fetch(height_width_angle_tex, segment_id + 1).xy, along);\n"
" return abs(dot(offset, right_dir)) / half_height_width.y + abs(dot(offset, cross(right_dir, line_dir))) / half_height_width.x <= 1.0;\n"
"}\n"
"void main() {\n"
" vec3 c = (color + color_direct * shadow_shade()) * exposure;\n"
" vec3 c;\n"
" if (dot(vec4(world_position, 1.0), clipping_plane) >= 0.0)\n"
" c = color + color_direct * shadow_shade(world_position, shadow_normal);\n"
" else {\n"
" vec3 cut;\n"
" if (!find_cut(cut))\n"
" discard;\n"
" c = color + cut_color_direct * shadow_shade(cut, mat3(view_matrix) * -clipping_plane.xyz);\n"
" }\n"
" c *= exposure;\n"
" c = mix(vec3(dot(c, LUMA)), c, saturation);\n"
" fragment_color = vec4(clamp(c, 0.0, 1.0), 1.0);\n"
"}\n";
@@ -230,9 +285,12 @@ static const char* Options_Vertex_Shader_ES =
"uniform sampler2D height_width_angle_tex;\n"
"uniform sampler2D color_tex;\n"
"uniform usampler2D segment_index_tex;\n"
// ORCA: section view
"uniform vec4 clipping_plane;\n"
"in vec3 in_position;\n"
"in vec3 in_normal;\n"
"out vec3 color;\n"
"out float clipping_plane_dot;\n"
"vec3 decode_color(float color) {\n"
" int c = int(round(color));\n"
" int r = (c >> 16) & 0xFF;\n"
@@ -264,6 +322,8 @@ static const char* Options_Vertex_Shader_ES =
" height_width.y, 0.0, 0.0,\n"
" 0.0, height_width.y, 0.0,\n"
" 0.0, 0.0, height_width.x);\n"
// ORCA: section view - markers are cut away whole, by their centre
" clipping_plane_dot = dot(vec4(offset, 1.0), clipping_plane);\n"
" vec3 eye_position = (view_matrix * vec4(scale_matrix * in_position + offset, 1.0)).xyz;\n"
" vec3 eye_normal = (view_matrix * vec4(in_normal, 0.0)).xyz;\n"
" vec3 color_base = decode_color(texelFetch(color_tex, tex_coord(color_tex, id), 0).r);\n"
@@ -275,8 +335,11 @@ static const char* Options_Fragment_Shader_ES =
"#version 300 es\n"
"precision highp float;\n"
"in vec3 color;\n"
"in float clipping_plane_dot;\n"
"out vec4 fragment_color;\n"
"void main() {\n"
" if (clipping_plane_dot < 0.0)\n"
" discard;\n"
" fragment_color = vec4(color, 1.0);\n"
"}\n";
+5
View File
@@ -57,6 +57,11 @@ void Viewer::set_tone(float exposure, float saturation)
m_impl->set_tone(exposure, saturation);
}
void Viewer::set_clipping_plane(const std::array<float, 4>& plane)
{
m_impl->set_clipping_plane(plane);
}
EViewType Viewer::get_view_type() const
{
return m_impl->get_view_type();
+6
View File
@@ -773,6 +773,8 @@ void ViewerImpl::init(const std::string& opengl_context_version)
m_uni_segments_exposure_id = glGetUniformLocation(m_segments_shader_id, "exposure");
m_uni_segments_saturation_id = glGetUniformLocation(m_segments_shader_id, "saturation");
m_uni_segments_bias_scale_id = glGetUniformLocation(m_segments_shader_id, "bias_scale");
// ORCA: section view
m_uni_segments_clipping_plane_id = glGetUniformLocation(m_segments_shader_id, "clipping_plane");
glcheck();
assert(m_uni_segments_view_matrix_id != -1 &&
m_uni_segments_projection_matrix_id != -1 &&
@@ -797,6 +799,8 @@ void ViewerImpl::init(const std::string& opengl_context_version)
m_uni_options_height_width_angle_tex_id = glGetUniformLocation(m_options_shader_id, "height_width_angle_tex");
m_uni_options_colors_tex_id = glGetUniformLocation(m_options_shader_id, "color_tex");
m_uni_options_segment_index_tex_id = glGetUniformLocation(m_options_shader_id, "segment_index_tex");
// ORCA: section view
m_uni_options_clipping_plane_id = glGetUniformLocation(m_options_shader_id, "clipping_plane");
glcheck();
assert(m_uni_options_view_matrix_id != -1 &&
m_uni_options_projection_matrix_id != -1 &&
@@ -2110,6 +2114,7 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
glsafe(glUniform1f(m_uni_segments_exposure_id, m_exposure));
glsafe(glUniform1f(m_uni_segments_saturation_id, m_saturation));
glsafe(glUniform1f(m_uni_segments_bias_scale_id, m_rendering_shadow_casters ? 0.0f : 1.0f));
glsafe(glUniform4fv(m_uni_segments_clipping_plane_id, 1, m_clipping_plane.data()));
glsafe(glDisable(GL_CULL_FACE));
@@ -2197,6 +2202,7 @@ void ViewerImpl::render_options(const Mat4x4& view_matrix, const Mat4x4& project
glsafe(glUniform1i(m_uni_options_segment_index_tex_id, 3));
glsafe(glUniformMatrix4fv(m_uni_options_view_matrix_id, 1, GL_FALSE, view_matrix.data()));
glsafe(glUniformMatrix4fv(m_uni_options_projection_matrix_id, 1, GL_FALSE, projection_matrix.data()));
glsafe(glUniform4fv(m_uni_options_clipping_plane_id, 1, m_clipping_plane.data()));
glsafe(glEnable(GL_CULL_FACE));
+7
View File
@@ -89,6 +89,8 @@ public:
// caller decides which of the two it varies with the realistic view setting.
//
void set_tone(float exposure, float saturation);
// ORCA: section view, see Viewer::set_clipping_plane()
void set_clipping_plane(const std::array<float, 4>& plane) { m_clipping_plane = plane; }
EViewType get_view_type() const { return m_settings.view_type; }
void set_view_type(EViewType type);
@@ -371,6 +373,7 @@ private:
int m_uni_segments_exposure_id{ -1 };
int m_uni_segments_saturation_id{ -1 };
int m_uni_segments_bias_scale_id{ -1 };
int m_uni_segments_clipping_plane_id{ -1 };
//
// Caches for OpenGL uniforms id for options shader
//
@@ -380,6 +383,7 @@ private:
int m_uni_options_height_width_angle_tex_id{ -1 };
int m_uni_options_colors_tex_id{ -1 };
int m_uni_options_segment_index_tex_id{ -1 };
int m_uni_options_clipping_plane_id{ -1 };
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
//
// Caches for OpenGL uniforms id for cog marker shader
@@ -530,6 +534,9 @@ private:
float m_exposure{ 1.0f };
float m_saturation{ 1.0f };
// ORCA: section view
std::array<float, 4> m_clipping_plane{ 0.0f, 0.0f, 0.0f, 1.0f };
void apply_pending_updates();
void update_view_full_range();
void update_color_ranges();