Use a 2-pass depth based outline algorithm when selected

This commit is contained in:
Noisyfox 2024-08-18 19:30:40 +08:00
parent 2acd858bf3
commit 13e18d1d65
7 changed files with 262 additions and 46 deletions

View file

@ -423,7 +423,7 @@ void GLVolume::render()
}
//BBS: add outline related logic
void GLVolume::render_with_outline(const Transform3d &view_model_matrix)
void GLVolume::render_with_outline(const GUI::Size& cnv_size)
{
if (!is_active)
return;
@ -435,37 +435,80 @@ void GLVolume::render_with_outline(const Transform3d &view_model_matrix)
ModelObjectPtrs &model_objects = GUI::wxGetApp().model().objects;
std::vector<ColorRGBA> colors = get_extruders_colors();
glEnable(GL_STENCIL_TEST);
glStencilMask(0xFF);
glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
glClear(GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_ALWAYS, 0xff, 0xFF);
const GUI::OpenGLManager::EFramebufferType framebuffers_type = GUI::OpenGLManager::get_framebuffers_type();
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Unknown) {
// No supported, degrade to normal rendering
simple_render(shader, model_objects, colors);
return;
}
simple_render(shader, model_objects, colors);
// 1st. render pass, render the model into a separate render target that has only depth buffer
GLuint depth_fbo = 0;
GLuint depth_tex = 0;
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) {
glsafe(::glGenFramebuffers(1, &depth_fbo));
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo));
// 2nd. render pass: now draw slightly scaled versions of the objects, this time disabling stencil writing.
// Because the stencil buffer is now filled with several 1s. The parts of the buffer that are 1 are not drawn, thus only drawing
// the objects' size differences, making it look like borders.
glStencilFunc(GL_NOTEQUAL, 0xff, 0xFF);
glStencilMask(0x00);
float scale = 1.02f;
ColorRGBA body_color = { 1.0f, 1.0f, 1.0f, 1.0f }; //red
glActiveTexture(GL_TEXTURE0);
glsafe(::glGenTextures(1, &depth_tex));
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr));
model.set_color(body_color);
shader->set_uniform("is_outline", true);
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_tex, 0));
} else {
glsafe(::glGenFramebuffersEXT(1, &depth_fbo));
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, depth_fbo));
Transform3d matrix = view_model_matrix;
matrix.scale(scale);
shader->set_uniform("view_model_matrix", matrix);
glActiveTexture(GL_TEXTURE0);
glsafe(::glGenTextures(1, &depth_tex));
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr));
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth_tex, 0));
}
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
if (tverts_range == std::make_pair<size_t, size_t>(0, -1))
model.render();
else
model.render(this->tverts_range);
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
shader->set_uniform("view_model_matrix", view_model_matrix);
// 2nd. render pass, just a normal render with the depth buffer passed as a texture
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) {
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0));
} else if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Ext) {
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
}
shader->set_uniform("is_outline", true);
shader->set_uniform("screen_size", Vec2f{cnv_size.get_width(), cnv_size.get_height()});
glActiveTexture(GL_TEXTURE0);
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
shader->set_uniform("depth_tex", 0);
simple_render(shader, model_objects, colors);
// Some clean up to do
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
shader->set_uniform("screen_size", 0);
shader->set_uniform("is_outline", false);
glDisable(GL_STENCIL_TEST);
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) {
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0));
if (depth_fbo != 0)
glsafe(::glDeleteFramebuffers(1, &depth_fbo));
} else if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Ext) {
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
if (depth_fbo != 0)
glsafe(::glDeleteFramebuffersEXT(1, &depth_fbo));
}
if (depth_tex != 0)
glsafe(::glDeleteTextures(1, &depth_tex));
}
//BBS add render for simple case
@ -847,8 +890,8 @@ int GLVolumeCollection::get_selection_support_threshold_angle(bool &enable_suppo
}
//BBS: add outline drawing logic
void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix,
std::function<bool(const GLVolume&)> filter_func, bool with_outline) const
void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix, const GUI::Size& cnv_size,
std::function<bool(const GLVolume&)> filter_func) const
{
GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func);
if (to_render.empty())
@ -953,9 +996,9 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
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);
//BBS: add outline related logic
//if (with_outline && volume.first->selected)
// volume.first->render_with_outline(view_matrix * model_matrix);
//else
if (volume.first->selected)
volume.first->render_with_outline(cnv_size);
else
volume.first->render();
#if ENABLE_ENVIRONMENT_MAP

View file

