mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-15 20:01:31 +00:00
Faster Preview View (#15884)
Co-authored-by: Kris Austin <kris.austin@gmail.com>
This commit is contained in:
+223
-75
@@ -1303,6 +1303,7 @@ GLCanvas3D::~GLCanvas3D()
|
||||
{
|
||||
if (_set_current()) {
|
||||
m_scene_cache.reset();
|
||||
m_frame_profiler.reset();
|
||||
if (m_fxaa_texture_id != 0) {
|
||||
glsafe(::glDeleteTextures(1, &m_fxaa_texture_id));
|
||||
m_fxaa_texture_id = 0;
|
||||
@@ -2166,7 +2167,7 @@ void GLCanvas3D::_render_frame(bool scene_dirty, bool only_init)
|
||||
|
||||
wxGetApp().imgui()->new_frame();
|
||||
|
||||
if (m_picking_enabled) {
|
||||
if (m_picking_enabled && !m_benchmarking) {
|
||||
if (m_rectangle_selection.is_dragging())
|
||||
// picking pass using rectangle selection
|
||||
_rectangular_selection_picking_pass();
|
||||
@@ -2191,22 +2192,30 @@ void GLCanvas3D::_render_frame(bool scene_dirty, bool only_init)
|
||||
// An overlay-only frame reuses the last scene pass. The overlay is rebuilt either way, and drawn
|
||||
// below once it is known whether the frame differs from the one on screen.
|
||||
const bool reuse_scene = !scene_dirty && _can_reuse_cached_scene(camera);
|
||||
// Only frames that redraw the scene are profiled.
|
||||
if (!reuse_scene && (_is_render_timings_enabled() || m_frame_profiler.is_averaging()))
|
||||
m_frame_profiler.begin_frame();
|
||||
Slic3r::ScopeGuard profiler_guard([this]() { m_frame_profiler.end_frame(); });
|
||||
if (!reuse_scene) {
|
||||
_render_scene(camera, cnv_size);
|
||||
if (!overlay_tick)
|
||||
m_render_stats.increment_scene_fps_counter();
|
||||
}
|
||||
|
||||
if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview)
|
||||
if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview) {
|
||||
// BBS: GUI refactor: add canvas size as parameters
|
||||
_render_gcode_overlay(cnv_size.get_width(), cnv_size.get_height());
|
||||
m_frame_profiler.mark("legend");
|
||||
}
|
||||
|
||||
// draw overlays
|
||||
_render_overlays();
|
||||
m_frame_profiler.mark("ui");
|
||||
|
||||
const int current_fps = m_render_stats.get_fps_and_reset_if_needed();
|
||||
if (_is_fps_overlay_enabled()) {
|
||||
if (_is_fps_overlay_enabled() || _is_render_timings_enabled())
|
||||
_render_fps_overlay(current_fps);
|
||||
if (_is_fps_overlay_enabled()) {
|
||||
// The timer requests an overlay-only frame a second from now. A frame it requested
|
||||
// re-arms it only while a count is above zero.
|
||||
if (!overlay_tick || current_fps > 0 || m_render_stats.get_scene_fps() > 0)
|
||||
@@ -2296,12 +2305,14 @@ void GLCanvas3D::_render_frame(bool scene_dirty, bool only_init)
|
||||
wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overlay_window_width(), bottom_margin, right_margin);
|
||||
wxGetApp().plater()->get_dailytips()->render();
|
||||
}
|
||||
m_frame_profiler.mark("notifications");
|
||||
|
||||
ImDrawData* draw_data = wxGetApp().imgui()->end_frame();
|
||||
|
||||
std::optional<size_t> signature;
|
||||
if (_is_frame_skipping_enabled())
|
||||
signature = _overlay_signature(draw_data);
|
||||
m_frame_profiler.mark("imgui end");
|
||||
|
||||
if (reuse_scene) {
|
||||
// A reused scene under an unchanged overlay is the frame already on screen.
|
||||
@@ -2311,13 +2322,16 @@ void GLCanvas3D::_render_frame(bool scene_dirty, bool only_init)
|
||||
}
|
||||
|
||||
_render_overlay_toolbars();
|
||||
m_frame_profiler.mark("toolbars");
|
||||
|
||||
wxGetApp().imgui()->render(draw_data);
|
||||
m_frame_profiler.mark("imgui draw");
|
||||
|
||||
// On Wayland, eglSwapBuffers blocks when the canvas is hidden or
|
||||
// occluded. Skip the swap to avoid stalling the render loop.
|
||||
if (m_canvas->IsShownOnScreen()) {
|
||||
m_canvas->SwapBuffers();
|
||||
m_frame_profiler.mark("swap");
|
||||
if (!overlay_tick)
|
||||
m_render_stats.increment_fps_counter();
|
||||
m_presented_signature = signature;
|
||||
@@ -2362,20 +2376,25 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size)
|
||||
// floating would read as a rendering fault rather than a deliberate view option.
|
||||
if (show_bed)
|
||||
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), m_show_world_axes);
|
||||
m_frame_profiler.mark("bed");
|
||||
if (show_bed) //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);
|
||||
if (m_axes_at_bed_center && show_bed)
|
||||
// Design tab: replace the plate's corner-origin grid with the origin-centred CAD grid.
|
||||
_render_cad_grid(camera.get_view_matrix(), camera.get_projection_matrix());
|
||||
m_frame_profiler.mark("plates");
|
||||
|
||||
//BBS: add outline logic
|
||||
// Depth pass for object-on-object and self shadows; consumed by the gouraud shader below.
|
||||
_render_shadows(camera.get_view_matrix(), camera.get_projection_matrix());
|
||||
m_frame_profiler.mark("shadows");
|
||||
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
||||
m_frame_profiler.mark("objects");
|
||||
_render_sla_slices();
|
||||
_render_selection();
|
||||
_render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running());
|
||||
_render_wireframe_overlay();
|
||||
m_frame_profiler.mark("transparent");
|
||||
}
|
||||
/* preview render */
|
||||
else if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview) {
|
||||
@@ -2384,12 +2403,17 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size)
|
||||
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
||||
_render_sla_slices();
|
||||
_render_selection();
|
||||
m_frame_profiler.mark("objects");
|
||||
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), m_show_world_axes);
|
||||
m_frame_profiler.mark("bed");
|
||||
_render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, true, hover_id);
|
||||
m_frame_profiler.mark("plates");
|
||||
// Realistic view: the print casts a shadow onto the plate here as it does in View3D.
|
||||
_render_shadows(camera.get_view_matrix(), camera.get_projection_matrix());
|
||||
m_frame_profiler.mark("shadows");
|
||||
// BBS: GUI refactor: add canvas size as parameters
|
||||
_render_gcode(cnv_size.get_width(), cnv_size.get_height());
|
||||
m_frame_profiler.mark("gcode");
|
||||
}
|
||||
/* assemble render*/
|
||||
else if (m_canvas_type == ECanvasType::CanvasAssembleView) {
|
||||
@@ -2406,6 +2430,7 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size)
|
||||
// BBS: add outline logic
|
||||
_render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running());
|
||||
_render_wireframe_overlay();
|
||||
m_frame_profiler.mark("objects");
|
||||
}
|
||||
|
||||
_render_sequential_clearance();
|
||||
@@ -2428,12 +2453,17 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size)
|
||||
|
||||
if (m_picking_enabled && m_rectangle_selection.is_dragging())
|
||||
m_rectangle_selection.render(*this);
|
||||
m_frame_profiler.mark("gizmos");
|
||||
|
||||
if (_is_ssao_enabled())
|
||||
if (_is_ssao_enabled()) {
|
||||
_render_ssao_pass(static_cast<unsigned int>(cnv_size.get_width()), static_cast<unsigned int>(cnv_size.get_height()));
|
||||
m_frame_profiler.mark("ssao");
|
||||
}
|
||||
|
||||
if (_is_fxaa_enabled())
|
||||
if (_is_fxaa_enabled()) {
|
||||
_render_fxaa_pass(static_cast<unsigned int>(cnv_size.get_width()), static_cast<unsigned int>(cnv_size.get_height()));
|
||||
m_frame_profiler.mark("fxaa");
|
||||
}
|
||||
|
||||
// Design tab: interactive 2D sketch overlay, drawn over the scene but
|
||||
// beneath the UI overlays (toolbars, labels).
|
||||
@@ -2443,6 +2473,7 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size)
|
||||
#endif
|
||||
|
||||
_capture_scene_cache(camera);
|
||||
m_frame_profiler.mark("cache");
|
||||
}
|
||||
|
||||
void GLCanvas3D::render_thumbnail(ThumbnailData & thumbnail_data,
|
||||
@@ -3435,7 +3466,7 @@ void GLCanvas3D::unbind_event_handlers()
|
||||
|
||||
void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
||||
{
|
||||
if (!m_initialized)
|
||||
if (!m_initialized || m_benchmarking)
|
||||
return;
|
||||
|
||||
// Toolbar states, notifications and ImGui's own layout settling only touch the overlay.
|
||||
@@ -7873,7 +7904,51 @@ int GLCanvas3D::_get_effective_fps_cap() const
|
||||
|
||||
bool GLCanvas3D::_is_fps_overlay_enabled() const
|
||||
{
|
||||
return wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_SHOW_FPS_OVERLAY);
|
||||
return !m_benchmarking && wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_SHOW_FPS_OVERLAY);
|
||||
}
|
||||
|
||||
// Above the front left of the plate.
|
||||
static const Vec3d STATIC_LIGHT_DIR = Vec3d(-0.4, -0.6, 1.0).normalized();
|
||||
|
||||
std::optional<Vec3d> GLCanvas3D::_static_light_dir_eye() const
|
||||
{
|
||||
if (!_is_realistic_view_enabled() || _shadow_mode() != EShadowMode::Static)
|
||||
return std::nullopt;
|
||||
const Matrix3d view_rot = wxGetApp().plater()->get_camera().get_view_matrix().matrix().block<3, 3>(0, 0);
|
||||
return Vec3d((view_rot * STATIC_LIGHT_DIR).normalized());
|
||||
}
|
||||
|
||||
GLCanvas3D::EShadowMode GLCanvas3D::_shadow_mode() const
|
||||
{
|
||||
const std::string mode = wxGetApp().app_config != nullptr ? wxGetApp().app_config->get(SETTING_OPENGL_REALISTIC_SHADOWS) : std::string();
|
||||
return mode == "static" ? EShadowMode::Static : mode == "orbit" ? EShadowMode::Orbit : EShadowMode::Off;
|
||||
}
|
||||
|
||||
size_t GLCanvas3D::_shadow_casters_signature(bool toolpath_casters) const
|
||||
{
|
||||
if (toolpath_casters)
|
||||
return m_gcode_viewer.shadow_casters_signature();
|
||||
|
||||
size_t hash = 1;
|
||||
for (const GLVolume* volume : m_volumes.volumes) {
|
||||
if (volume == nullptr || !volume->is_active || !volume->printable || volume->is_modifier || volume->is_wipe_tower)
|
||||
continue;
|
||||
boost::hash_combine(hash, volume);
|
||||
boost::hash_combine(hash, volume->model.vertices_count());
|
||||
boost::hash_combine(hash, volume->model.indices_count());
|
||||
const BoundingBoxf3& bb = volume->model.get_bounding_box();
|
||||
for (double value : { bb.min.x(), bb.min.y(), bb.min.z(), bb.max.x(), bb.max.y(), bb.max.z() })
|
||||
boost::hash_combine(hash, value);
|
||||
const Transform3d world = volume->world_matrix();
|
||||
for (int i = 0; i < 16; ++i)
|
||||
boost::hash_combine(hash, world.matrix().data()[i]);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_is_render_timings_enabled() const
|
||||
{
|
||||
return !m_benchmarking && wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_SHOW_RENDER_TIMINGS);
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_is_scene_cache_enabled() const
|
||||
@@ -7908,14 +7983,22 @@ bool GLCanvas3D::_is_frame_skipping_enabled() const
|
||||
|
||||
void GLCanvas3D::_render_fps_overlay(int fps) const
|
||||
{
|
||||
if (fps < 0)
|
||||
const bool show_fps = _is_fps_overlay_enabled() && fps >= 0;
|
||||
const std::vector<FrameProfiler::Section>& sections = m_frame_profiler.sections();
|
||||
const bool show_timings = _is_render_timings_enabled() && !sections.empty();
|
||||
if (!show_fps && !show_timings)
|
||||
return;
|
||||
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
const float margin = 10.0f * get_scale();
|
||||
const ImVec2 display_size = ImGui::GetIO().DisplaySize;
|
||||
ImGui::SetNextWindowPos(ImVec2(display_size.x - margin, margin), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
|
||||
ImVec2 pos(display_size.x - margin, margin);
|
||||
// The Preview legend takes the top-right corner.
|
||||
if (const ImGuiWindow* legend = ImGui::FindWindowByName("Legend"); m_canvas_type == ECanvasType::CanvasPreview && legend != nullptr && legend->Active)
|
||||
pos = ImVec2(legend->Pos.x - margin, legend->Pos.y);
|
||||
ImGui::SetNextWindowPos(pos, ImGuiCond_Always, ImVec2(1.0f, 0.0f));
|
||||
ImGui::SetNextWindowBgAlpha(0.35f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 8.0f * get_scale());
|
||||
imgui.begin(
|
||||
std::string("###fps_overlay"),
|
||||
ImGuiWindowFlags_AlwaysAutoResize |
|
||||
@@ -7925,10 +8008,38 @@ void GLCanvas3D::_render_fps_overlay(int fps) const
|
||||
ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoInputs);
|
||||
imgui.text(std::string("FPS: ") + std::to_string(fps));
|
||||
// The subset of those frames that redrew the scene rather than reusing the cached one.
|
||||
imgui.text(std::string("3D: ") + std::to_string(m_render_stats.get_scene_fps()));
|
||||
if (show_fps) {
|
||||
imgui.text(std::string("FPS: ") + std::to_string(fps));
|
||||
ImGui::SameLine();
|
||||
// The subset of those frames that redrew the scene rather than reusing the cached one.
|
||||
imgui.text(std::string("3D: ") + std::to_string(m_render_stats.get_scene_fps()));
|
||||
}
|
||||
if (show_timings && ImGui::BeginTable("frame_sections", 3, ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableSetupColumn("ms");
|
||||
ImGui::TableSetupColumn("CPU");
|
||||
ImGui::TableSetupColumn("GPU");
|
||||
ImGui::TableHeadersRow();
|
||||
double cpu_ms = 0.0;
|
||||
double gpu_ms = 0.0;
|
||||
auto row = [](const char* name, double cpu, double gpu) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::TextUnformatted(name);
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("%.2f", cpu);
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImGui::Text("%.2f", gpu);
|
||||
};
|
||||
for (const FrameProfiler::Section& section : sections) {
|
||||
row(section.name, section.cpu_ms, section.gpu_ms);
|
||||
cpu_ms += section.cpu_ms;
|
||||
gpu_ms += section.gpu_ms;
|
||||
}
|
||||
row("total", cpu_ms, gpu_ms);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
imgui.end();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_fxaa_pass(unsigned int width, unsigned int height)
|
||||
@@ -8278,7 +8389,8 @@ void GLCanvas3D::_render_shadows(const Transform3d& view_matrix, const Transform
|
||||
return;
|
||||
if (!_is_realistic_view_enabled())
|
||||
return;
|
||||
if (!wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS))
|
||||
const EShadowMode mode = _shadow_mode();
|
||||
if (mode == EShadowMode::Off)
|
||||
return;
|
||||
|
||||
// The preview canvas holds no volumes of its own for FFF. Once slicing has run its printed
|
||||
@@ -8293,10 +8405,11 @@ void GLCanvas3D::_render_shadows(const Transform3d& view_matrix, const Transform
|
||||
|
||||
if (OpenGLManager::get_framebuffers_type() == OpenGLManager::EFramebufferType::Arb) {
|
||||
|
||||
// Light direction (same as used in shading and plate shading)
|
||||
// Orbit: the light used for shading, fixed to the camera. Static: a world light, also used for shading.
|
||||
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 dir_to_light = (view_rot.transpose() * light_dir_eye).normalized();
|
||||
const Vec3d dir_to_light = mode == EShadowMode::Static ? STATIC_LIGHT_DIR :
|
||||
Vec3d((view_rot.transpose() * light_dir_eye).normalized());
|
||||
|
||||
// Bounding box of the printable objects (the shadow casters).
|
||||
BoundingBoxf3 obj_bb;
|
||||
@@ -8348,11 +8461,19 @@ void GLCanvas3D::_render_shadows(const Transform3d& view_matrix, const Transform
|
||||
lmin = lmin.cwiseMin(lp);
|
||||
lmax = lmax.cwiseMax(lp);
|
||||
};
|
||||
// The plate area a shadow can reach: the casters' footprint and where their corners land on z = 0.
|
||||
Vec2d reach_min(DBL_MAX, DBL_MAX);
|
||||
Vec2d reach_max(-DBL_MAX, -DBL_MAX);
|
||||
auto reach = [&](const Vec3d& p) {
|
||||
reach_min = reach_min.cwiseMin(Vec2d(p.x(), p.y()));
|
||||
reach_max = reach_max.cwiseMax(Vec2d(p.x(), p.y()));
|
||||
};
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
const Vec3d corner((i & 1) ? obj_bb.max.x() : obj_bb.min.x(),
|
||||
(i & 2) ? obj_bb.max.y() : obj_bb.min.y(),
|
||||
(i & 4) ? obj_bb.max.z() : obj_bb.min.z());
|
||||
enclose(corner);
|
||||
reach(corner);
|
||||
// Where this corner's shadow lands on z = 0, clamped to the plate so a grazing angle
|
||||
// (t -> infinity) stays bounded.
|
||||
if (ray_dir.z() < -1e-6) {
|
||||
@@ -8362,6 +8483,7 @@ void GLCanvas3D::_render_shadows(const Transform3d& view_matrix, const Transform
|
||||
s.y() = std::min(std::max(s.y(), plate_bb.min.y()), plate_bb.max.y());
|
||||
s.z() = 0.0;
|
||||
enclose(s);
|
||||
reach(s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8395,6 +8517,11 @@ void GLCanvas3D::_render_shadows(const Transform3d& view_matrix, const Transform
|
||||
|
||||
// Create / resize the depth texture and FBO
|
||||
const unsigned int size = 2048;
|
||||
|
||||
// Padded by the filter's 2 texel reach, stretched where the light grazes the plate.
|
||||
const double reach_margin = 3.0 * 2.0 * std::max(halfx, halfy) / size / std::max(0.1, std::abs(dir_to_light.z()));
|
||||
m_shadow_plate_bounds = { float(reach_min.x() - reach_margin), float(reach_min.y() - reach_margin),
|
||||
float(reach_max.x() + reach_margin), float(reach_max.y() + reach_margin) };
|
||||
if (m_shadow_map_texture_id == 0) {
|
||||
glsafe(::glGenTextures(1, &m_shadow_map_texture_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_shadow_map_texture_id));
|
||||
@@ -8414,66 +8541,80 @@ void GLCanvas3D::_render_shadows(const Transform3d& view_matrix, const Transform
|
||||
if (m_shadow_map_fbo == 0)
|
||||
glsafe(::glGenFramebuffers(1, &m_shadow_map_fbo));
|
||||
|
||||
// Save OpenGL state that we will modify
|
||||
GLint prev_viewport[4] = { 0, 0, 0, 0 };
|
||||
glsafe(::glGetIntegerv(GL_VIEWPORT, prev_viewport));
|
||||
GLint prev_fbo = 0;
|
||||
glsafe(::glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo));
|
||||
GLboolean prev_color_mask[4] = { GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE };
|
||||
glsafe(::glGetBooleanv(GL_COLOR_WRITEMASK, prev_color_mask));
|
||||
const GLboolean prev_cull = ::glIsEnabled(GL_CULL_FACE);
|
||||
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));
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, m_shadow_map_fbo));
|
||||
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_shadow_map_texture_id, 0));
|
||||
glsafe(::glDrawBuffer(GL_NONE));
|
||||
glsafe(::glReadBuffer(GL_NONE));
|
||||
|
||||
if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(prev_fbo)));
|
||||
m_shadow_map_valid = false;
|
||||
} else {
|
||||
glsafe(::glViewport(0, 0, size, size));
|
||||
glsafe(::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
glsafe(::glDepthFunc(GL_LESS));
|
||||
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
||||
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glPolygonOffset(4.0f, 4.0f));
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
|
||||
if (toolpath_casters)
|
||||
m_gcode_viewer.render_shadow_casters(Transform3d(light_view), Transform3d(light_proj), eye);
|
||||
// Only this branch draws through "flat"; the toolpaths bring their own program.
|
||||
else if (GLShaderProgram* shader = wxGetApp().get_shader("flat"); shader != nullptr) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("projection_matrix", Transform3d(light_proj));
|
||||
for (GLVolume* volume : m_volumes.volumes) {
|
||||
if (volume == nullptr || !volume->is_active || !volume->printable || volume->is_modifier || volume->is_wipe_tower)
|
||||
continue;
|
||||
const Transform3d view_model = Transform3d(light_view) * volume->world_matrix();
|
||||
shader->set_uniform("view_model_matrix", view_model);
|
||||
volume->model.render(shader);
|
||||
}
|
||||
shader->stop_using();
|
||||
}
|
||||
|
||||
// Restore state
|
||||
glsafe(::glDisable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glColorMask(prev_color_mask[0], prev_color_mask[1], prev_color_mask[2], prev_color_mask[3]));
|
||||
if (prev_cull)
|
||||
glsafe(::glEnable(GL_CULL_FACE));
|
||||
else
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
glsafe(::glDepthFunc(prev_depth_func));
|
||||
glsafe(::glDepthMask(prev_depth_mask));
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(prev_fbo)));
|
||||
glsafe(::glViewport(prev_viewport[0], prev_viewport[1], prev_viewport[2], prev_viewport[3]));
|
||||
|
||||
// A static light keeps the map until its frustum or the casters change.
|
||||
size_t cache_key = 0;
|
||||
if (mode == EShadowMode::Static) {
|
||||
cache_key = _shadow_casters_signature(toolpath_casters);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
boost::hash_combine(cache_key, m_shadow_light_vp.matrix().data()[i]);
|
||||
}
|
||||
if (cache_key != 0 && cache_key == m_shadow_map_key)
|
||||
m_shadow_map_valid = true;
|
||||
else {
|
||||
m_shadow_map_key = 0;
|
||||
// Save OpenGL state that we will modify
|
||||
GLint prev_viewport[4] = { 0, 0, 0, 0 };
|
||||
glsafe(::glGetIntegerv(GL_VIEWPORT, prev_viewport));
|
||||
GLint prev_fbo = 0;
|
||||
glsafe(::glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo));
|
||||
GLboolean prev_color_mask[4] = { GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE };
|
||||
glsafe(::glGetBooleanv(GL_COLOR_WRITEMASK, prev_color_mask));
|
||||
const GLboolean prev_cull = ::glIsEnabled(GL_CULL_FACE);
|
||||
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));
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, m_shadow_map_fbo));
|
||||
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_shadow_map_texture_id, 0));
|
||||
glsafe(::glDrawBuffer(GL_NONE));
|
||||
glsafe(::glReadBuffer(GL_NONE));
|
||||
|
||||
if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(prev_fbo)));
|
||||
m_shadow_map_valid = false;
|
||||
} else {
|
||||
glsafe(::glViewport(0, 0, size, size));
|
||||
glsafe(::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
glsafe(::glDepthFunc(GL_LESS));
|
||||
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
||||
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glPolygonOffset(4.0f, 4.0f));
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
|
||||
if (toolpath_casters)
|
||||
m_gcode_viewer.render_shadow_casters(Transform3d(light_view), Transform3d(light_proj), eye);
|
||||
// Only this branch draws through "flat"; the toolpaths bring their own program.
|
||||
else if (GLShaderProgram* shader = wxGetApp().get_shader("flat"); shader != nullptr) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("projection_matrix", Transform3d(light_proj));
|
||||
for (GLVolume* volume : m_volumes.volumes) {
|
||||
if (volume == nullptr || !volume->is_active || !volume->printable || volume->is_modifier || volume->is_wipe_tower)
|
||||
continue;
|
||||
const Transform3d view_model = Transform3d(light_view) * volume->world_matrix();
|
||||
shader->set_uniform("view_model_matrix", view_model);
|
||||
volume->model.render(shader);
|
||||
}
|
||||
shader->stop_using();
|
||||
}
|
||||
|
||||
// Restore state
|
||||
glsafe(::glDisable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glColorMask(prev_color_mask[0], prev_color_mask[1], prev_color_mask[2], prev_color_mask[3]));
|
||||
if (prev_cull)
|
||||
glsafe(::glEnable(GL_CULL_FACE));
|
||||
else
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
glsafe(::glDepthFunc(prev_depth_func));
|
||||
glsafe(::glDepthMask(prev_depth_mask));
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(prev_fbo)));
|
||||
glsafe(::glViewport(prev_viewport[0], prev_viewport[1], prev_viewport[2], prev_viewport[3]));
|
||||
|
||||
m_shadow_map_valid = true;
|
||||
}
|
||||
if (m_shadow_map_valid)
|
||||
m_shadow_map_key = cache_key;
|
||||
}
|
||||
} else {
|
||||
m_shadow_map_valid = false;
|
||||
@@ -8573,6 +8714,7 @@ void GLCanvas3D::_render_shadows(const Transform3d& view_matrix, const Transform
|
||||
plate_shader->set_uniform("shadow_light_vp", m_shadow_light_vp);
|
||||
plate_shader->set_uniform("shadow_intensity", 0.35f);
|
||||
plate_shader->set_uniform("shadow_map_texel", 1.0f / static_cast<float>(m_shadow_map_size));
|
||||
plate_shader->set_uniform("shadow_bounds", m_shadow_plate_bounds);
|
||||
m_plate_shadow_mask.render(plate_shader);
|
||||
plate_shader->stop_using();
|
||||
|
||||
@@ -8697,6 +8839,10 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
}
|
||||
else
|
||||
shader->set_uniform("shadow_intensity", 0.0f);
|
||||
const std::optional<Vec3d> static_light = _static_light_dir_eye();
|
||||
shader->set_uniform("use_static_light", static_light.has_value());
|
||||
if (static_light.has_value())
|
||||
shader->set_uniform("static_light_dir", *static_light);
|
||||
|
||||
const Size& cvn_size = get_canvas_size();
|
||||
{
|
||||
@@ -8789,6 +8935,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0));
|
||||
}
|
||||
shader->set_uniform("use_static_light", false);
|
||||
|
||||
shader->stop_using();
|
||||
}
|
||||
@@ -8938,8 +9085,9 @@ void GLCanvas3D::_render_gcode(int canvas_width, int canvas_height)
|
||||
// again - realistic view with at least one lossy pass on - else the lift would just clip.
|
||||
const AppConfig* cfg = wxGetApp().app_config;
|
||||
const bool lossy_passes = cfg != nullptr && _is_realistic_view_enabled() &&
|
||||
(cfg->get_bool(SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS) || cfg->get_bool(SETTING_OPENGL_PHONG_SSAO));
|
||||
(_shadow_mode() != EShadowMode::Off || cfg->get_bool(SETTING_OPENGL_PHONG_SSAO));
|
||||
m_gcode_viewer.set_tone(lossy_passes ? 1.1f : 1.0f, 1.15f);
|
||||
m_gcode_viewer.set_light_top_dir(_static_light_dir_eye().value_or(Vec3d(-0.4574957, 0.4574957, 0.7624929)));
|
||||
|
||||
m_gcode_viewer.render_scene(canvas_width, canvas_height);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user