|
|
|
|
@@ -1218,10 +1218,22 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)
|
|
|
|
|
|
|
|
|
|
GLCanvas3D::~GLCanvas3D()
|
|
|
|
|
{
|
|
|
|
|
if (m_fxaa_texture_id != 0 && _set_current()) {
|
|
|
|
|
glsafe(::glDeleteTextures(1, &m_fxaa_texture_id));
|
|
|
|
|
m_fxaa_texture_id = 0;
|
|
|
|
|
if (_set_current()) {
|
|
|
|
|
if (m_fxaa_texture_id != 0) {
|
|
|
|
|
glsafe(::glDeleteTextures(1, &m_fxaa_texture_id));
|
|
|
|
|
m_fxaa_texture_id = 0;
|
|
|
|
|
}
|
|
|
|
|
if (m_ssao_color_texture_id != 0) {
|
|
|
|
|
glsafe(::glDeleteTextures(1, &m_ssao_color_texture_id));
|
|
|
|
|
m_ssao_color_texture_id = 0;
|
|
|
|
|
}
|
|
|
|
|
if (m_ssao_depth_texture_id != 0) {
|
|
|
|
|
glsafe(::glDeleteTextures(1, &m_ssao_depth_texture_id));
|
|
|
|
|
m_ssao_depth_texture_id = 0;
|
|
|
|
|
}
|
|
|
|
|
m_plate_shadow_mask.reset();
|
|
|
|
|
}
|
|
|
|
|
m_plate_shadow_mask_key.clear();
|
|
|
|
|
|
|
|
|
|
reset_volumes();
|
|
|
|
|
|
|
|
|
|
@@ -2039,14 +2051,16 @@ void GLCanvas3D::render(bool only_init)
|
|
|
|
|
/* view3D render*/
|
|
|
|
|
int hover_id = (m_hover_plate_idxs.size() > 0)?m_hover_plate_idxs.front():-1;
|
|
|
|
|
if (m_canvas_type == ECanvasType::CanvasView3D) {
|
|
|
|
|
//BBS: add outline logic
|
|
|
|
|
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
|
|
|
|
_render_sla_slices();
|
|
|
|
|
_render_selection();
|
|
|
|
|
if (!no_partplate)
|
|
|
|
|
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), m_show_world_axes);
|
|
|
|
|
if (!no_partplate) //BBS: add outline logic
|
|
|
|
|
_render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, only_body, hover_id, true, show_grid);
|
|
|
|
|
|
|
|
|
|
//BBS: add outline logic
|
|
|
|
|
_render_cast_shadows_on_plate(camera.get_view_matrix(), camera.get_projection_matrix());
|
|
|
|
|
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
|
|
|
|
_render_sla_slices();
|
|
|
|
|
_render_selection();
|
|
|
|
|
_render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running());
|
|
|
|
|
}
|
|
|
|
|
/* preview render */
|
|
|
|
|
@@ -2103,6 +2117,9 @@ void GLCanvas3D::render(bool only_init)
|
|
|
|
|
if (m_picking_enabled && m_rectangle_selection.is_dragging())
|
|
|
|
|
m_rectangle_selection.render(*this);
|
|
|
|
|
|
|
|
|
|
if (_is_ssao_enabled())
|
|
|
|
|
_render_ssao_pass(static_cast<unsigned int>(cnv_size.get_width()), static_cast<unsigned int>(cnv_size.get_height()));
|
|
|
|
|
|
|
|
|
|
if (_is_fxaa_enabled())
|
|
|
|
|
_render_fxaa_pass(static_cast<unsigned int>(cnv_size.get_width()), static_cast<unsigned int>(cnv_size.get_height()));
|
|
|
|
|
|
|
|
|
|
@@ -7502,6 +7519,14 @@ bool GLCanvas3D::_is_fxaa_enabled() const
|
|
|
|
|
return wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_FXAA_ENABLED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GLCanvas3D::_is_ssao_enabled() const
|
|
|
|
|
{
|
|
|
|
|
if (wxGetApp().app_config == nullptr)
|
|
|
|
|
return false;
|
|
|
|
|
return wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_MODE) &&
|
|
|
|
|
wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SSAO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GLCanvas3D::_get_effective_fps_cap() const
|
|
|
|
|
{
|
|
|
|
|
if (wxGetApp().app_config == nullptr)
|
|
|
|
|
@@ -7596,6 +7621,159 @@ void GLCanvas3D::_render_fxaa_pass(unsigned int width, unsigned int height)
|
|
|
|
|
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GLCanvas3D::_render_ssao_pass(unsigned int width, unsigned int height)
|
|
|
|
|
{
|
|
|
|
|
if (width == 0 || height == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
GLShaderProgram* shader = wxGetApp().get_shader("ssao");
|
|
|
|
|
if (shader == nullptr)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (m_ssao_color_texture_id == 0) {
|
|
|
|
|
glsafe(::glGenTextures(1, &m_ssao_color_texture_id));
|
|
|
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_color_texture_id));
|
|
|
|
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
|
|
|
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
if (m_ssao_depth_texture_id == 0) {
|
|
|
|
|
glsafe(::glGenTextures(1, &m_ssao_depth_texture_id));
|
|
|
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_depth_texture_id));
|
|
|
|
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
|
|
|
|
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_ssao_texture_size[0] != width || m_ssao_texture_size[1] != height) {
|
|
|
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_color_texture_id));
|
|
|
|
|
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
|
|
|
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_depth_texture_id));
|
|
|
|
|
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr));
|
|
|
|
|
m_ssao_texture_size = { { width, height } };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_color_texture_id));
|
|
|
|
|
glsafe(::glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height));
|
|
|
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_depth_texture_id));
|
|
|
|
|
glsafe(::glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height));
|
|
|
|
|
|
|
|
|
|
const Camera& camera = wxGetApp().plater()->get_camera();
|
|
|
|
|
|
|
|
|
|
GLint prev_stencil_mask = 0xFF;
|
|
|
|
|
glsafe(::glGetIntegerv(GL_STENCIL_WRITEMASK, &prev_stencil_mask));
|
|
|
|
|
GLboolean prev_stencil_test = GL_FALSE;
|
|
|
|
|
glsafe(::glGetBooleanv(GL_STENCIL_TEST, &prev_stencil_test));
|
|
|
|
|
GLboolean prev_depth_mask = GL_TRUE;
|
|
|
|
|
glsafe(::glGetBooleanv(GL_DEPTH_WRITEMASK, &prev_depth_mask));
|
|
|
|
|
GLint prev_depth_func = GL_LESS;
|
|
|
|
|
glsafe(::glGetIntegerv(GL_DEPTH_FUNC, &prev_depth_func));
|
|
|
|
|
|
|
|
|
|
glsafe(::glDisable(GL_DEPTH_TEST));
|
|
|
|
|
glsafe(::glDisable(GL_BLEND));
|
|
|
|
|
|
|
|
|
|
// Build stencil mask for bed/plate and apply SSAO only outside this mask.
|
|
|
|
|
glsafe(::glEnable(GL_STENCIL_TEST));
|
|
|
|
|
glsafe(::glStencilMask(0xFF));
|
|
|
|
|
glsafe(::glClearStencil(0));
|
|
|
|
|
glsafe(::glClear(GL_STENCIL_BUFFER_BIT));
|
|
|
|
|
glsafe(::glStencilFunc(GL_ALWAYS, 1, 0xFF));
|
|
|
|
|
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE));
|
|
|
|
|
// Mark only visible plate pixels (do not exclude objects in front of plate).
|
|
|
|
|
glsafe(::glEnable(GL_DEPTH_TEST));
|
|
|
|
|
glsafe(::glDepthMask(GL_FALSE));
|
|
|
|
|
glsafe(::glDepthFunc(GL_LEQUAL));
|
|
|
|
|
|
|
|
|
|
GLboolean prev_color_mask[4] = { GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE };
|
|
|
|
|
glsafe(::glGetBooleanv(GL_COLOR_WRITEMASK, prev_color_mask));
|
|
|
|
|
glsafe(::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
|
|
|
|
|
|
|
|
|
|
if (const BuildVolume& build_volume = m_bed.build_volume(); build_volume.valid()) {
|
|
|
|
|
GLShaderProgram* flat = wxGetApp().get_shader("flat");
|
|
|
|
|
if (flat != nullptr) {
|
|
|
|
|
flat->start_using();
|
|
|
|
|
flat->set_uniform("projection_matrix", camera.get_projection_matrix());
|
|
|
|
|
|
|
|
|
|
GLModel plate_mask;
|
|
|
|
|
GLModel::Geometry mask;
|
|
|
|
|
mask.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 };
|
|
|
|
|
|
|
|
|
|
if (build_volume.type() == BuildVolume_Type::Rectangle) {
|
|
|
|
|
const BoundingBox3Base<Vec3d> bb = build_volume.bounding_volume();
|
|
|
|
|
mask.reserve_vertices(4);
|
|
|
|
|
mask.reserve_indices(6);
|
|
|
|
|
mask.add_vertex(Vec3f((float)bb.min.x(), (float)bb.min.y(), 0.0f));
|
|
|
|
|
mask.add_vertex(Vec3f((float)bb.max.x(), (float)bb.min.y(), 0.0f));
|
|
|
|
|
mask.add_vertex(Vec3f((float)bb.max.x(), (float)bb.max.y(), 0.0f));
|
|
|
|
|
mask.add_vertex(Vec3f((float)bb.min.x(), (float)bb.max.y(), 0.0f));
|
|
|
|
|
mask.add_triangle(0, 1, 2);
|
|
|
|
|
mask.add_triangle(0, 2, 3);
|
|
|
|
|
} else if (build_volume.type() == BuildVolume_Type::Circle) {
|
|
|
|
|
const Vec2f c = Vec2f(unscaled<float>(build_volume.circle().center.x()), unscaled<float>(build_volume.circle().center.y()));
|
|
|
|
|
const float r = unscaled<float>(build_volume.circle().radius);
|
|
|
|
|
const int segments = 64;
|
|
|
|
|
mask.reserve_vertices(segments + 1);
|
|
|
|
|
mask.reserve_indices(segments * 3);
|
|
|
|
|
mask.add_vertex(Vec3f(c.x(), c.y(), 0.0f));
|
|
|
|
|
for (int i = 0; i < segments; ++i) {
|
|
|
|
|
const float a = (2.0f * float(PI) * float(i)) / float(segments);
|
|
|
|
|
mask.add_vertex(Vec3f(c.x() + r * std::cos(a), c.y() + r * std::sin(a), 0.0f));
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < segments; ++i) {
|
|
|
|
|
const unsigned int i1 = 1 + i;
|
|
|
|
|
const unsigned int i2 = 1 + ((i + 1) % segments);
|
|
|
|
|
mask.add_triangle(0, i1, i2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mask.vertices_count() > 0 && mask.indices_count() > 0) {
|
|
|
|
|
plate_mask.init_from(std::move(mask));
|
|
|
|
|
flat->set_uniform("view_model_matrix", camera.get_view_matrix());
|
|
|
|
|
plate_mask.render(flat);
|
|
|
|
|
}
|
|
|
|
|
flat->stop_using();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glsafe(::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
|
|
|
|
|
glsafe(::glDisable(GL_DEPTH_TEST));
|
|
|
|
|
glsafe(::glStencilMask(0x00));
|
|
|
|
|
glsafe(::glStencilFunc(GL_NOTEQUAL, 1, 0xFF));
|
|
|
|
|
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP));
|
|
|
|
|
|
|
|
|
|
shader->start_using();
|
|
|
|
|
shader->set_uniform("view_model_matrix", Transform3d::Identity());
|
|
|
|
|
shader->set_uniform("projection_matrix", Transform3d::Identity());
|
|
|
|
|
shader->set_uniform("color_texture", 0);
|
|
|
|
|
shader->set_uniform("depth_texture", 1);
|
|
|
|
|
shader->set_uniform("inv_tex_size", Vec2f(1.0f / static_cast<float>(width), 1.0f / static_cast<float>(height)));
|
|
|
|
|
shader->set_uniform("z_near", camera.get_near_z());
|
|
|
|
|
shader->set_uniform("z_far", camera.get_far_z());
|
|
|
|
|
|
|
|
|
|
glsafe(::glActiveTexture(GL_TEXTURE0));
|
|
|
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_color_texture_id));
|
|
|
|
|
glsafe(::glActiveTexture(GL_TEXTURE1));
|
|
|
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_depth_texture_id));
|
|
|
|
|
m_background.render();
|
|
|
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
|
|
|
|
glsafe(::glActiveTexture(GL_TEXTURE0));
|
|
|
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
|
|
|
|
shader->stop_using();
|
|
|
|
|
|
|
|
|
|
if (!prev_stencil_test)
|
|
|
|
|
glsafe(::glDisable(GL_STENCIL_TEST));
|
|
|
|
|
glsafe(::glStencilMask(prev_stencil_mask));
|
|
|
|
|
glsafe(::glColorMask(prev_color_mask[0], prev_color_mask[1], prev_color_mask[2], prev_color_mask[3]));
|
|
|
|
|
|
|
|
|
|
glsafe(::glDepthMask(prev_depth_mask));
|
|
|
|
|
glsafe(::glDepthFunc(prev_depth_func));
|
|
|
|
|
glsafe(::glEnable(GL_DEPTH_TEST));
|
|
|
|
|
glsafe(::glEnable(GL_BLEND));
|
|
|
|
|
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GLCanvas3D::_render_background()
|
|
|
|
|
{
|
|
|
|
|
bool use_error_color = false;
|
|
|
|
|
@@ -7686,6 +7864,206 @@ void GLCanvas3D::_render_platelist(const Transform3d& view_matrix, const Transfo
|
|
|
|
|
wxGetApp().plater()->get_partplate_list().render(view_matrix, projection_matrix, bottom, only_current, only_body, hover_id, render_cali, show_grid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GLCanvas3D::_render_cast_shadows_on_plate(const Transform3d& view_matrix, const Transform3d& projection_matrix)
|
|
|
|
|
{
|
|
|
|
|
// Check if shadow rendering is enabled in configuration
|
|
|
|
|
if (wxGetApp().app_config == nullptr)
|
|
|
|
|
return;
|
|
|
|
|
if (!wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_MODE))
|
|
|
|
|
return;
|
|
|
|
|
if (!wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS))
|
|
|
|
|
return;
|
|
|
|
|
if (m_volumes.empty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
GLShaderProgram* shader = wxGetApp().get_shader("flat");
|
|
|
|
|
if (shader == nullptr)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Fixed light direction (pointing downward at an angle)
|
|
|
|
|
// Drive shadow direction from current view angle: define light in eye-space,
|
|
|
|
|
// then transform it to world-space with inverse view rotation.
|
|
|
|
|
const Vec3d light_dir_eye = Vec3d(-0.4574957, 0.4574957, 0.7624929).normalized();
|
|
|
|
|
const Matrix3d view_rot = view_matrix.matrix().block<3, 3>(0, 0);
|
|
|
|
|
const Vec3d light_dir_to_light = (view_rot.transpose() * light_dir_eye).normalized();
|
|
|
|
|
const Vec3d ray_dir = -light_dir_to_light; // Direction of shadow projection
|
|
|
|
|
|
|
|
|
|
if (std::abs(ray_dir.z()) < 1e-6)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Shadow projection matrix - flattens geometry onto Z=0 plane along light direction
|
|
|
|
|
Matrix4d shadow_proj = Matrix4d::Identity();
|
|
|
|
|
shadow_proj(0, 2) = -ray_dir.x() / ray_dir.z();
|
|
|
|
|
shadow_proj(1, 2) = -ray_dir.y() / ray_dir.z();
|
|
|
|
|
shadow_proj(2, 0) = 0.0;
|
|
|
|
|
shadow_proj(2, 1) = 0.0;
|
|
|
|
|
shadow_proj(2, 2) = 0.0;
|
|
|
|
|
shadow_proj(2, 3) = 0.01; // Bias to prevent shadow acne
|
|
|
|
|
|
|
|
|
|
// Save OpenGL state
|
|
|
|
|
GLint prev_depth_func = GL_LESS;
|
|
|
|
|
glsafe(::glGetIntegerv(GL_DEPTH_FUNC, &prev_depth_func));
|
|
|
|
|
GLboolean prev_depth_mask = GL_TRUE;
|
|
|
|
|
glsafe(::glGetBooleanv(GL_DEPTH_WRITEMASK, &prev_depth_mask));
|
|
|
|
|
GLint prev_stencil_mask = 0xFF;
|
|
|
|
|
glsafe(::glGetIntegerv(GL_STENCIL_WRITEMASK, &prev_stencil_mask));
|
|
|
|
|
GLboolean prev_stencil_test = GL_FALSE;
|
|
|
|
|
glsafe(::glGetBooleanv(GL_STENCIL_TEST, &prev_stencil_test));
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// PASS 0: Create stencil mask for the build plate (value = 1)
|
|
|
|
|
// ============================================================
|
|
|
|
|
glsafe(::glEnable(GL_STENCIL_TEST));
|
|
|
|
|
glsafe(::glStencilMask(0xFF));
|
|
|
|
|
glsafe(::glClearStencil(0));
|
|
|
|
|
glsafe(::glClear(GL_STENCIL_BUFFER_BIT));
|
|
|
|
|
|
|
|
|
|
glsafe(::glStencilFunc(GL_ALWAYS, 1, 0xFF));
|
|
|
|
|
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE));
|
|
|
|
|
|
|
|
|
|
glsafe(::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
|
|
|
|
|
glsafe(::glDisable(GL_DEPTH_TEST));
|
|
|
|
|
|
|
|
|
|
shader->start_using();
|
|
|
|
|
shader->set_uniform("projection_matrix", projection_matrix);
|
|
|
|
|
|
|
|
|
|
// Draw the build plate (cached model to avoid per-frame uploads)
|
|
|
|
|
if (const BuildVolume& build_volume = m_bed.build_volume(); build_volume.valid()) {
|
|
|
|
|
const std::string mask_key = build_volume.type() == BuildVolume_Type::Rectangle
|
|
|
|
|
? (boost::format("rect|%1$.5f|%2$.5f|%3$.5f|%4$.5f")
|
|
|
|
|
% build_volume.bounding_volume().min.x()
|
|
|
|
|
% build_volume.bounding_volume().min.y()
|
|
|
|
|
% build_volume.bounding_volume().max.x()
|
|
|
|
|
% build_volume.bounding_volume().max.y()).str()
|
|
|
|
|
: (build_volume.type() == BuildVolume_Type::Circle
|
|
|
|
|
? (boost::format("circle|%1$.5f|%2$.5f|%3$.5f")
|
|
|
|
|
% unscaled<double>(build_volume.circle().center.x())
|
|
|
|
|
% unscaled<double>(build_volume.circle().center.y())
|
|
|
|
|
% unscaled<double>(build_volume.circle().radius)).str()
|
|
|
|
|
: std::string("invalid"));
|
|
|
|
|
|
|
|
|
|
if (mask_key != m_plate_shadow_mask_key) {
|
|
|
|
|
m_plate_shadow_mask.reset();
|
|
|
|
|
m_plate_shadow_mask_key = mask_key;
|
|
|
|
|
|
|
|
|
|
GLModel::Geometry mask;
|
|
|
|
|
mask.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 };
|
|
|
|
|
|
|
|
|
|
if (build_volume.type() == BuildVolume_Type::Rectangle) {
|
|
|
|
|
const BoundingBox3Base<Vec3d> bb = build_volume.bounding_volume();
|
|
|
|
|
mask.reserve_vertices(4);
|
|
|
|
|
mask.reserve_indices(6);
|
|
|
|
|
mask.add_vertex(Vec3f((float)bb.min.x(), (float)bb.min.y(), 0.0f));
|
|
|
|
|
mask.add_vertex(Vec3f((float)bb.max.x(), (float)bb.min.y(), 0.0f));
|
|
|
|
|
mask.add_vertex(Vec3f((float)bb.max.x(), (float)bb.max.y(), 0.0f));
|
|
|
|
|
mask.add_vertex(Vec3f((float)bb.min.x(), (float)bb.max.y(), 0.0f));
|
|
|
|
|
mask.add_triangle(0, 1, 2);
|
|
|
|
|
mask.add_triangle(0, 2, 3);
|
|
|
|
|
}
|
|
|
|
|
else if (build_volume.type() == BuildVolume_Type::Circle) {
|
|
|
|
|
const Vec2f c = Vec2f(unscaled<float>(build_volume.circle().center.x()), unscaled<float>(build_volume.circle().center.y()));
|
|
|
|
|
const float r = unscaled<float>(build_volume.circle().radius);
|
|
|
|
|
const int segments = 64;
|
|
|
|
|
mask.reserve_vertices(segments + 1);
|
|
|
|
|
mask.reserve_indices(segments * 3);
|
|
|
|
|
mask.add_vertex(Vec3f(c.x(), c.y(), 0.0f));
|
|
|
|
|
for (int i = 0; i < segments; ++i) {
|
|
|
|
|
const float a = (2.0f * float(PI) * float(i)) / float(segments);
|
|
|
|
|
mask.add_vertex(Vec3f(c.x() + r * std::cos(a), c.y() + r * std::sin(a), 0.0f));
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < segments; ++i) {
|
|
|
|
|
const unsigned int i1 = 1 + i;
|
|
|
|
|
const unsigned int i2 = 1 + ((i + 1) % segments);
|
|
|
|
|
mask.add_triangle(0, i1, i2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mask.vertices_count() > 0 && mask.indices_count() > 0)
|
|
|
|
|
m_plate_shadow_mask.init_from(std::move(mask));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_plate_shadow_mask.is_initialized()) {
|
|
|
|
|
shader->set_uniform("view_model_matrix", view_matrix);
|
|
|
|
|
m_plate_shadow_mask.render(shader);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// PASS 1: Project object shadows onto plate (increment stencil to 2)
|
|
|
|
|
// ============================================================
|
|
|
|
|
// Only render where plate exists (stencil == 1), then increment to 2
|
|
|
|
|
glsafe(::glStencilFunc(GL_EQUAL, 1, 0xFF));
|
|
|
|
|
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_INCR));
|
|
|
|
|
|
|
|
|
|
glsafe(::glDepthMask(GL_FALSE));
|
|
|
|
|
glsafe(::glEnable(GL_DEPTH_TEST));
|
|
|
|
|
glsafe(::glDepthFunc(GL_ALWAYS)); // Shadows don't need depth testing
|
|
|
|
|
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
|
|
|
|
|
glsafe(::glPolygonOffset(-2.0f, -2.0f));
|
|
|
|
|
glsafe(::glDisable(GL_CULL_FACE));
|
|
|
|
|
|
|
|
|
|
// Render projected shadow geometry
|
|
|
|
|
for (GLVolume* volume : m_volumes.volumes) {
|
|
|
|
|
if (volume == nullptr || !volume->is_active || !volume->printable || volume->is_modifier || volume->is_wipe_tower)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// CRITICAL FIX: Apply shadow projection in object's local space, then to world, then to view
|
|
|
|
|
// This ensures shadows are cast from the object's actual position
|
|
|
|
|
Matrix4d world_matrix = volume->world_matrix().matrix();
|
|
|
|
|
|
|
|
|
|
// Project the shadow - this flattens the geometry onto Z=0 in WORLD space
|
|
|
|
|
Matrix4d shadow_world_matrix = shadow_proj * world_matrix;
|
|
|
|
|
|
|
|
|
|
// Transform to view space for rendering
|
|
|
|
|
Matrix4d view_shadow_matrix = view_matrix.matrix() * shadow_world_matrix;
|
|
|
|
|
|
|
|
|
|
shader->set_uniform("view_model_matrix", view_shadow_matrix);
|
|
|
|
|
shader->set_uniform("projection_matrix", projection_matrix);
|
|
|
|
|
|
|
|
|
|
volume->model.render(shader);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// PASS 2: Draw shadow color where stencil == 2
|
|
|
|
|
// ============================================================
|
|
|
|
|
glsafe(::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
|
|
|
|
|
glsafe(::glStencilFunc(GL_EQUAL, 2, 0xFF));
|
|
|
|
|
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP));
|
|
|
|
|
glsafe(::glStencilMask(0x00));
|
|
|
|
|
|
|
|
|
|
glsafe(::glDepthFunc(GL_ALWAYS));
|
|
|
|
|
glsafe(::glEnable(GL_BLEND));
|
|
|
|
|
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
|
|
|
|
|
|
|
|
|
// Draw shadow fill
|
|
|
|
|
shader->set_uniform("view_model_matrix", Transform3d::Identity());
|
|
|
|
|
shader->set_uniform("projection_matrix", Transform3d::Identity());
|
|
|
|
|
|
|
|
|
|
const ColorRGBA shadow_fill_color(0.0f, 0.0f, 0.0f, 0.4f); // Darker shadow for visibility
|
|
|
|
|
const ColorRGBA prev_bg_color = m_background.get_geometry().color;
|
|
|
|
|
m_background.set_color(shadow_fill_color);
|
|
|
|
|
shader->set_uniform("uniform_color", shadow_fill_color);
|
|
|
|
|
m_background.render(shader);
|
|
|
|
|
m_background.set_color(prev_bg_color);
|
|
|
|
|
shader->set_uniform("uniform_color", prev_bg_color);
|
|
|
|
|
|
|
|
|
|
shader->stop_using();
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// RESTORE STATE
|
|
|
|
|
// ============================================================
|
|
|
|
|
glsafe(::glEnable(GL_DEPTH_TEST));
|
|
|
|
|
glsafe(::glDepthMask(prev_depth_mask));
|
|
|
|
|
glsafe(::glDepthFunc(prev_depth_func));
|
|
|
|
|
glsafe(::glEnable(GL_CULL_FACE));
|
|
|
|
|
glsafe(::glDisable(GL_POLYGON_OFFSET_FILL));
|
|
|
|
|
glsafe(::glDisable(GL_BLEND));
|
|
|
|
|
|
|
|
|
|
if (!prev_stencil_test)
|
|
|
|
|
glsafe(::glDisable(GL_STENCIL_TEST));
|
|
|
|
|
glsafe(::glStencilMask(prev_stencil_mask));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GLCanvas3D::_render_plane() const
|
|
|
|
|
{
|
|
|
|
|
;//TODO render assemble plane
|
|
|
|
|
@@ -7756,12 +8134,20 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
|
|
|
|
else
|
|
|
|
|
m_volumes.set_show_sinking_contours(!m_gizmos.is_hiding_instances());
|
|
|
|
|
|
|
|
|
|
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
|
|
|
|
|
const bool realistic_mode = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_MODE);
|
|
|
|
|
const bool realistic_phong = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_PHONG);
|
|
|
|
|
const std::string shader_name = (realistic_mode && realistic_phong) ? "phong" : "gouraud";
|
|
|
|
|
GLShaderProgram* shader = wxGetApp().get_shader(shader_name);
|
|
|
|
|
if (shader == nullptr && shader_name != "gouraud")
|
|
|
|
|
shader = wxGetApp().get_shader("gouraud");
|
|
|
|
|
ECanvasType canvas_type = this->m_canvas_type;
|
|
|
|
|
bool partly_inside_enable = canvas_type == ECanvasType::CanvasAssembleView ? false : true;
|
|
|
|
|
if (shader != nullptr) {
|
|
|
|
|
shader->start_using();
|
|
|
|
|
|
|
|
|
|
const bool phong_ssao = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SSAO);
|
|
|
|
|
shader->set_uniform("enable_ssao", phong_ssao);
|
|
|
|
|
|
|
|
|
|
const Size& cvn_size = get_canvas_size();
|
|
|
|
|
{
|
|
|
|
|
const Camera& camera = wxGetApp().plater()->get_camera();
|
|
|
|
|
@@ -8836,6 +9222,15 @@ void GLCanvas3D::_render_canvas_toolbar()
|
|
|
|
|
[this]{wxGetApp().toggle_show_outline();}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
create_menu_item( _utf8(L("Realistic View")),
|
|
|
|
|
true,
|
|
|
|
|
cfg->get_bool(SETTING_OPENGL_REALISTIC_MODE),
|
|
|
|
|
[this, &cfg]{
|
|
|
|
|
cfg->set_bool(SETTING_OPENGL_REALISTIC_MODE, !cfg->get_bool(SETTING_OPENGL_REALISTIC_MODE));
|
|
|
|
|
cfg->save();
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
ImGui::Separator();
|
|
|
|
|
|
|
|
|
|
create_menu_item( _utf8(L("Perspective")),
|
|
|
|
|
|