@ -39,6 +39,10 @@ extern Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::C
namespace Slic3r {
namespace GUI {
class Size;
}
class SLAPrintObject;
enum SLAPrintObjectStep : unsigned int;
class BuildVolume;
@ -322,7 +326,7 @@ public:
virtual void render();
//BBS: add outline related logic and add virtual specifier
virtual void render_with_outline(const Transform3d &view_model_matrix);
virtual void render_with_outline(const GUI::Size& cnv_size);
//BBS: add simple render function for thumbnail
void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector<ColorRGBA>& extruder_colors, bool ban_light =false);
@ -355,7 +359,7 @@ class GLWipeTowerVolume : public GLVolume {
public:
GLWipeTowerVolume(const std::vector<ColorRGBA>& colors);
void render() override;
void render_with_outline(const Transform3d &view_model_matrix) override { render(); }
void render_with_outline(const GUI::Size& cnv_size) override { render(); }
std::vector<GUI::GLModel> model_per_colors;
bool IsTransparent();
@ -465,8 +469,8 @@ public:
int get_selection_support_threshold_angle(bool&) const;
// Render the volumes by OpenGL.
//BBS: add outline drawing logic
void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix,
std::function<bool(const GLVolume &)> filter_func = std::function<bool(const GLVolume &)>(), bool with_outline = true) const;
void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix, const GUI::Size& cnv_size,
std::function<bool(const GLVolume &)> filter_func = std::function<bool(const GLVolume &)>()) const;
// Clear the geometry
void clear() { for (auto *v : volumes) delete v; volumes.clear(); }

View file

@ -1244,7 +1244,7 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin)
#endif // ENABLE_GCODE_VIEWER_STATISTICS
glsafe(::glEnable(GL_DEPTH_TEST));
render_shells();
render_shells(canvas_width, canvas_height);
if (m_roles.empty())
return;
@ -4023,7 +4023,7 @@ void GCodeViewer::render_toolpaths()
}
}
void GCodeViewer::render_shells()
void GCodeViewer::render_shells(int canvas_width, int canvas_height)
{
//BBS: add shell previewing logic
if ((!m_shells.previewing && !m_shells.visible) || m_shells.volumes.empty())
@ -4039,7 +4039,9 @@ void GCodeViewer::render_shells()
shader->start_using();
shader->set_uniform("emission_factor", 0.1f);
const Camera& camera = wxGetApp().plater()->get_camera();
m_shells.volumes.render(GLVolumeCollection::ERenderType::Transparent, false, camera.get_view_matrix(), camera.get_projection_matrix());
shader->set_uniform("z_far", camera.get_far_z());
shader->set_uniform("z_near", camera.get_near_z());
m_shells.volumes.render(GLVolumeCollection::ERenderType::Transparent, false, camera.get_view_matrix(), camera.get_projection_matrix(), {canvas_width, canvas_height});
shader->set_uniform("emission_factor", 0.0f);
shader->stop_using();

View file

@ -893,7 +893,7 @@ private:
//void load_shells(const Print& print);
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
void render_toolpaths();
void render_shells();
void render_shells(int canvas_width, int canvas_height);
//BBS: GUI refactor: add canvas size
void render_legend(float &legend_height, int canvas_width, int canvas_height, int right_margin);

View file

@ -7228,6 +7228,12 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
if (shader != nullptr) {
shader->start_using();
const Size& cvn_size = get_canvas_size();
{
const Camera& camera = wxGetApp().plater()->get_camera();
shader->set_uniform("z_far", camera.get_far_z());
shader->set_uniform("z_near", camera.get_near_z());
}
switch (type)
{
default:
@ -7239,7 +7245,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
if (m_picking_enabled && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) {
int object_id = m_layers_editing.last_object_id;
const Camera& camera = wxGetApp().plater()->get_camera();
m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), [object_id](const GLVolume& volume) {
m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), cvn_size, [object_id](const GLVolume& volume) {
// Which volume to paint without the layer height profile shader?
return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
});
@ -7255,14 +7261,14 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
//BBS:add assemble view related logic
// do not cull backfaces to show broken geometry, if any
const Camera& camera = wxGetApp().plater()->get_camera();
m_volumes.render(type, m_picking_enabled, camera.get_view_matrix(), camera.get_projection_matrix(), [this, canvas_type](const GLVolume& volume) {
m_volumes.render(type, m_picking_enabled, camera.get_view_matrix(), camera.get_projection_matrix(), cvn_size, [this, canvas_type](const GLVolume& volume) {
if (canvas_type == ECanvasType::CanvasAssembleView) {
return !volume.is_modifier && !volume.is_wipe_tower;
}
else {
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
}
}, with_outline);
});
}
}
else {
@ -7289,14 +7295,14 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
}*/
const Camera& camera = wxGetApp().plater()->get_camera();
//BBS:add assemble view related logic
m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), [this, canvas_type](const GLVolume& volume) {
m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), cvn_size, [this, canvas_type](const GLVolume& volume) {
if (canvas_type == ECanvasType::CanvasAssembleView) {
return !volume.is_modifier;
}
else {
return true;
}
}, with_outline);
});
if (m_canvas_type == CanvasAssembleView && m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position() > 0) {
const GLGizmosManager& gm = get_gizmos_manager();
shader->stop_using();