From 78c6ed2c6b9ce79698f0cf450f450d1bdba6c3a5 Mon Sep 17 00:00:00 2001 From: Enrico Turri <34307919+enricoturri1966@users.noreply.github.com> Date: Mon, 10 Feb 2025 13:40:06 +0100 Subject: [PATCH] SPE-2678 - Fixed rendering of tool marker model for sequential prints in preview --- resources/shaders/110/tool_marker.fs | 18 ++++++++ resources/shaders/110/tool_marker.vs | 58 ++++++++++++++++++++++++++ resources/shaders/140/tool_marker.fs | 19 +++++++++ resources/shaders/140/tool_marker.vs | 58 ++++++++++++++++++++++++++ resources/shaders/ES/tool_marker.fs | 19 +++++++++ resources/shaders/ES/tool_marker.vs | 58 ++++++++++++++++++++++++++ src/libslic3r/MultipleBeds.hpp | 1 + src/slic3r/GUI/GCodeViewer.cpp | 61 +++++++++++++++++++++------- src/slic3r/GUI/GCodeViewer.hpp | 2 +- src/slic3r/GUI/GLShadersManager.cpp | 2 + 10 files changed, 281 insertions(+), 15 deletions(-) create mode 100644 resources/shaders/110/tool_marker.fs create mode 100644 resources/shaders/110/tool_marker.vs create mode 100644 resources/shaders/140/tool_marker.fs create mode 100644 resources/shaders/140/tool_marker.vs create mode 100644 resources/shaders/ES/tool_marker.fs create mode 100644 resources/shaders/ES/tool_marker.vs diff --git a/resources/shaders/110/tool_marker.fs b/resources/shaders/110/tool_marker.fs new file mode 100644 index 0000000000..74e5ee75cb --- /dev/null +++ b/resources/shaders/110/tool_marker.fs @@ -0,0 +1,18 @@ +#version 110 + +const vec2 ZERO = vec2(0.0, 0.0); + +uniform vec4 uniform_color; + +// x = diffuse, y = specular; +varying vec2 intensity; +varying vec2 clipping_planes_dots; + + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/110/tool_marker.vs b/resources/shaders/110/tool_marker.vs new file mode 100644 index 0000000000..46c7d4af82 --- /dev/null +++ b/resources/shaders/110/tool_marker.vs @@ -0,0 +1,58 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SHININESS 5.0 + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; + +// Clipping planes used to clip the tool marker model. +uniform vec4 clipping_planes[2]; + +attribute vec3 v_position; +attribute vec3 v_normal; + +// x = diffuse, y = specular; +varying vec2 intensity; +varying vec2 clipping_planes_dots; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 eye_normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + // Point in homogenous coordinates. + vec4 world_pos = volume_world_matrix * vec4(v_position, 1.0); + // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded. + clipping_planes_dots.x = dot(world_pos, clipping_planes[0]); + clipping_planes_dots.y = dot(world_pos, clipping_planes[1]); + + gl_Position = projection_matrix * position; +} diff --git a/resources/shaders/140/tool_marker.fs b/resources/shaders/140/tool_marker.fs new file mode 100644 index 0000000000..c3357ae35f --- /dev/null +++ b/resources/shaders/140/tool_marker.fs @@ -0,0 +1,19 @@ +#version 140 + +const vec2 ZERO = vec2(0.0, 0.0); + +uniform vec4 uniform_color; + +// x = diffuse, y = specular; +in vec2 intensity; +in vec2 clipping_planes_dots; + +out vec4 out_color; + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + + out_color = vec4(vec3(intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/140/tool_marker.vs b/resources/shaders/140/tool_marker.vs new file mode 100644 index 0000000000..ec6401e8d9 --- /dev/null +++ b/resources/shaders/140/tool_marker.vs @@ -0,0 +1,58 @@ +#version 140 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SHININESS 5.0 + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; + +// Clipping planes used to clip the tool marker model. +uniform vec4 clipping_planes[2]; + +in vec3 v_position; +in vec3 v_normal; + +// x = diffuse, y = specular; +out vec2 intensity; +out vec2 clipping_planes_dots; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 eye_normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + // Point in homogenous coordinates. + vec4 world_pos = volume_world_matrix * vec4(v_position, 1.0); + // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded. + clipping_planes_dots.x = dot(world_pos, clipping_planes[0]); + clipping_planes_dots.y = dot(world_pos, clipping_planes[1]); + + gl_Position = projection_matrix * position; +} diff --git a/resources/shaders/ES/tool_marker.fs b/resources/shaders/ES/tool_marker.fs new file mode 100644 index 0000000000..f8db1c0996 --- /dev/null +++ b/resources/shaders/ES/tool_marker.fs @@ -0,0 +1,19 @@ +#version 100 + +precision highp float; + +const vec2 ZERO = vec2(0.0, 0.0); + +uniform vec4 uniform_color; + +// x = diffuse, y = specular; +varying vec2 intensity; +varying vec2 clipping_planes_dots; + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/ES/tool_marker.vs b/resources/shaders/ES/tool_marker.vs new file mode 100644 index 0000000000..333c9dad06 --- /dev/null +++ b/resources/shaders/ES/tool_marker.vs @@ -0,0 +1,58 @@ +#version 100 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SHININESS 5.0 + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; + +// Clipping planes used to clip the tool marker model. +uniform vec4 clipping_planes[2]; + +attribute vec3 v_position; +attribute vec3 v_normal; + +// x = diffuse, y = specular; +varying vec2 intensity; +varying vec2 clipping_planes_dots; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 eye_normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + // Point in homogenous coordinates. + vec4 world_pos = volume_world_matrix * vec4(v_position, 1.0); + // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded. + clipping_planes_dots.x = dot(world_pos, clipping_planes[0]); + clipping_planes_dots.y = dot(world_pos, clipping_planes[1]); + + gl_Position = projection_matrix * position; +} diff --git a/src/libslic3r/MultipleBeds.hpp b/src/libslic3r/MultipleBeds.hpp index bfd670f88c..10ab40f420 100644 --- a/src/libslic3r/MultipleBeds.hpp +++ b/src/libslic3r/MultipleBeds.hpp @@ -87,6 +87,7 @@ public: m_build_volume_bb = build_volume_bb; } Vec2d get_bed_size() const { return m_build_volume_bb.size(); } + BoundingBoxf get_build_volume_box() const { return m_build_volume_bb; } BoundingBox get_bed_box() const { return BoundingBox({m_build_volume_bb.min.x(), m_build_volume_bb.min.y()}, {m_build_volume_bb.max.x(), m_build_volume_bb.max.y()}); } Vec2d bed_gap() const; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 5cc6bc7e68..6349005987 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -217,21 +217,28 @@ int GCodeViewer::SequentialView::ActualSpeedImguiWidget::plot(const char* label, } #endif // ENABLE_ACTUAL_SPEED_DEBUG -void GCodeViewer::SequentialView::Marker::init(std::optional>& model_opt) +bool GCodeViewer::SequentialView::Marker::init(std::optional>& model_opt) { if (! model_opt.has_value()) - return; + return false; - m_model.reset(); + if (m_generic_marker != (model_opt->get() == nullptr)) + m_model.reset(); m_generic_marker = (model_opt->get() == nullptr); - if (m_generic_marker) - m_model.init_from(stilized_arrow(16, 2.0f, 4.0f, 1.0f, 8.0f)); - else - m_model = **model_opt; - model_opt.reset(); + bool ret = false; + if (!m_model.is_initialized()) { + if (m_generic_marker) + m_model.init_from(stilized_arrow(16, 2.0f, 4.0f, 1.0f, 8.0f)); + else { + m_model = **model_opt; + model_opt.reset(); + } + ret = true; + } m_model.set_color({ 1.0f, 1.0f, 1.0f, 0.5f }); + return ret; } void GCodeViewer::SequentialView::Marker::render() @@ -239,37 +246,62 @@ void GCodeViewer::SequentialView::Marker::render() if (!m_visible) return; - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + GLShaderProgram* shader = wxGetApp().get_shader("tool_marker"); if (shader == nullptr) return; glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + const bool curr_cull_face = glIsEnabled(GL_CULL_FACE); + glsafe(::glDisable(GL_CULL_FACE)); + shader->start_using(); - shader->set_uniform("emission_factor", 0.0f); const Camera& camera = wxGetApp().plater()->get_camera(); Transform3d view_matrix = camera.get_view_matrix(); - view_matrix.translate(s_multiple_beds.get_bed_translation(s_multiple_beds.get_active_bed())); + Vec3d bed_inst_offset = s_multiple_beds.get_bed_translation(s_multiple_beds.get_active_bed()); + view_matrix.translate(bed_inst_offset); + + std::array, 2> clip_planes; + if (m_generic_marker) + // dummy values, generic marker does not need clipping + clip_planes = {{ { 1.0f, 0.0f, 0.0f, FLT_MAX }, { 1.0f, 0.0f, 0.0f, FLT_MAX } }}; + else { + BoundingBoxf box = s_multiple_beds.get_build_volume_box(); + box.translate(to_2d(bed_inst_offset)); + // add a bit on both sides + box = box.inflated(40.0f); + clip_planes = {{ { 1.0f, 0.0f, 0.0f, -box.min.cast().x() } , { -1.0f, 0.0f, 0.0f, box.max.cast().x() }}}; + } float scale_factor = m_scale_factor; if (m_fixed_screen_size) scale_factor *= 10.0f * camera.get_inv_zoom(); const Transform3d model_matrix = m_generic_marker - ? (Geometry::translation_transform((m_world_position + m_model_z_offset * Vec3f::UnitZ()).cast()) * - Geometry::translation_transform(scale_factor * m_model.get_bounding_box().size().z() * Vec3d::UnitZ()) * Geometry::rotation_transform({ M_PI, 0.0, 0.0 })) * + ? Geometry::translation_transform((m_world_position + m_model_z_offset * Vec3f::UnitZ()).cast()) * + Geometry::translation_transform(scale_factor * m_model.get_bounding_box().size().z() * Vec3d::UnitZ()) * + Geometry::rotation_transform({ M_PI, 0.0, 0.0 }) * Geometry::scale_transform(scale_factor) : Geometry::translation_transform(m_world_position.cast()); shader->set_uniform("view_model_matrix", view_matrix * model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); + Transform3d volume_world_matrix = model_matrix; + if (!m_generic_marker) + volume_world_matrix = Geometry::translation_transform(bed_inst_offset) * volume_world_matrix; + shader->set_uniform("volume_world_matrix", volume_world_matrix); + shader->set_uniform("clipping_planes[0]", clip_planes[0]); + shader->set_uniform("clipping_planes[1]", clip_planes[1]); m_model.render(); shader->stop_using(); + if (curr_cull_face) + glsafe(::glEnable(GL_CULL_FACE)); + glsafe(::glDisable(GL_BLEND)); } @@ -1181,7 +1213,8 @@ void GCodeViewer::render() // Following just makes sure that the shown marker is correct. auto marker_model_opt = wxGetApp().plater()->get_current_canvas3D()->get_current_marker_model(); - m_sequential_view.marker.init(marker_model_opt); + if (m_sequential_view.marker.init(marker_model_opt)) + m_max_bounding_box.reset(); m_sequential_view.render(legend_height, &m_viewer, curr_vertex.gcode_id); } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 8b01cb9bc2..ac3ef8bcb6 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -133,7 +133,7 @@ public: #endif // ENABLE_ACTUAL_SPEED_DEBUG public: - void init(std::optional>& model_opt); + bool init(std::optional>& model_opt); const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); } diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index a5221f8b0a..9c921c82fe 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -67,6 +67,8 @@ std::pair GLShadersManager::init() #endif // SLIC3R_OPENGL_ES // used to render toolpaths center of gravity valid &= append_shader("toolpaths_cog", { prefix + "toolpaths_cog.vs", prefix + "toolpaths_cog.fs" }); + // used to render tool marker + valid &= append_shader("tool_marker", { prefix + "tool_marker.vs", prefix + "tool_marker.fs" }); // used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview valid &= append_shader("gouraud_light", { prefix + "gouraud_light.vs", prefix + "gouraud_light.fs" }); // extend "gouraud_light" by adding clipping, used in sla gizmos