mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-19 06:53:02 +00:00
cut the GPU load of moving the mouse over the 3D viewport (#15674)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
This commit is contained in:
co-authored by
Rodrigo Faselli
parent
e0410db22e
commit
213c6569ab
@@ -283,6 +283,12 @@ void AppConfig::set_defaults()
|
||||
set(SETTING_OPENGL_FPS_CAP, std::to_string(fps_cap));
|
||||
}
|
||||
|
||||
if (get(SETTING_OPENGL_SCENE_CACHE).empty())
|
||||
set_bool(SETTING_OPENGL_SCENE_CACHE, true);
|
||||
|
||||
if (get(SETTING_OPENGL_SKIP_IDENTICAL_FRAMES).empty())
|
||||
set_bool(SETTING_OPENGL_SKIP_IDENTICAL_FRAMES, true);
|
||||
|
||||
// The getter already defaults, parses and clamps; write back what it resolves to.
|
||||
set(SETTING_PLUGIN_PAGES_VISIBLE_COUNT, std::to_string(get_plugin_pages_visible_count()));
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@ using namespace nlohmann;
|
||||
#define SETTING_OPENGL_AA_SAMPLES "opengl_antialiasing_samples"
|
||||
#define SETTING_OPENGL_FXAA_ENABLED "opengl_fxaa_enabled"
|
||||
#define SETTING_OPENGL_FPS_CAP "opengl_fps_cap"
|
||||
#define SETTING_OPENGL_SCENE_CACHE "opengl_scene_cache"
|
||||
#define SETTING_OPENGL_SKIP_IDENTICAL_FRAMES "opengl_skip_identical_frames"
|
||||
#define SETTING_OPENGL_SHOW_FPS_OVERLAY "opengl_show_fps_overlay"
|
||||
#define SETTING_OPENGL_REALISTIC_MODE "opengl_realistic_mode"
|
||||
#define SETTING_OPENGL_REALISTIC_PHONG "opengl_realistic_phong"
|
||||
|
||||
@@ -469,6 +469,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/SavePresetDialog.hpp
|
||||
GUI/SceneRaycaster.cpp
|
||||
GUI/SceneRaycaster.hpp
|
||||
GUI/SceneCache.cpp
|
||||
GUI/SceneCache.hpp
|
||||
GUI/PartSkipCommon.hpp
|
||||
GUI/PartSkipDialog.cpp
|
||||
GUI/PartSkipDialog.hpp
|
||||
|
||||
@@ -997,13 +997,17 @@ void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file()
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": finished mapping file " << m_filename;
|
||||
}
|
||||
}
|
||||
void GCodeViewer::SequentialView::render(const bool has_render_path, float legend_height, const libvgcode::Viewer* viewer, uint32_t gcode_id, int canvas_width, int canvas_height, int right_margin, const libvgcode::EViewType& view_type)
|
||||
void GCodeViewer::SequentialView::render_marker(const bool has_render_path, int canvas_width, int canvas_height, const libvgcode::EViewType& view_type)
|
||||
{
|
||||
if (has_render_path && m_show_marker) {
|
||||
if (has_render_path && m_show_marker)
|
||||
// marker.set_world_offset(current_offset);
|
||||
marker.render(canvas_width, canvas_height, view_type);
|
||||
}
|
||||
|
||||
void GCodeViewer::SequentialView::render_overlay(const bool has_render_path, float legend_height, const libvgcode::Viewer* viewer, uint32_t gcode_id, int canvas_width, int canvas_height, int right_margin, const libvgcode::EViewType& view_type)
|
||||
{
|
||||
if (has_render_path && m_show_marker)
|
||||
marker.render_position_window(viewer, canvas_width, canvas_height, view_type);
|
||||
}
|
||||
|
||||
//float bottom = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_height();
|
||||
// BBS
|
||||
@@ -1618,7 +1622,7 @@ void GCodeViewer::reset()
|
||||
}
|
||||
|
||||
//BBS: GUI refactor: add canvas width and height
|
||||
void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin)
|
||||
void GCodeViewer::render_scene(int canvas_width, int canvas_height)
|
||||
{
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
render_shells(canvas_width, canvas_height);
|
||||
@@ -1628,6 +1632,20 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin)
|
||||
|
||||
render_toolpaths();
|
||||
|
||||
auto current = m_viewer.get_view_visible_range();
|
||||
auto endpoints = m_viewer.get_view_full_range();
|
||||
m_sequential_view.m_show_marker = m_sequential_view.m_show_marker || (current.back() != endpoints.back() && !m_no_render_path);
|
||||
const libvgcode::PathVertex& curr_vertex = m_viewer.get_current_vertex();
|
||||
m_sequential_view.marker.set_world_position(libvgcode::convert(curr_vertex.position));
|
||||
m_sequential_view.marker.set_z_offset(m_z_offset + 0.5f);
|
||||
m_sequential_view.render_marker(!m_no_render_path, canvas_width, sequential_view_height(canvas_height), m_viewer.get_view_type());
|
||||
}
|
||||
|
||||
void GCodeViewer::render_overlay(int canvas_width, int canvas_height, int right_margin)
|
||||
{
|
||||
if (m_viewer.get_extrusion_roles().empty())
|
||||
return;
|
||||
|
||||
float legend_height = 0.0f;
|
||||
render_legend(legend_height, canvas_width, canvas_height, right_margin);
|
||||
|
||||
@@ -1636,16 +1654,7 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin)
|
||||
m_user_mode = wxGetApp().get_mode();
|
||||
}
|
||||
|
||||
//BBS fixed bottom_margin for space to render horiz slider
|
||||
int bottom_margin = SLIDER_BOTTOM_MARGIN * GCODE_VIEWER_SLIDER_SCALE;
|
||||
auto current = m_viewer.get_view_visible_range();
|
||||
auto endpoints = m_viewer.get_view_full_range();
|
||||
m_sequential_view.m_show_marker = m_sequential_view.m_show_marker || (current.back() != endpoints.back() && !m_no_render_path);
|
||||
const libvgcode::PathVertex& curr_vertex = m_viewer.get_current_vertex();
|
||||
m_sequential_view.marker.set_world_position(libvgcode::convert(curr_vertex.position));
|
||||
m_sequential_view.marker.set_z_offset(m_z_offset + 0.5f);
|
||||
// BBS fixed buttom margin. m_moves_slider.pos_y
|
||||
m_sequential_view.render(!m_no_render_path, legend_height, &m_viewer, m_viewer.get_current_vertex().gcode_id, canvas_width, canvas_height - bottom_margin * m_scale, right_margin * m_scale, m_viewer.get_view_type());
|
||||
m_sequential_view.render_overlay(!m_no_render_path, legend_height, &m_viewer, m_viewer.get_current_vertex().gcode_id, canvas_width, sequential_view_height(canvas_height), right_margin * m_scale, m_viewer.get_view_type());
|
||||
|
||||
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
||||
if (is_legend_shown()) {
|
||||
@@ -1686,6 +1695,14 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin)
|
||||
render_slider(canvas_width, canvas_height);
|
||||
}
|
||||
|
||||
int GCodeViewer::sequential_view_height(int canvas_height) const
|
||||
{
|
||||
//BBS fixed bottom_margin for space to render horiz slider
|
||||
const int bottom_margin = SLIDER_BOTTOM_MARGIN * GCODE_VIEWER_SLIDER_SCALE;
|
||||
// BBS fixed buttom margin. m_moves_slider.pos_y
|
||||
return canvas_height - bottom_margin * m_scale;
|
||||
}
|
||||
|
||||
#define ENABLE_CALIBRATION_THUMBNAIL_OUTPUT 0
|
||||
#if ENABLE_CALIBRATION_THUMBNAIL_OUTPUT
|
||||
static void debug_calibration_output_thumbnail(const ThumbnailData& thumbnail_data)
|
||||
|
||||
@@ -153,7 +153,10 @@ public:
|
||||
GCodeWindow gcode_window;
|
||||
float m_scale = 1.0;
|
||||
bool m_show_marker = false;
|
||||
void render(const bool has_render_path, float legend_height, const libvgcode::Viewer* viewer, uint32_t gcode_id, int canvas_width, int canvas_height, int right_margin, const libvgcode::EViewType& view_type);
|
||||
// The tool marker at the current move, drawn in 3D.
|
||||
void render_marker(const bool has_render_path, int canvas_width, int canvas_height, const libvgcode::EViewType& view_type);
|
||||
// The marker's position window and the G-code window, both ImGui.
|
||||
void render_overlay(const bool has_render_path, float legend_height, const libvgcode::Viewer* viewer, uint32_t gcode_id, int canvas_width, int canvas_height, int right_margin, const libvgcode::EViewType& view_type);
|
||||
};
|
||||
struct ExtruderFilament
|
||||
{
|
||||
@@ -272,7 +275,10 @@ public:
|
||||
//BBS: add all plates filament statistics
|
||||
void render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show = true) const;
|
||||
//BBS: GUI refactor: add canvas width and height
|
||||
void render(int canvas_width, int canvas_height, int right_margin);
|
||||
// Shells, toolpaths and the sequential marker, drawn in 3D.
|
||||
void render_scene(int canvas_width, int canvas_height);
|
||||
// Legend, sliders, the marker's position window and the G-code window, all ImGui.
|
||||
void render_overlay(int canvas_width, int canvas_height, int right_margin);
|
||||
//BBS
|
||||
// void _render_calibration_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||
// void _render_calibration_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||
@@ -362,6 +368,8 @@ public:
|
||||
private:
|
||||
//BBS: always load shell at preview
|
||||
//void load_shells(const Print& print);
|
||||
// Canvas height minus the room the horizontal slider takes.
|
||||
int sequential_view_height(int canvas_height) const;
|
||||
void render_toolpaths();
|
||||
void render_shells(int canvas_width, int canvas_height);
|
||||
|
||||
|
||||
+339
-160
@@ -69,6 +69,7 @@
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/spin_mutex.h>
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
@@ -370,7 +371,6 @@ void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(GLCanvas3D&
|
||||
|
||||
void GLCanvas3D::LayersEditing::render_overlay(GLCanvas3D& canvas)
|
||||
{
|
||||
render_variable_layer_height_dialog(canvas);
|
||||
render_active_object_annotations(canvas);
|
||||
render_profile(canvas);
|
||||
}
|
||||
@@ -1191,6 +1191,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)
|
||||
#endif // ENABLE_RETINA_GL
|
||||
}
|
||||
m_timer_set_color.Bind(wxEVT_TIMER, &GLCanvas3D::on_set_color_timer, this);
|
||||
m_fps_overlay_timer.Bind(wxEVT_TIMER, &GLCanvas3D::on_fps_overlay_timer, this);
|
||||
load_arrange_settings();
|
||||
|
||||
m_selection.set_volumes(&m_volumes.volumes);
|
||||
@@ -1207,6 +1208,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)
|
||||
GLCanvas3D::~GLCanvas3D()
|
||||
{
|
||||
if (_set_current()) {
|
||||
m_scene_cache.reset();
|
||||
if (m_fxaa_texture_id != 0) {
|
||||
glsafe(::glDeleteTextures(1, &m_fxaa_texture_id));
|
||||
m_fxaa_texture_id = 0;
|
||||
@@ -1354,6 +1356,7 @@ void GLCanvas3D::on_change_color_mode(bool is_dark, bool reinit) {
|
||||
m_gizmos.set_icon_dirty();
|
||||
}
|
||||
}
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
const float GLCanvas3D::get_scale() const
|
||||
@@ -1938,7 +1941,14 @@ bool GLCanvas3D::make_current_for_postinit() {
|
||||
return _set_current();
|
||||
}
|
||||
|
||||
// Redraws the scene and presents it.
|
||||
void GLCanvas3D::render(bool only_init)
|
||||
{
|
||||
m_presented_signature.reset();
|
||||
_render_frame(true, only_init);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_frame(bool scene_dirty, bool only_init)
|
||||
{
|
||||
if (m_in_render) {
|
||||
// if called recursively, return
|
||||
@@ -2041,121 +2051,33 @@ void GLCanvas3D::render(bool only_init)
|
||||
}
|
||||
}
|
||||
|
||||
// draw scene
|
||||
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
||||
// Invalidate the shadow map each frame; only the View3D path below rebuilds it. This keeps
|
||||
// the Preview / Assemble canvases from sampling a stale map with an outdated light matrix.
|
||||
m_shadow_map_valid = false;
|
||||
_render_background();
|
||||
const bool overlay_tick = m_fps_overlay_tick;
|
||||
m_fps_overlay_tick = false;
|
||||
|
||||
//BBS add partplater rendering logic
|
||||
bool only_current = false, only_body = false, no_partplate = false;
|
||||
bool show_grid = true;
|
||||
GLGizmosManager::EType gizmo_type = m_gizmos.get_current_type();
|
||||
if (!m_main_toolbar.is_enabled()) {
|
||||
//only_body = true;
|
||||
only_current = true;
|
||||
// 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);
|
||||
if (!reuse_scene) {
|
||||
_render_scene(camera, cnv_size);
|
||||
if (!overlay_tick)
|
||||
m_render_stats.increment_scene_fps_counter();
|
||||
}
|
||||
else if ((gizmo_type == GLGizmosManager::FdmSupports) || (gizmo_type == GLGizmosManager::Seam) || (gizmo_type == GLGizmosManager::MmSegmentation) || (gizmo_type == GLGizmosManager::FuzzySkin))
|
||||
no_partplate = true;
|
||||
else if (gizmo_type == GLGizmosManager::BrimEars && !camera.is_looking_downward())
|
||||
show_grid = false;
|
||||
if (m_axes_at_bed_center)
|
||||
// Design tab: the plate grid is generated from the plate's front-left corner, so it
|
||||
// floats mid-cell under the modeling-origin triad. Suppress it here; a CAD grid centred
|
||||
// on the origin is rendered in its place (see _render_cad_grid).
|
||||
show_grid = false;
|
||||
|
||||
/* view3D render*/
|
||||
int hover_id = (m_hover_plate_idxs.size() > 0)?m_hover_plate_idxs.front():-1;
|
||||
if (m_canvas_type == ECanvasType::CanvasView3D) {
|
||||
// m_show_bed gates the plate list too: hiding the bed but leaving its grid and outline
|
||||
// floating would read as a rendering fault rather than a deliberate view option.
|
||||
if (!no_partplate && m_show_bed)
|
||||
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), m_show_world_axes);
|
||||
if (!no_partplate && m_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 && m_show_bed && !no_partplate)
|
||||
// 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());
|
||||
|
||||
//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());
|
||||
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
||||
_render_sla_slices();
|
||||
_render_selection();
|
||||
_render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running());
|
||||
_render_wireframe_overlay();
|
||||
}
|
||||
/* preview render */
|
||||
else if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview) {
|
||||
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
||||
_render_sla_slices();
|
||||
_render_selection();
|
||||
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), m_show_world_axes);
|
||||
_render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, true, hover_id);
|
||||
if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview)
|
||||
// BBS: GUI refactor: add canvas size as parameters
|
||||
_render_gcode(cnv_size.get_width(), cnv_size.get_height());
|
||||
}
|
||||
/* assemble render*/
|
||||
else if (m_canvas_type == ECanvasType::CanvasAssembleView) {
|
||||
//BBS: add outline logic
|
||||
//if (m_show_world_axes) {
|
||||
// m_axes.render();
|
||||
//}
|
||||
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
||||
_render_selection();
|
||||
//_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), show_axes);
|
||||
_render_plane();
|
||||
//BBS: add outline logic insteadof selection under assemble view
|
||||
//_render_selection();
|
||||
// BBS: add outline logic
|
||||
_render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running());
|
||||
_render_wireframe_overlay();
|
||||
}
|
||||
|
||||
_render_sequential_clearance();
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
_render_selection_center();
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
|
||||
// sidebar hints need to be rendered before the gizmos because the depth buffer
|
||||
// could be invalidated by the following gizmo render methods
|
||||
_render_selection_sidebar_hints();
|
||||
_render_current_gizmo();
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING_DEBUG
|
||||
if (m_picking_enabled && !m_mouse.dragging && !m_gizmos.is_dragging() && !m_rectangle_selection.is_dragging())
|
||||
m_scene_raycaster.render_hit(camera);
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
|
||||
#if ENABLE_SHOW_CAMERA_TARGET
|
||||
_render_camera_target();
|
||||
#endif // ENABLE_SHOW_CAMERA_TARGET
|
||||
|
||||
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()));
|
||||
|
||||
// Design tab: interactive 2D sketch overlay, drawn over the scene but
|
||||
// beneath the UI overlays (toolbars, labels).
|
||||
#ifdef SLIC3R_CAD
|
||||
if (m_design_sketch_tool != nullptr && m_design_sketch_tool->has_display())
|
||||
m_design_sketch_tool->render(*this);
|
||||
#endif
|
||||
_render_gcode_overlay(cnv_size.get_width(), cnv_size.get_height());
|
||||
|
||||
// draw overlays
|
||||
_render_overlays();
|
||||
|
||||
const int current_fps = m_render_stats.get_fps_and_reset_if_needed();
|
||||
if (_is_fps_overlay_enabled())
|
||||
if (_is_fps_overlay_enabled()) {
|
||||
_render_fps_overlay(current_fps);
|
||||
// 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)
|
||||
m_fps_overlay_timer.StartOnce(1000);
|
||||
}
|
||||
|
||||
if (wxGetApp().plater()->is_render_statistic_dialog_visible()) {
|
||||
ImGui::ShowMetricsWindow();
|
||||
@@ -2241,14 +2163,149 @@ void GLCanvas3D::render(bool only_init)
|
||||
wxGetApp().plater()->get_dailytips()->render();
|
||||
}
|
||||
|
||||
wxGetApp().imgui()->render();
|
||||
ImDrawData* draw_data = wxGetApp().imgui()->end_frame();
|
||||
|
||||
std::optional<size_t> signature;
|
||||
if (_is_frame_skipping_enabled())
|
||||
signature = _overlay_signature(draw_data);
|
||||
|
||||
if (reuse_scene) {
|
||||
// A reused scene under an unchanged overlay is the frame already on screen.
|
||||
if (signature.has_value() && signature == m_presented_signature)
|
||||
return;
|
||||
m_scene_cache.render(m_background);
|
||||
}
|
||||
|
||||
_render_overlay_toolbars();
|
||||
|
||||
wxGetApp().imgui()->render(draw_data);
|
||||
|
||||
// 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_render_stats.increment_fps_counter();
|
||||
if (!overlay_tick)
|
||||
m_render_stats.increment_fps_counter();
|
||||
m_presented_signature = signature;
|
||||
}
|
||||
else
|
||||
m_presented_signature.reset();
|
||||
}
|
||||
|
||||
// Everything drawn into the 3D scene, from the clear to the post processing passes, ending in the
|
||||
// capture an overlay-only frame reuses.
|
||||
void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size)
|
||||
{
|
||||
// Recorded by PartPlate::render_icons() below, when it runs.
|
||||
wxGetApp().plater()->get_partplate_list().clear_hover_tooltip();
|
||||
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
||||
// Invalidate the shadow map each frame; only the View3D path below rebuilds it. This keeps
|
||||
// the Preview / Assemble canvases from sampling a stale map with an outdated light matrix.
|
||||
m_shadow_map_valid = false;
|
||||
_render_background();
|
||||
|
||||
//BBS add partplater rendering logic
|
||||
bool only_current = false, only_body = false, no_partplate = false;
|
||||
bool show_grid = true;
|
||||
GLGizmosManager::EType gizmo_type = m_gizmos.get_current_type();
|
||||
if (!m_main_toolbar.is_enabled()) {
|
||||
//only_body = true;
|
||||
only_current = true;
|
||||
}
|
||||
else if ((gizmo_type == GLGizmosManager::FdmSupports) || (gizmo_type == GLGizmosManager::Seam) || (gizmo_type == GLGizmosManager::MmSegmentation) || (gizmo_type == GLGizmosManager::FuzzySkin))
|
||||
no_partplate = true;
|
||||
else if (gizmo_type == GLGizmosManager::BrimEars && !camera.is_looking_downward())
|
||||
show_grid = false;
|
||||
if (m_axes_at_bed_center)
|
||||
// Design tab: the plate grid is generated from the plate's front-left corner, so it
|
||||
// floats mid-cell under the modeling-origin triad. Suppress it here; a CAD grid centred
|
||||
// on the origin is rendered in its place (see _render_cad_grid).
|
||||
show_grid = false;
|
||||
|
||||
/* view3D render*/
|
||||
int hover_id = (m_hover_plate_idxs.size() > 0)?m_hover_plate_idxs.front():-1;
|
||||
if (m_canvas_type == ECanvasType::CanvasView3D) {
|
||||
// m_show_bed gates the plate list too: hiding the bed but leaving its grid and outline
|
||||
// floating would read as a rendering fault rather than a deliberate view option.
|
||||
if (!no_partplate && m_show_bed)
|
||||
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), m_show_world_axes);
|
||||
if (!no_partplate && m_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 && m_show_bed && !no_partplate)
|
||||
// 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());
|
||||
|
||||
//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());
|
||||
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
||||
_render_sla_slices();
|
||||
_render_selection();
|
||||
_render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running());
|
||||
_render_wireframe_overlay();
|
||||
}
|
||||
/* preview render */
|
||||
else if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview) {
|
||||
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
||||
_render_sla_slices();
|
||||
_render_selection();
|
||||
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), m_show_world_axes);
|
||||
_render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, true, hover_id);
|
||||
// BBS: GUI refactor: add canvas size as parameters
|
||||
_render_gcode(cnv_size.get_width(), cnv_size.get_height());
|
||||
}
|
||||
/* assemble render*/
|
||||
else if (m_canvas_type == ECanvasType::CanvasAssembleView) {
|
||||
//BBS: add outline logic
|
||||
//if (m_show_world_axes) {
|
||||
// m_axes.render();
|
||||
//}
|
||||
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
||||
_render_selection();
|
||||
//_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), show_axes);
|
||||
_render_plane();
|
||||
//BBS: add outline logic insteadof selection under assemble view
|
||||
//_render_selection();
|
||||
// BBS: add outline logic
|
||||
_render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running());
|
||||
_render_wireframe_overlay();
|
||||
}
|
||||
|
||||
_render_sequential_clearance();
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
_render_selection_center();
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
// sidebar hints need to be rendered before the gizmos because the depth buffer
|
||||
// could be invalidated by the following gizmo render methods
|
||||
_render_selection_sidebar_hints();
|
||||
_render_current_gizmo();
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING_DEBUG
|
||||
if (m_picking_enabled && !m_mouse.dragging && !m_gizmos.is_dragging() && !m_rectangle_selection.is_dragging())
|
||||
m_scene_raycaster.render_hit(camera);
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
|
||||
#if ENABLE_SHOW_CAMERA_TARGET
|
||||
_render_camera_target();
|
||||
#endif // ENABLE_SHOW_CAMERA_TARGET
|
||||
|
||||
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()));
|
||||
|
||||
// Design tab: interactive 2D sketch overlay, drawn over the scene but
|
||||
// beneath the UI overlays (toolbars, labels).
|
||||
#ifdef SLIC3R_CAD
|
||||
if (m_design_sketch_tool != nullptr && m_design_sketch_tool->has_display())
|
||||
m_design_sketch_tool->render(*this);
|
||||
#endif
|
||||
|
||||
_capture_scene_cache(camera);
|
||||
}
|
||||
|
||||
void GLCanvas3D::render_thumbnail(ThumbnailData & thumbnail_data,
|
||||
@@ -3226,29 +3283,30 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
||||
if (!m_initialized)
|
||||
return;
|
||||
|
||||
m_dirty |= m_main_toolbar.update_items_state();
|
||||
// Toolbar states, notifications and ImGui's own layout settling only touch the overlay.
|
||||
m_overlay_dirty |= m_main_toolbar.update_items_state();
|
||||
//BBS: GUI refactor: GLToolbar
|
||||
m_dirty |= m_assemble_view_toolbar.update_items_state();
|
||||
m_overlay_dirty |= m_assemble_view_toolbar.update_items_state();
|
||||
// BBS
|
||||
//m_dirty |= wxGetApp().plater()->get_view_toolbar().update_items_state();
|
||||
m_dirty |= wxGetApp().plater()->get_collapse_toolbar().update_items_state();
|
||||
m_overlay_dirty |= wxGetApp().plater()->get_collapse_toolbar().update_items_state();
|
||||
// apply() DRAINS the 3D-mouse queue, so only the canvas actually on screen may call it: a
|
||||
// hidden canvas renders nothing, so the motion it swallowed moves the shared camera without
|
||||
// ever being drawn and the next visible frame jumps several states at once.
|
||||
bool mouse3d_controller_applied = _is_shown_on_screen()
|
||||
&& wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera());
|
||||
m_dirty |= mouse3d_controller_applied;
|
||||
m_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this);
|
||||
m_overlay_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this);
|
||||
auto gizmo = wxGetApp().plater()->get_view3D_canvas3D()->get_gizmos_manager().get_current();
|
||||
if (gizmo != nullptr) m_dirty |= gizmo->update_items_state();
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
// ImGuiWrapper::m_requires_extra_frame may have been set by a render made outside of the OnIdle mechanism
|
||||
bool imgui_requires_extra_frame = wxGetApp().imgui()->requires_extra_frame();
|
||||
m_dirty |= imgui_requires_extra_frame;
|
||||
m_overlay_dirty |= imgui_requires_extra_frame;
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_dirty |= GLTexture::Compressor::has_compressed_texture_to_refresh();
|
||||
|
||||
if (!m_dirty)
|
||||
if (!m_dirty && !m_overlay_dirty)
|
||||
return;
|
||||
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
@@ -3273,19 +3331,24 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
||||
m_last_frame_start_time = now;
|
||||
}
|
||||
|
||||
_refresh_if_shown_on_screen();
|
||||
// Read and cleared before the render; a request made during it is left for the next frame.
|
||||
const bool scene_dirty = m_dirty;
|
||||
m_dirty = false;
|
||||
m_overlay_dirty = false;
|
||||
_refresh_if_shown_on_screen(scene_dirty);
|
||||
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (m_extra_frame_requested || mouse3d_controller_applied || imgui_requires_extra_frame || wxGetApp().imgui()->requires_extra_frame()) {
|
||||
#else
|
||||
if (m_extra_frame_requested || mouse3d_controller_applied) {
|
||||
m_dirty = true;
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_extra_frame_requested = false;
|
||||
evt.RequestMore();
|
||||
}
|
||||
else
|
||||
m_dirty = false;
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
else if (imgui_requires_extra_frame || wxGetApp().imgui()->requires_extra_frame()) {
|
||||
// ImGui settling a window or fading a tooltip.
|
||||
m_overlay_dirty = true;
|
||||
evt.RequestMore();
|
||||
}
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
}
|
||||
|
||||
void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||
@@ -4124,6 +4187,12 @@ void GLCanvas3D::on_set_color_timer(wxTimerEvent& evt)
|
||||
m_timer_set_color.Stop();
|
||||
}
|
||||
|
||||
void GLCanvas3D::on_fps_overlay_timer(wxTimerEvent& evt)
|
||||
{
|
||||
m_fps_overlay_tick = true;
|
||||
_set_overlay_as_dirty();
|
||||
wxWakeUpIdle();
|
||||
}
|
||||
|
||||
void GLCanvas3D::schedule_extra_frame(int milliseconds)
|
||||
{
|
||||
@@ -4301,11 +4370,18 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
|
||||
mouse_up_cleanup();
|
||||
|
||||
render();
|
||||
// Hovering an ImGui window only changes the overlay.
|
||||
const bool overlay_only = evt.Moving() && !m_mouse.dragging;
|
||||
// ImGui takes a press or a release only inside a frame. Motion is rendered from on_idle().
|
||||
if (evt.ButtonDown() || evt.ButtonUp() || evt.ButtonDClick())
|
||||
_render_frame(true);
|
||||
#ifdef SLIC3R_DEBUG_MOUSE_EVENTS
|
||||
printf((format_mouse_event_debug_message(evt) + " - Consumed by ImGUI\n").c_str());
|
||||
#endif /* SLIC3R_DEBUG_MOUSE_EVENTS */
|
||||
m_dirty = true;
|
||||
if (overlay_only)
|
||||
_set_overlay_as_dirty();
|
||||
else
|
||||
m_dirty = true;
|
||||
// do not return if dragging or tooltip not empty to allow for tooltip update
|
||||
// also, do not return if the mouse is moving and also is inside MM gizmo to allow update seed fill selection
|
||||
if (!m_mouse.dragging && m_tooltip.is_empty() && (m_gizmos.get_current_type() != GLGizmosManager::MmSegmentation || !evt.Moving()))
|
||||
@@ -4448,6 +4524,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
wxGetApp().obj_list()->selection_changed();
|
||||
}
|
||||
|
||||
// A gizmo that acts on a click or a drag may not request a frame itself.
|
||||
if (!evt.Moving())
|
||||
m_dirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4939,7 +5018,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
if (m_selection.is_empty())
|
||||
m_gizmos.reset_all_states();
|
||||
|
||||
m_dirty = true;
|
||||
_set_overlay_as_dirty();
|
||||
}
|
||||
else
|
||||
evt.Skip();
|
||||
@@ -4989,6 +5068,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
|
||||
void GLCanvas3D::on_paint(wxPaintEvent& evt)
|
||||
{
|
||||
m_presented_signature.reset();
|
||||
if (m_initialized) {
|
||||
#ifdef __WXMSW__
|
||||
// Idle events are not dispatched during the Windows resize modal loop,
|
||||
@@ -5542,7 +5622,11 @@ void GLCanvas3D::update_gizmos_on_off_state()
|
||||
|
||||
void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool focus_on)
|
||||
{
|
||||
m_sidebar_field = focus_on ? opt_key : "";
|
||||
const std::string field = focus_on ? opt_key : "";
|
||||
// The gizmo panels report this on every build.
|
||||
if (m_sidebar_field == field)
|
||||
return;
|
||||
m_sidebar_field = field;
|
||||
|
||||
//BBS: this event was sent from gizmo now, no need to clear gizmo
|
||||
//if (!m_sidebar_field.empty())
|
||||
@@ -6640,6 +6724,9 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const
|
||||
//if (thumbnail_params.transparent_background)
|
||||
// glsafe(::glClearColor(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("render_thumbnail: finished");
|
||||
|
||||
// Puts the canvas viewport back in place of the thumbnail one set above.
|
||||
wxGetApp().plater()->get_camera().apply_viewport();
|
||||
}
|
||||
|
||||
void GLCanvas3D::render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params,
|
||||
@@ -6890,9 +6977,6 @@ void GLCanvas3D::render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned
|
||||
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT
|
||||
debug_output_thumbnail(thumbnail_data);
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT
|
||||
|
||||
// restore the default framebuffer size to avoid flickering on the 3D scene
|
||||
//wxGetApp().plater()->get_camera().apply_viewport();
|
||||
}
|
||||
|
||||
//BBS: GUI refractor
|
||||
@@ -7441,15 +7525,15 @@ void GLCanvas3D::_update_camera_zoom(double zoom)
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::_refresh_if_shown_on_screen()
|
||||
void GLCanvas3D::_refresh_if_shown_on_screen(bool scene_dirty)
|
||||
{
|
||||
if (_is_shown_on_screen()) {
|
||||
const Size& cnv_size = get_canvas_size();
|
||||
_resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height());
|
||||
|
||||
// Because of performance problems on macOS, where PaintEvents are not delivered
|
||||
// frequently enough, we call render() here directly when we can.
|
||||
render();
|
||||
// frequently enough, we render here directly when we can.
|
||||
_render_frame(scene_dirty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7841,6 +7925,36 @@ bool GLCanvas3D::_is_fps_overlay_enabled() const
|
||||
return wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_SHOW_FPS_OVERLAY);
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_is_scene_cache_enabled() const
|
||||
{
|
||||
return wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_SCENE_CACHE);
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_is_scene_cacheable() const
|
||||
{
|
||||
if (!_is_scene_cache_enabled())
|
||||
return false;
|
||||
|
||||
#ifdef SLIC3R_CAD
|
||||
// The Design tab draws its sketch overlay, ImGui included, inside the scene.
|
||||
if (m_design_sketch_tool != nullptr && m_design_sketch_tool->has_display())
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// The scene follows the cursor during a drag, under a gizmo that draws at the cursor, and while
|
||||
// the cursor is on the layer height bar, where the object shader draws a band at its height.
|
||||
const GLGizmoBase* gizmo = m_gizmos.get_current();
|
||||
const bool cursor_on_layers_bar = is_layers_editing_enabled() &&
|
||||
m_layers_editing.bar_rect_contains(*this, (float)m_mouse.position.x(), (float)m_mouse.position.y());
|
||||
return !m_mouse.dragging && !m_gizmos.is_dragging() && !m_rectangle_selection.is_dragging() &&
|
||||
(gizmo == nullptr || !gizmo->render_follows_cursor()) && !cursor_on_layers_bar;
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_is_frame_skipping_enabled() const
|
||||
{
|
||||
return wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_SKIP_IDENTICAL_FRAMES);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_fps_overlay(int fps) const
|
||||
{
|
||||
if (fps < 0)
|
||||
@@ -7861,6 +7975,8 @@ void GLCanvas3D::_render_fps_overlay(int fps) const
|
||||
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()));
|
||||
imgui.end();
|
||||
}
|
||||
|
||||
@@ -7873,23 +7989,7 @@ void GLCanvas3D::_render_fxaa_pass(unsigned int width, unsigned int height)
|
||||
if (shader == nullptr)
|
||||
return;
|
||||
|
||||
if (m_fxaa_texture_id == 0) {
|
||||
glsafe(::glGenTextures(1, &m_fxaa_texture_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_fxaa_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));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
}
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_fxaa_texture_id));
|
||||
if (m_fxaa_texture_size[0] != width || m_fxaa_texture_size[1] != height) {
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
|
||||
m_fxaa_texture_size = { width, height };
|
||||
}
|
||||
|
||||
glsafe(::glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height));
|
||||
GLTexture::copy_from_framebuffer(m_fxaa_texture_id, m_fxaa_texture_size, width, height, GL_LINEAR);
|
||||
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
@@ -8065,6 +8165,51 @@ void GLCanvas3D::_render_ssao_pass(unsigned int width, unsigned int height)
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
}
|
||||
|
||||
SceneCache::Key GLCanvas3D::_scene_cache_key(const Camera& camera) const
|
||||
{
|
||||
SceneCache::Key key;
|
||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||
key.size = { { (unsigned int)viewport[2], (unsigned int)viewport[3] } };
|
||||
key.view_matrix = camera.get_view_matrix();
|
||||
key.projection_matrix = camera.get_projection_matrix();
|
||||
// Hover reaches the scene only through the sinking contour a hovered volume draws over
|
||||
// itself (GLVolumeCollection::render()), the plate icons (PartPlate::render_icons()) and the
|
||||
// open gizmo's grabbers.
|
||||
for (size_t i = 0; i < m_volumes.volumes.size(); ++i) {
|
||||
const GLVolume& volume = *m_volumes.volumes[i];
|
||||
if (volume.hover != GLVolume::HS_None && volume.is_sinking() && !volume.is_below_printbed())
|
||||
key.sinking_hover_volume_idxs.emplace_back((int)i);
|
||||
}
|
||||
for (int id : m_hover_plate_idxs) {
|
||||
if (id % PartPlate::GRABBER_COUNT != 0)
|
||||
key.hover_plate_icon_idxs.emplace_back(id);
|
||||
}
|
||||
const GLGizmoBase* gizmo = m_gizmos.get_current();
|
||||
key.gizmo_hover_id = gizmo != nullptr ? gizmo->get_hover_id() : -1;
|
||||
key.render_preview = m_render_preview;
|
||||
return key;
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_can_reuse_cached_scene(const Camera& camera) const
|
||||
{
|
||||
return _is_scene_cacheable() && m_scene_cache.matches(_scene_cache_key(camera));
|
||||
}
|
||||
|
||||
void GLCanvas3D::_capture_scene_cache(const Camera& camera)
|
||||
{
|
||||
if (!_is_scene_cache_enabled()) {
|
||||
m_scene_cache.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_is_scene_cacheable()) {
|
||||
m_scene_cache.invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
m_scene_cache.capture(_scene_cache_key(camera));
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_background()
|
||||
{
|
||||
bool use_error_color = false;
|
||||
@@ -8792,7 +8937,12 @@ void GLCanvas3D::_render_wireframe_overlay()
|
||||
//BBS: GUI refactor: add canvas size as parameters
|
||||
void GLCanvas3D::_render_gcode(int canvas_width, int canvas_height)
|
||||
{
|
||||
m_gcode_viewer.render(canvas_width, canvas_height, SLIDER_RIGHT_MARGIN * GCODE_VIEWER_SLIDER_SCALE);
|
||||
m_gcode_viewer.render_scene(canvas_width, canvas_height);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_gcode_overlay(int canvas_width, int canvas_height)
|
||||
{
|
||||
m_gcode_viewer.render_overlay(canvas_width, canvas_height, SLIDER_RIGHT_MARGIN * GCODE_VIEWER_SLIDER_SCALE);
|
||||
IMSlider *layers_slider = m_gcode_viewer.get_layers_slider();
|
||||
IMSlider *moves_slider = m_gcode_viewer.get_moves_slider();
|
||||
|
||||
@@ -8924,20 +9074,14 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale()
|
||||
wxGetApp().set_auto_toolbar_icon_scale(new_scale);
|
||||
}
|
||||
|
||||
// The ImGui half of the overlay, drawn by ImGui at the end of the frame.
|
||||
void GLCanvas3D::_render_overlays()
|
||||
{
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
|
||||
_check_and_update_toolbar_icon_scale();
|
||||
|
||||
_render_assemble_control();
|
||||
_render_assemble_info();
|
||||
|
||||
_render_separator_toolbar_right();
|
||||
_render_separator_toolbar_left();
|
||||
_render_main_toolbar();
|
||||
_render_collapse_toolbar();
|
||||
_render_assemble_view_toolbar();
|
||||
//BBS: GUI refactor: GLToolbar
|
||||
_render_imgui_select_plate_toolbar();
|
||||
_render_return_toolbar();
|
||||
@@ -8945,12 +9089,13 @@ void GLCanvas3D::_render_overlays()
|
||||
//_render_view_toolbar();
|
||||
_render_paint_toolbar();
|
||||
|
||||
//BBS: GUI refactor: GLToolbar
|
||||
//move gizmos behind of main
|
||||
_render_gizmos_overlay();
|
||||
// The options window of a pressed toolbar item (arrange).
|
||||
m_main_toolbar.render_item_windows(*this);
|
||||
|
||||
m_gizmos.render_overlay_input_window();
|
||||
|
||||
if (m_layers_editing.last_object_id >= 0 && m_layers_editing.object_max_z() > 0.0f)
|
||||
m_layers_editing.render_overlay(*this);
|
||||
m_layers_editing.render_variable_layer_height_dialog(*this);
|
||||
|
||||
auto curr_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate();
|
||||
auto curr_print_seq = curr_plate->get_real_print_seq();
|
||||
@@ -8977,6 +9122,40 @@ void GLCanvas3D::_render_overlays()
|
||||
_render_3d_navigator();
|
||||
|
||||
_render_canvas_toolbar();
|
||||
|
||||
// Recorded by the scene pass, which a reused frame skips.
|
||||
wxGetApp().plater()->get_partplate_list().render_hover_tooltip();
|
||||
}
|
||||
|
||||
// The GL half of the overlay.
|
||||
void GLCanvas3D::_render_overlay_toolbars()
|
||||
{
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
|
||||
_render_separator_toolbar_right();
|
||||
_render_separator_toolbar_left();
|
||||
_render_main_toolbar();
|
||||
_render_collapse_toolbar();
|
||||
_render_assemble_view_toolbar();
|
||||
//BBS: GUI refactor: GLToolbar
|
||||
//move gizmos behind of main
|
||||
_render_gizmos_overlay();
|
||||
|
||||
if (m_layers_editing.last_object_id >= 0 && m_layers_editing.object_max_z() > 0.0f)
|
||||
m_layers_editing.render_overlay(*this);
|
||||
}
|
||||
|
||||
size_t GLCanvas3D::_overlay_signature(const ImDrawData* draw_data) const
|
||||
{
|
||||
// The recorded ImGui geometry plus the state of the toolbars and the gizmo bar, which draw
|
||||
// outside ImGui.
|
||||
size_t hash = ImGuiWrapper::draw_data_signature(draw_data);
|
||||
for (size_t state_hash : { m_main_toolbar.get_state_hash(), m_separator_toolbar.get_state_hash(),
|
||||
m_assemble_view_toolbar.get_state_hash(),
|
||||
wxGetApp().plater()->get_collapse_toolbar().get_state_hash(),
|
||||
m_gizmos.get_overlay_state_hash() })
|
||||
boost::hash_combine(hash, state_hash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_style_editor()
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include "GLToolbar.hpp"
|
||||
#include "Event.hpp"
|
||||
@@ -17,6 +18,7 @@
|
||||
#include "GCodeViewer.hpp"
|
||||
#include "Camera.hpp"
|
||||
#include "SceneRaycaster.hpp"
|
||||
#include "SceneCache.hpp"
|
||||
#include "IMToolbar.hpp"
|
||||
#include "slic3r/GUI/3DBed.hpp"
|
||||
#include "libslic3r/Slicing.hpp"
|
||||
@@ -33,6 +35,7 @@ class wxTimerEvent;
|
||||
class wxPaintEvent;
|
||||
class wxGLCanvas;
|
||||
class wxGLContext;
|
||||
struct ImDrawData;
|
||||
|
||||
// Support for Retina OpenGL on Mac OS.
|
||||
// wxGTK3 seems to simulate OSX behavior in regard to HiDPI scaling support, enable it as well.
|
||||
@@ -402,16 +405,23 @@ class GLCanvas3D
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_measuring_start;
|
||||
int m_fps_out = -1;
|
||||
int m_fps_running = 0;
|
||||
// Frames that redrew the 3D scene rather than reusing the cached one.
|
||||
int m_scene_fps_out = 0;
|
||||
int m_scene_fps_running = 0;
|
||||
public:
|
||||
void increment_fps_counter() { ++m_fps_running; }
|
||||
void increment_scene_fps_counter() { ++m_scene_fps_running; }
|
||||
int get_fps() { return m_fps_out; }
|
||||
int get_scene_fps() const { return m_scene_fps_out; }
|
||||
int get_fps_and_reset_if_needed() {
|
||||
auto cur_time = std::chrono::high_resolution_clock::now();
|
||||
int elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(cur_time-m_measuring_start).count();
|
||||
if (elapsed_ms > 1000 || m_fps_out == -1) {
|
||||
m_measuring_start = cur_time;
|
||||
m_fps_out = int (1000. * m_fps_running / elapsed_ms);
|
||||
m_scene_fps_out = int (1000. * m_scene_fps_running / elapsed_ms);
|
||||
m_fps_running = 0;
|
||||
m_scene_fps_running = 0;
|
||||
}
|
||||
return m_fps_out;
|
||||
}
|
||||
@@ -533,6 +543,10 @@ private:
|
||||
bool m_in_render;
|
||||
wxTimer m_timer;
|
||||
wxTimer m_timer_set_color;
|
||||
// Armed by each frame that draws the FPS overlay; its tick requests an overlay-only frame.
|
||||
wxTimer m_fps_overlay_timer;
|
||||
// True during the frame the timer requested, which is not counted.
|
||||
bool m_fps_overlay_tick{ false };
|
||||
LayersEditing m_layers_editing;
|
||||
Mouse m_mouse;
|
||||
GLGizmosManager m_gizmos;
|
||||
@@ -600,6 +614,8 @@ private:
|
||||
|
||||
// Screen is only refreshed from the OnIdle handler if it is dirty.
|
||||
bool m_dirty;
|
||||
// A frame is needed, and only for the overlay.
|
||||
bool m_overlay_dirty{ false };
|
||||
bool m_initialized;
|
||||
//BBS: add flag to controll rendering
|
||||
bool m_render_preview{ true };
|
||||
@@ -755,6 +771,11 @@ public:
|
||||
unsigned int m_ssao_color_texture_id{ 0 };
|
||||
unsigned int m_ssao_depth_texture_id{ 0 };
|
||||
std::array<unsigned int, 2> m_ssao_texture_size{ { 0, 0 } };
|
||||
// The last scene pass, for frames that only rebuild the overlay.
|
||||
SceneCache m_scene_cache;
|
||||
// Signature of the overlay on screen; empty after render(), a paint request or a frame drawn but
|
||||
// not shown, so the next frame is presented regardless.
|
||||
std::optional<size_t> m_presented_signature;
|
||||
GLModel m_plate_shadow_mask;
|
||||
std::string m_plate_shadow_mask_key;
|
||||
// Depth-based shadow map used to cast object shadows onto other objects and themselves.
|
||||
@@ -1077,6 +1098,7 @@ public:
|
||||
void on_timer(wxTimerEvent& evt);
|
||||
void on_render_timer(wxTimerEvent& evt);
|
||||
void on_set_color_timer(wxTimerEvent& evt);
|
||||
void on_fps_overlay_timer(wxTimerEvent& evt);
|
||||
void on_mouse(wxMouseEvent& evt);
|
||||
void on_gesture(wxGestureEvent& evt);
|
||||
void on_paint(wxPaintEvent& evt);
|
||||
@@ -1274,7 +1296,7 @@ private:
|
||||
void _zoom_to_box(const BoundingBoxf3& box, double margin_factor = DefaultCameraZoomToBoxMarginFactor);
|
||||
void _update_camera_zoom(double zoom);
|
||||
|
||||
void _refresh_if_shown_on_screen();
|
||||
void _refresh_if_shown_on_screen(bool scene_dirty = true);
|
||||
|
||||
void _picking_pass();
|
||||
void _rectangular_selection_picking_pass();
|
||||
@@ -1282,9 +1304,21 @@ private:
|
||||
bool _is_ssao_enabled() const;
|
||||
int _get_effective_fps_cap() const;
|
||||
bool _is_fps_overlay_enabled() const;
|
||||
bool _is_scene_cache_enabled() const;
|
||||
bool _is_scene_cacheable() const;
|
||||
bool _is_frame_skipping_enabled() const;
|
||||
void _render_fps_overlay(int fps) const;
|
||||
void _render_fxaa_pass(unsigned int width, unsigned int height);
|
||||
void _render_ssao_pass(unsigned int width, unsigned int height);
|
||||
// scene_dirty is false only for a frame that its requester knows to be overlay-only.
|
||||
void _render_frame(bool scene_dirty, bool only_init = false);
|
||||
void _render_scene(const Camera& camera, const Size& cnv_size);
|
||||
// Request a frame that only rebuilds the overlay.
|
||||
void _set_overlay_as_dirty() { m_overlay_dirty = true; }
|
||||
// These read the hover state _picking_pass() sets.
|
||||
SceneCache::Key _scene_cache_key(const Camera& camera) const;
|
||||
bool _can_reuse_cached_scene(const Camera& camera) const;
|
||||
void _capture_scene_cache(const Camera& camera);
|
||||
void _render_background();
|
||||
void _render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes);
|
||||
// Build the light-space depth shadow map (consumed by gouraud/phong for object & self shadows)
|
||||
@@ -1301,6 +1335,7 @@ private:
|
||||
void _render_wireframe_overlay();
|
||||
//BBS: GUI refactor: add canvas size as parameters
|
||||
void _render_gcode(int canvas_width, int canvas_height);
|
||||
void _render_gcode_overlay(int canvas_width, int canvas_height);
|
||||
//BBS: render a plane for assemble
|
||||
void _render_plane() const;
|
||||
void _render_selection();
|
||||
@@ -1310,6 +1345,8 @@ private:
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
void _check_and_update_toolbar_icon_scale();
|
||||
void _render_overlays();
|
||||
void _render_overlay_toolbars();
|
||||
size_t _overlay_signature(const ImDrawData* draw_data) const;
|
||||
void _render_style_editor();
|
||||
void _render_volumes_for_picking(const Camera& camera) const;
|
||||
void _render_current_gizmo() const;
|
||||
|
||||
@@ -707,6 +707,29 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right,
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
|
||||
void GLTexture::copy_from_framebuffer(unsigned int& tex_id, std::array<unsigned int, 2>& tex_size, unsigned int width, unsigned int height, int filter)
|
||||
{
|
||||
if (tex_id == 0) {
|
||||
glsafe(::glGenTextures(1, &tex_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter));
|
||||
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));
|
||||
}
|
||||
else
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id));
|
||||
|
||||
if (tex_size[0] != width || tex_size[1] != height) {
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
|
||||
tex_size = { width, height };
|
||||
}
|
||||
|
||||
// Copying from the default framebuffer resolves its multisampling.
|
||||
glsafe(::glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
}
|
||||
|
||||
static bool to_squared_power_of_two(const std::string& filename, int max_size_px, int& w, int& h)
|
||||
{
|
||||
auto is_power_of_two = [](int v) { return v != 0 && (v & (v - 1)) == 0; };
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <thread>
|
||||
|
||||
#include <wx/colour.h>
|
||||
@@ -132,6 +133,9 @@ namespace GUI {
|
||||
|
||||
static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top);
|
||||
static void render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const Quad_UVs& uvs);
|
||||
// Copies the bound read framebuffer into an RGBA texture, creating it on first use and
|
||||
// reallocating it when the size changes.
|
||||
static void copy_from_framebuffer(unsigned int& tex_id, std::array<unsigned int, 2>& tex_size, unsigned int width, unsigned int height, int filter);
|
||||
|
||||
private:
|
||||
bool load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy);
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
#include <wx/event.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/dcmemory.h>
|
||||
@@ -199,7 +201,10 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b
|
||||
};
|
||||
|
||||
GLTexture::render_sub_texture(tex_id, left, right, bottom, top, uvs(tex_width, tex_height, icon_size));
|
||||
}
|
||||
|
||||
void GLToolbarItem::render_window(float left, float right, float bottom, float top) const
|
||||
{
|
||||
if (is_pressed())
|
||||
{
|
||||
if ((m_last_action_type == Left) && m_data.left.can_render())
|
||||
@@ -215,13 +220,6 @@ void GLToolbarItem::render_image(unsigned int tex_id, float left, float right, f
|
||||
//GLTexture::Quad_UVs image_uvs = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f } };
|
||||
|
||||
GLTexture::render_sub_texture(tex_id, left, right, bottom, top, image_uvs);
|
||||
|
||||
if (is_pressed()) {
|
||||
if ((m_last_action_type == Left) && m_data.left.can_render())
|
||||
m_data.left.render_callback(left, right, bottom, top);
|
||||
else if ((m_last_action_type == Right) && m_data.right.can_render())
|
||||
m_data.right.render_callback(left, right, bottom, top);
|
||||
}
|
||||
}
|
||||
|
||||
BackgroundTexture::Metadata::Metadata()
|
||||
@@ -544,11 +542,35 @@ void GLToolbar::render(const GLCanvas3D& parent,GLToolbarItem::EType type)
|
||||
switch (m_layout.type)
|
||||
{
|
||||
default:
|
||||
case Layout::Horizontal: { render_horizontal(parent,type); break; }
|
||||
case Layout::Vertical: { render_vertical(parent); break; }
|
||||
case Layout::Horizontal: { render_horizontal(parent, type, true); break; }
|
||||
case Layout::Vertical: { render_vertical(parent, true); break; }
|
||||
}
|
||||
}
|
||||
|
||||
void GLToolbar::render_item_windows(const GLCanvas3D& parent)
|
||||
{
|
||||
if (!m_enabled || m_items.empty())
|
||||
return;
|
||||
|
||||
switch (m_layout.type)
|
||||
{
|
||||
default:
|
||||
case Layout::Horizontal: { render_horizontal(parent, GLToolbarItem::Action, false); break; }
|
||||
case Layout::Vertical: { render_vertical(parent, false); break; }
|
||||
}
|
||||
}
|
||||
|
||||
size_t GLToolbar::get_state_hash() const
|
||||
{
|
||||
size_t hash = 0;
|
||||
boost::hash_combine(hash, m_enabled);
|
||||
for (const GLToolbarItem* item : m_items) {
|
||||
boost::hash_combine(hash, (int)item->get_state());
|
||||
boost::hash_combine(hash, item->is_visible());
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
|
||||
{
|
||||
if (!m_enabled)
|
||||
@@ -1354,7 +1376,7 @@ void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighte
|
||||
}
|
||||
}
|
||||
|
||||
void GLToolbar::render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType type)
|
||||
void GLToolbar::render_horizontal(const GLCanvas3D& parent, GLToolbarItem::EType type, bool draw_icons)
|
||||
{
|
||||
const Size cnv_size = parent.get_canvas_size();
|
||||
const float cnv_w = (float)cnv_size.get_width();
|
||||
@@ -1385,7 +1407,8 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType
|
||||
right = left + width * 0.5;
|
||||
const float bottom = top - height;
|
||||
|
||||
render_background(left, top, right, bottom, border_w, border_h);
|
||||
if (draw_icons)
|
||||
render_background(left, top, right, bottom, border_w, border_h);
|
||||
|
||||
left += border_w;
|
||||
top -= border_h;
|
||||
@@ -1400,7 +1423,9 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType
|
||||
else {
|
||||
//BBS GUI refactor
|
||||
item->render_left_pos = left;
|
||||
if (!item->is_action_with_text_image()) {
|
||||
if (!draw_icons)
|
||||
item->render_window(left, left + icons_size_x, top - icons_size_y, top);
|
||||
else if (!item->is_action_with_text_image()) {
|
||||
unsigned int tex_id = m_icons_texture.get_id();
|
||||
int tex_width = m_icons_texture.get_width();
|
||||
int tex_height = m_icons_texture.get_height();
|
||||
@@ -1412,7 +1437,8 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType
|
||||
if (item->is_action_with_text())
|
||||
{
|
||||
float scaled_text_size = item->get_extra_size_ratio() * icons_size_x;
|
||||
item->render_text(left + icons_size_x, left + icons_size_x + scaled_text_size, top - icons_size_y, top);
|
||||
if (draw_icons)
|
||||
item->render_text(left + icons_size_x, left + icons_size_x + scaled_text_size, top - icons_size_y, top);
|
||||
left += scaled_text_size;
|
||||
}
|
||||
left += icon_stride;
|
||||
@@ -1420,7 +1446,7 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType
|
||||
}
|
||||
}
|
||||
|
||||
void GLToolbar::render_vertical(const GLCanvas3D& parent)
|
||||
void GLToolbar::render_vertical(const GLCanvas3D& parent, bool draw_icons)
|
||||
{
|
||||
const Size cnv_size = parent.get_canvas_size();
|
||||
const float cnv_w = (float)cnv_size.get_width();
|
||||
@@ -1449,7 +1475,8 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent)
|
||||
const float right = left + width;
|
||||
const float bottom = top - height;
|
||||
|
||||
render_background(left, top, right, bottom, border_w, border_h);
|
||||
if (draw_icons)
|
||||
render_background(left, top, right, bottom, border_w, border_h);
|
||||
|
||||
left += border_w;
|
||||
top -= border_h;
|
||||
@@ -1462,6 +1489,11 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent)
|
||||
if (item->is_separator())
|
||||
top -= separator_stride;
|
||||
else {
|
||||
if (!draw_icons) {
|
||||
item->render_window(left, left + icons_size_x, top - icons_size_y, top);
|
||||
top -= icon_stride;
|
||||
continue;
|
||||
}
|
||||
unsigned int tex_id;
|
||||
int tex_width, tex_height;
|
||||
if (item->is_action_with_text_image()) {
|
||||
|
||||
@@ -231,6 +231,8 @@ public:
|
||||
int generate_image_texture();
|
||||
|
||||
void render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const;
|
||||
// The ImGui window a pressed item shows, given the icon's rectangle.
|
||||
void render_window(float left, float right, float bottom, float top) const;
|
||||
void render_image(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const;
|
||||
private:
|
||||
void set_visible(bool visible) { m_data.visible = visible; }
|
||||
@@ -410,6 +412,10 @@ public:
|
||||
bool update_items_state();
|
||||
|
||||
void render(const GLCanvas3D& parent,GLToolbarItem::EType type = GLToolbarItem::Action);
|
||||
// The ImGui windows of pressed items, built with the same layout as render().
|
||||
void render_item_windows(const GLCanvas3D& parent);
|
||||
// Hash of the state render() draws from: enabled, and each item's state and visibility.
|
||||
size_t get_state_hash() const;
|
||||
void render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighted_item);
|
||||
|
||||
bool on_mouse(wxMouseEvent& evt, GLCanvas3D& parent);
|
||||
@@ -438,8 +444,8 @@ private:
|
||||
int contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const;
|
||||
|
||||
void render_background(float left, float top, float right, float bottom, float border_w, float border_h) const;
|
||||
void render_horizontal(const GLCanvas3D &parent, GLToolbarItem::EType type);
|
||||
void render_vertical(const GLCanvas3D& parent);
|
||||
void render_horizontal(const GLCanvas3D &parent, GLToolbarItem::EType type, bool draw_icons);
|
||||
void render_vertical(const GLCanvas3D& parent, bool draw_icons);
|
||||
|
||||
bool generate_icons_texture();
|
||||
|
||||
|
||||
+12
-10
@@ -8467,12 +8467,14 @@ void GUI_App::open_exportpresetbundledialog(size_t open_on_tab, const std::strin
|
||||
|
||||
void GUI_App::open_preferences(size_t open_on_tab, const std::string& highlight_option)
|
||||
{
|
||||
static constexpr const char* opengl_fxaa_setting_key = "opengl_fxaa_enabled";
|
||||
static constexpr const char* opengl_fps_cap_setting_key = "opengl_fps_cap";
|
||||
static constexpr const char* opengl_show_fps_overlay_setting_key = "opengl_show_fps_overlay";
|
||||
const std::string previous_opengl_fxaa = app_config->get(opengl_fxaa_setting_key);
|
||||
const std::string previous_opengl_fps_cap = app_config->get(opengl_fps_cap_setting_key);
|
||||
const std::string previous_opengl_show_fps_overlay = app_config->get(opengl_show_fps_overlay_setting_key);
|
||||
// Render settings the canvas reads every frame; a change needs one redraw to show.
|
||||
static constexpr const char* opengl_render_setting_keys[] = {
|
||||
SETTING_OPENGL_FXAA_ENABLED, SETTING_OPENGL_FPS_CAP, SETTING_OPENGL_SHOW_FPS_OVERLAY, SETTING_OPENGL_SCENE_CACHE,
|
||||
SETTING_OPENGL_SKIP_IDENTICAL_FRAMES
|
||||
};
|
||||
std::vector<std::string> previous_opengl_render_settings;
|
||||
for (const char* key : opengl_render_setting_keys)
|
||||
previous_opengl_render_settings.emplace_back(app_config->get(key));
|
||||
|
||||
bool need_recreate_gui = false;
|
||||
std::string pending_language;
|
||||
@@ -8512,10 +8514,10 @@ void GUI_App::open_preferences(size_t open_on_tab, const std::string& highlight_
|
||||
}
|
||||
}
|
||||
|
||||
const bool opengl_fxaa_changed = app_config->get(opengl_fxaa_setting_key) != previous_opengl_fxaa;
|
||||
const bool opengl_fps_cap_changed = app_config->get(opengl_fps_cap_setting_key) != previous_opengl_fps_cap;
|
||||
const bool opengl_show_fps_overlay_changed = app_config->get(opengl_show_fps_overlay_setting_key) != previous_opengl_show_fps_overlay;
|
||||
if ((opengl_fxaa_changed || opengl_fps_cap_changed || opengl_show_fps_overlay_changed) && !need_recreate_gui && this->plater_ != nullptr) {
|
||||
bool opengl_render_settings_changed = false;
|
||||
for (size_t i = 0; i < previous_opengl_render_settings.size(); ++i)
|
||||
opengl_render_settings_changed |= app_config->get(opengl_render_setting_keys[i]) != previous_opengl_render_settings[i];
|
||||
if (opengl_render_settings_changed && !need_recreate_gui && this->plater_ != nullptr) {
|
||||
this->plater_->set_current_canvas_as_dirty();
|
||||
this->plater_->get_current_canvas3D()->force_set_focus();
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ bool GLGizmoAssembly::on_is_activable() const
|
||||
|
||||
void GLGizmoAssembly::on_render_input_window(float x, float y, float bottom_limit)
|
||||
{
|
||||
render_dimensioning_if_scene_reused();
|
||||
static std::optional<Measure::SurfaceFeature> last_feature;
|
||||
static EMode last_mode = EMode::FeatureSelection;
|
||||
static SelectedFeatures last_selected_features;
|
||||
|
||||
@@ -179,6 +179,8 @@ public:
|
||||
bool is_selectable() const { return on_is_selectable(); }
|
||||
CommonGizmosDataID get_requirements() const { return on_get_requirements(); }
|
||||
virtual bool wants_enter_leave_snapshots() const { return false; }
|
||||
// True when what on_render() draws would change if the cursor moved.
|
||||
virtual bool render_follows_cursor() const { return false; }
|
||||
virtual std::string get_gizmo_entering_text() const { assert(false); return ""; }
|
||||
virtual std::string get_gizmo_leaving_text() const { assert(false); return ""; }
|
||||
virtual std::string get_action_snapshot_name() const;
|
||||
|
||||
@@ -167,6 +167,8 @@ protected:
|
||||
|
||||
std::string on_get_name() const override;
|
||||
bool on_is_activable() const override;
|
||||
// The preview ear is drawn only while the cursor is on the model.
|
||||
bool render_follows_cursor() const override { return render_hover_point.has_value(); }
|
||||
//bool on_is_selectable() const override;
|
||||
virtual CommonGizmosDataID on_get_requirements() const override;
|
||||
void on_load(cereal::BinaryInputArchive& ar) override;
|
||||
|
||||
@@ -563,8 +563,30 @@ void GLGizmoMeasure::init_plane_glmodel(GripperType gripper_type, const Measure:
|
||||
}
|
||||
}
|
||||
|
||||
bool GLGizmoMeasure::render_follows_cursor() const
|
||||
{
|
||||
// The two raycasts on_render() starts with, without their side effects.
|
||||
if (m_editing_distance)
|
||||
return false;
|
||||
|
||||
const Vec2d mouse_position = m_parent.get_local_mouse_position();
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
Vec3f hit = Vec3f::Zero();
|
||||
Vec3f normal = Vec3f::Zero();
|
||||
for (const auto& item : m_gripper_id_raycast_map) {
|
||||
if (item.second->get_id() > 0 && item.second->get_raycaster()->closest_hit(mouse_position, item.second->get_transform(), camera, hit, normal))
|
||||
return true;
|
||||
}
|
||||
for (const auto& item : m_mesh_raycaster_map) {
|
||||
if (item.second->get_raycaster()->unproject_on_mesh(mouse_position, item.second->get_transform(), camera, hit, normal))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GLGizmoMeasure::on_render()
|
||||
{
|
||||
m_rendered_this_frame = true;
|
||||
#if ENABLE_MEASURE_GIZMO_DEBUG
|
||||
render_debug_dialog();
|
||||
#endif // ENABLE_MEASURE_GIZMO_DEBUG
|
||||
@@ -701,35 +723,36 @@ void GLGizmoMeasure::on_render()
|
||||
reset_gripper_pick(GripperType::UNDEFINE, true);
|
||||
|
||||
m_curr_feature = curr_feature;
|
||||
if (!m_curr_feature.has_value())
|
||||
return;
|
||||
m_curr_feature->volume = m_last_hit_volume;
|
||||
m_curr_feature->world_tran = m_mesh_raycaster_map[m_last_hit_volume]->get_transform();
|
||||
// The selected features are drawn below whether or not one is hovered.
|
||||
if (m_curr_feature.has_value()) {
|
||||
m_curr_feature->volume = m_last_hit_volume;
|
||||
m_curr_feature->world_tran = m_mesh_raycaster_map[m_last_hit_volume]->get_transform();
|
||||
|
||||
switch (m_curr_feature->get_type()) {
|
||||
default: { assert(false); break; }
|
||||
case Measure::SurfaceFeatureType::Point:
|
||||
{
|
||||
m_gripper_id_raycast_map[GripperType::POINT] = std::make_shared<PickRaycaster>(POINT_ID, *m_sphere.mesh_raycaster);
|
||||
break;
|
||||
}
|
||||
case Measure::SurfaceFeatureType::Edge:
|
||||
{
|
||||
m_gripper_id_raycast_map[GripperType::EDGE] = std::make_shared<PickRaycaster>(EDGE_ID, *m_cylinder.mesh_raycaster);
|
||||
break;
|
||||
}
|
||||
case Measure::SurfaceFeatureType::Circle: {
|
||||
m_curr_circle.last_circle_feature = nullptr;
|
||||
m_curr_circle.inv_zoom = 0;
|
||||
init_circle_glmodel(GripperType::CIRCLE, *m_curr_feature, m_curr_circle,inv_zoom);
|
||||
break;
|
||||
}
|
||||
case Measure::SurfaceFeatureType::Plane: {
|
||||
update_world_plane_features(m_curr_measuring.get(), *m_curr_feature);
|
||||
m_curr_plane.plane_idx = -1;
|
||||
init_plane_glmodel(GripperType::PLANE, *m_curr_feature, m_curr_plane);
|
||||
break;
|
||||
}
|
||||
switch (m_curr_feature->get_type()) {
|
||||
default: { assert(false); break; }
|
||||
case Measure::SurfaceFeatureType::Point:
|
||||
{
|
||||
m_gripper_id_raycast_map[GripperType::POINT] = std::make_shared<PickRaycaster>(POINT_ID, *m_sphere.mesh_raycaster);
|
||||
break;
|
||||
}
|
||||
case Measure::SurfaceFeatureType::Edge:
|
||||
{
|
||||
m_gripper_id_raycast_map[GripperType::EDGE] = std::make_shared<PickRaycaster>(EDGE_ID, *m_cylinder.mesh_raycaster);
|
||||
break;
|
||||
}
|
||||
case Measure::SurfaceFeatureType::Circle: {
|
||||
m_curr_circle.last_circle_feature = nullptr;
|
||||
m_curr_circle.inv_zoom = 0;
|
||||
init_circle_glmodel(GripperType::CIRCLE, *m_curr_feature, m_curr_circle,inv_zoom);
|
||||
break;
|
||||
}
|
||||
case Measure::SurfaceFeatureType::Plane: {
|
||||
update_world_plane_features(m_curr_measuring.get(), *m_curr_feature);
|
||||
m_curr_plane.plane_idx = -1;
|
||||
init_plane_glmodel(GripperType::PLANE, *m_curr_feature, m_curr_plane);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2136,8 +2159,18 @@ void GLGizmoMeasure::init_render_input_window()
|
||||
m_same_model_object = is_two_volume_in_same_model_object();
|
||||
}
|
||||
|
||||
void GLGizmoMeasure::render_dimensioning_if_scene_reused()
|
||||
{
|
||||
// The labels are ImGui and live one frame; the lines drawn with them land under the cached
|
||||
// scene, which is drawn after the overlay is built.
|
||||
if (!m_rendered_this_frame)
|
||||
render_dimensioning();
|
||||
m_rendered_this_frame = false;
|
||||
}
|
||||
|
||||
void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit)
|
||||
{
|
||||
render_dimensioning_if_scene_reused();
|
||||
static std::optional<Measure::SurfaceFeature> last_feature;
|
||||
static EMode last_mode = EMode::FeatureSelection;
|
||||
static SelectedFeatures last_selected_features;
|
||||
|
||||
@@ -228,6 +228,9 @@ protected:
|
||||
void restore_scene_raycasters_state();
|
||||
|
||||
void render_dimensioning();
|
||||
// Builds the dimension labels on a frame that reused the cached scene and so skipped on_render().
|
||||
void render_dimensioning_if_scene_reused();
|
||||
bool m_rendered_this_frame{ false };
|
||||
|
||||
#if ENABLE_MEASURE_GIZMO_DEBUG
|
||||
void render_debug_dialog();
|
||||
@@ -255,6 +258,9 @@ protected:
|
||||
bool on_init() override;
|
||||
std::string on_get_name() const override;
|
||||
bool on_is_activable() const override;
|
||||
// The hover is resolved inside on_render(), against the mesh and against the grippers of the
|
||||
// selected features, which sit off the mesh.
|
||||
bool render_follows_cursor() const override;
|
||||
void on_render() override;
|
||||
void on_set_state() override;
|
||||
|
||||
|
||||
@@ -131,15 +131,12 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoPainterBase::render_cursor()
|
||||
std::vector<Transform3d> GLGizmoPainterBase::mesh_trafo_matrices() const
|
||||
{
|
||||
// First check that the mouse pointer is on an object.
|
||||
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
const ModelInstance* mi = mo->instances[selection.get_instance_idx()];
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
|
||||
// Precalculate transformations of individual meshes.
|
||||
std::vector<Transform3d> trafo_matrices;
|
||||
for (const ModelVolume* mv : mo->volumes) {
|
||||
if (mv->is_model_part())
|
||||
@@ -154,6 +151,26 @@ void GLGizmoPainterBase::render_cursor()
|
||||
}
|
||||
}
|
||||
}
|
||||
return trafo_matrices;
|
||||
}
|
||||
|
||||
bool GLGizmoPainterBase::render_follows_cursor() const
|
||||
{
|
||||
// The brush is drawn only where the cursor meets the model. update_raycast_cache() keeps the
|
||||
// answer for render_cursor().
|
||||
if (m_c->selection_info() == nullptr || m_c->selection_info()->model_object() == nullptr)
|
||||
return false;
|
||||
update_raycast_cache(m_parent.get_local_mouse_position(), wxGetApp().plater()->get_camera(), mesh_trafo_matrices());
|
||||
return m_rr.mesh_id != -1;
|
||||
}
|
||||
|
||||
void GLGizmoPainterBase::render_cursor()
|
||||
{
|
||||
// First check that the mouse pointer is on an object.
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
|
||||
// Precalculate transformations of individual meshes.
|
||||
const std::vector<Transform3d> trafo_matrices = mesh_trafo_matrices();
|
||||
// Raycast and return if there's no hit.
|
||||
update_raycast_cache(m_parent.get_local_mouse_position(), camera, trafo_matrices);
|
||||
if (m_rr.mesh_id == -1)
|
||||
|
||||
@@ -318,6 +318,8 @@ private:
|
||||
std::vector<ProjectedHeightRange> get_projected_height_range(const Vec2d& mouse_position, double resolution, const std::vector<const ModelVolume*>& part_volumes, const std::vector<Transform3d>& trafo_matrices) const;
|
||||
|
||||
bool is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const;
|
||||
// World transforms of the model parts, in mo->volumes order.
|
||||
std::vector<Transform3d> mesh_trafo_matrices() const;
|
||||
void update_raycast_cache(const Vec2d& mouse_position,
|
||||
const Camera& camera,
|
||||
const std::vector<Transform3d>& trafo_matrices) const;
|
||||
@@ -370,6 +372,7 @@ protected:
|
||||
virtual PainterGizmoType get_painter_type() const = 0;
|
||||
|
||||
bool on_is_activable() const override;
|
||||
bool render_follows_cursor() const override;
|
||||
bool on_is_selectable() const override;
|
||||
void on_load(cereal::BinaryInputArchive& ar) override;
|
||||
void on_save(cereal::BinaryOutputArchive& ar) const override {}
|
||||
|
||||
@@ -410,6 +410,10 @@ void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal, bool ver
|
||||
|
||||
void ObjectClipper::set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos)
|
||||
{
|
||||
// Called every frame by GLGizmoCut3D::on_render(), usually with the plane already set.
|
||||
if (m_clp && *m_clp == ClippingPlane(cpl_normal, cpl_offset) && m_clp_ratio == pos)
|
||||
return;
|
||||
|
||||
m_clp.reset(new ClippingPlane(cpl_normal, cpl_offset));
|
||||
m_clp_ratio = pos;
|
||||
get_pool()->get_canvas()->set_as_dirty();
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
#include <wx/glcanvas.h>
|
||||
|
||||
namespace Slic3r {
|
||||
@@ -631,6 +633,7 @@ void GLGizmosManager::render_painter_assemble_view() const
|
||||
m_assemble_view_data->model_objects_clipper()->render_cut();
|
||||
}
|
||||
|
||||
// The icon bar, drawn with GL.
|
||||
void GLGizmosManager::render_overlay()
|
||||
{
|
||||
if (!m_enabled)
|
||||
@@ -639,7 +642,27 @@ void GLGizmosManager::render_overlay()
|
||||
if (m_icons_texture_dirty)
|
||||
generate_icons_texture();
|
||||
|
||||
do_render_overlay();
|
||||
do_render_overlay(true);
|
||||
}
|
||||
|
||||
// The open gizmo's settings panel, ImGui.
|
||||
void GLGizmosManager::render_overlay_input_window()
|
||||
{
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
do_render_overlay(false);
|
||||
}
|
||||
|
||||
size_t GLGizmosManager::get_overlay_state_hash() const
|
||||
{
|
||||
size_t hash = 0;
|
||||
boost::hash_combine(hash, m_enabled);
|
||||
boost::hash_combine(hash, (int)m_hover);
|
||||
boost::hash_combine(hash, (int)m_current);
|
||||
boost::hash_combine(hash, (int)m_highlight.first);
|
||||
boost::hash_combine(hash, m_highlight.second);
|
||||
return hash;
|
||||
}
|
||||
|
||||
std::string GLGizmosManager::get_tooltip() const
|
||||
@@ -1222,7 +1245,9 @@ void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_t
|
||||
|
||||
//BBS: GUI refactor: GLToolbar&&Gizmo adjust
|
||||
//when rendering, {0, 0} is at the center, {-0.5, 0.5} at the left-top
|
||||
void GLGizmosManager::do_render_overlay() const
|
||||
// draw_icons selects the icon bar (GL) or the open gizmo's input window (ImGui), placed by the same
|
||||
// layout walk.
|
||||
void GLGizmosManager::do_render_overlay(bool draw_icons) const
|
||||
{
|
||||
const std::vector<size_t> selectable_idxs = get_selectable_idxs();
|
||||
if (selectable_idxs.empty())
|
||||
@@ -1261,7 +1286,8 @@ void GLGizmosManager::do_render_overlay() const
|
||||
}
|
||||
float top_y = 1.0f;
|
||||
|
||||
render_background(top_x, top_y, top_x + width, top_y - height, border_w, border_h);
|
||||
if (draw_icons)
|
||||
render_background(top_x, top_y, top_x + width, top_y - height, border_w, border_h);
|
||||
|
||||
top_x += border_w;
|
||||
top_y -= border_h;
|
||||
@@ -1298,7 +1324,8 @@ void GLGizmosManager::do_render_overlay() const
|
||||
const float v_top = v_offset + sprite_id * dv;
|
||||
const float v_bottom = v_top + dv - v_offset;
|
||||
|
||||
GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + icons_size_x, top_y - icons_size_y, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } });
|
||||
if (draw_icons)
|
||||
GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + icons_size_x, top_y - icons_size_y, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } });
|
||||
if (idx == m_current
|
||||
// Orca: Show Svg dialog at the same place as emboss gizmo
|
||||
|| (m_current == Svg && idx == Emboss)) {
|
||||
@@ -1306,7 +1333,8 @@ void GLGizmosManager::do_render_overlay() const
|
||||
//render_input_window uses a different coordination(imgui)
|
||||
//1. no need to scale by camera zoom, set {0,0} at left-up corner for imgui
|
||||
//gizmo->render_input_window(width, 0.5f * cnv_h - zoomed_top_y * zoom, toolbar_top);
|
||||
m_gizmos[m_current]->render_input_window(0.5 * cnv_w + 0.5f * top_x * cnv_w, get_scaled_total_height(), cnv_h);
|
||||
if (!draw_icons)
|
||||
m_gizmos[m_current]->render_input_window(0.5 * cnv_w + 0.5f * top_x * cnv_w, get_scaled_total_height(), cnv_h);
|
||||
|
||||
is_render_current = true;
|
||||
}
|
||||
@@ -1314,7 +1342,7 @@ void GLGizmosManager::do_render_overlay() const
|
||||
}
|
||||
|
||||
// BBS simplify gizmo is not a selected gizmo and need to render input window
|
||||
if (!is_render_current && m_current != Undefined) {
|
||||
if (!draw_icons && !is_render_current && m_current != Undefined) {
|
||||
m_gizmos[m_current]->render_input_window(0.5 * cnv_w + 0.5f * top_x * cnv_w, get_scaled_total_height(), cnv_h);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,6 +296,9 @@ public:
|
||||
void render_painter_assemble_view() const;
|
||||
|
||||
void render_overlay();
|
||||
void render_overlay_input_window();
|
||||
// Hash of the state render_overlay() draws from: enabled, hover, current and highlight.
|
||||
size_t get_overlay_state_hash() const;
|
||||
|
||||
void render_arrow(const GLCanvas3D& parent, EType highlighted_type) const;
|
||||
|
||||
@@ -329,7 +332,7 @@ private:
|
||||
|
||||
void render_background(float left, float top, float right, float bottom, float border_w, float border_h) const;
|
||||
|
||||
void do_render_overlay() const;
|
||||
void do_render_overlay(bool draw_icons) const;
|
||||
|
||||
bool generate_icons_texture();
|
||||
|
||||
|
||||
@@ -590,11 +590,39 @@ void ImGuiWrapper::new_frame()
|
||||
// BBL: end copy & paste
|
||||
}
|
||||
|
||||
void ImGuiWrapper::render()
|
||||
ImDrawData* ImGuiWrapper::end_frame()
|
||||
{
|
||||
ImGui::Render();
|
||||
render_draw_data(ImGui::GetDrawData());
|
||||
m_new_frame_open = false;
|
||||
return ImGui::GetDrawData();
|
||||
}
|
||||
|
||||
void ImGuiWrapper::render(ImDrawData* draw_data)
|
||||
{
|
||||
render_draw_data(draw_data);
|
||||
}
|
||||
|
||||
ImGuiID ImGuiWrapper::draw_data_signature(const ImDrawData* draw_data)
|
||||
{
|
||||
ImGuiID hash = 0;
|
||||
if (draw_data == nullptr)
|
||||
return hash;
|
||||
|
||||
for (int i = 0; i < draw_data->CmdListsCount; ++i) {
|
||||
const ImDrawList* list = draw_data->CmdLists[i];
|
||||
hash = ImHashData(list->VtxBuffer.Data, list->VtxBuffer.Size * sizeof(ImDrawVert), hash);
|
||||
hash = ImHashData(list->IdxBuffer.Data, list->IdxBuffer.Size * sizeof(ImDrawIdx), hash);
|
||||
// ImDrawCmd has padding, and a hovered ImageButton3() differs only in TextureId.
|
||||
for (const ImDrawCmd& cmd : list->CmdBuffer) {
|
||||
hash = ImHashData(&cmd.ClipRect, sizeof(cmd.ClipRect), hash);
|
||||
hash = ImHashData(&cmd.TextureId, sizeof(cmd.TextureId), hash);
|
||||
hash = ImHashData(&cmd.VtxOffset, sizeof(cmd.VtxOffset), hash);
|
||||
hash = ImHashData(&cmd.IdxOffset, sizeof(cmd.IdxOffset), hash);
|
||||
hash = ImHashData(&cmd.ElemCount, sizeof(cmd.ElemCount), hash);
|
||||
hash = ImHashData(&cmd.UserCallback, sizeof(cmd.UserCallback), hash);
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
ImVec2 ImGuiWrapper::calc_text_size(std::string_view text,
|
||||
|
||||
@@ -98,7 +98,11 @@ public:
|
||||
const ImWchar *get_glyph_ranges() const { return m_glyph_ranges; } // language specific
|
||||
|
||||
void new_frame();
|
||||
void render();
|
||||
// Ends the frame and returns its draw data without drawing it.
|
||||
ImDrawData* end_frame();
|
||||
void render(ImDrawData* draw_data);
|
||||
// Hash of every draw list's vertices, indices and commands.
|
||||
static ImGuiID draw_data_signature(const ImDrawData* draw_data);
|
||||
|
||||
float scaled(float x) const { return x * m_font_size; }
|
||||
ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); }
|
||||
|
||||
@@ -1115,19 +1115,9 @@ void PartPlate::render_plate_name_texture()
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
}
|
||||
|
||||
void PartPlate::show_tooltip(const std::string tooltip)
|
||||
void PartPlate::set_hover_tooltip(const std::string& tooltip)
|
||||
{
|
||||
const auto scale = m_plater->get_current_canvas3D()->get_scale();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {6 * scale, 3 * scale});
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 3 * scale);
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND);
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, {0, 0, 0, 0});
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 1.00f));
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::TextUnformatted(tooltip.c_str());
|
||||
ImGui::EndTooltip();
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopStyleVar(2);
|
||||
m_partplate_list->m_hover_tooltip = tooltip;
|
||||
}
|
||||
|
||||
void PartPlate::render_icons(bool bottom, bool only_name, int hover_id)
|
||||
@@ -1152,21 +1142,21 @@ void PartPlate::render_icons(bool bottom, bool only_name, int hover_id)
|
||||
if (!only_name) {
|
||||
if (hover_id == 1) {
|
||||
render_icon_texture(m_del_icon.model, m_partplate_list->m_del_hovered_texture);
|
||||
show_tooltip(_u8L("Remove current plate (if not last one)"));
|
||||
set_hover_tooltip(_u8L("Remove current plate (if not last one)"));
|
||||
}
|
||||
else
|
||||
render_icon_texture(m_del_icon.model, m_partplate_list->m_del_texture);
|
||||
|
||||
if (hover_id == 2) {
|
||||
render_icon_texture(m_orient_icon.model, m_partplate_list->m_orient_hovered_texture);
|
||||
show_tooltip(_u8L("Auto orient objects on current plate"));
|
||||
set_hover_tooltip(_u8L("Auto orient objects on current plate"));
|
||||
}
|
||||
else
|
||||
render_icon_texture(m_orient_icon.model, m_partplate_list->m_orient_texture);
|
||||
|
||||
if (hover_id == 3) {
|
||||
render_icon_texture(m_arrange_icon.model, m_partplate_list->m_arrange_hovered_texture);
|
||||
show_tooltip(_u8L("Arrange objects on current plate"));
|
||||
set_hover_tooltip(_u8L("Arrange objects on current plate"));
|
||||
}
|
||||
else
|
||||
render_icon_texture(m_arrange_icon.model, m_partplate_list->m_arrange_texture);
|
||||
@@ -1175,12 +1165,12 @@ void PartPlate::render_icons(bool bottom, bool only_name, int hover_id)
|
||||
if (this->is_locked()) {
|
||||
render_icon_texture(m_lock_icon.model,
|
||||
m_partplate_list->m_locked_hovered_texture);
|
||||
show_tooltip(_u8L("Unlock current plate"));
|
||||
set_hover_tooltip(_u8L("Unlock current plate"));
|
||||
}
|
||||
else {
|
||||
render_icon_texture(m_lock_icon.model,
|
||||
m_partplate_list->m_lockopen_hovered_texture);
|
||||
show_tooltip(_u8L("Lock current plate"));
|
||||
set_hover_tooltip(_u8L("Lock current plate"));
|
||||
}
|
||||
} else {
|
||||
if (this->is_locked())
|
||||
@@ -1194,21 +1184,21 @@ void PartPlate::render_icons(bool bottom, bool only_name, int hover_id)
|
||||
if (dual_bbl) {
|
||||
if (hover_id == PLATE_FILAMENT_MAP_ID){
|
||||
render_icon_texture(m_plate_filament_map_icon.model, m_partplate_list->m_plate_set_filament_map_hovered_texture);
|
||||
show_tooltip(_u8L("Filament grouping"));
|
||||
set_hover_tooltip(_u8L("Filament grouping"));
|
||||
} else
|
||||
render_icon_texture(m_plate_filament_map_icon.model, m_partplate_list->m_plate_set_filament_map_texture);
|
||||
}
|
||||
|
||||
if (hover_id == 6) {
|
||||
render_icon_texture(m_plate_name_edit_icon.model, m_partplate_list->m_plate_name_edit_hovered_texture);
|
||||
show_tooltip(_u8L("Edit current plate name"));
|
||||
set_hover_tooltip(_u8L("Edit current plate name"));
|
||||
}
|
||||
else
|
||||
render_icon_texture(m_plate_name_edit_icon.model, m_partplate_list->m_plate_name_edit_texture);
|
||||
|
||||
if (hover_id == 7) {
|
||||
render_icon_texture(m_move_front_icon.model, m_partplate_list->m_move_front_hovered_texture);
|
||||
show_tooltip(_u8L("Move plate to the front"));
|
||||
set_hover_tooltip(_u8L("Move plate to the front"));
|
||||
} else
|
||||
render_icon_texture(m_move_front_icon.model, m_partplate_list->m_move_front_texture);
|
||||
|
||||
@@ -1221,7 +1211,7 @@ void PartPlate::render_icons(bool bottom, bool only_name, int hover_id)
|
||||
else
|
||||
render_icon_texture(m_plate_settings_icon.model, m_partplate_list->m_plate_settings_changed_hovered_texture);
|
||||
|
||||
show_tooltip(_u8L("Customize current plate"));
|
||||
set_hover_tooltip(_u8L("Customize current plate"));
|
||||
} else {
|
||||
if (!has_plate_settings)
|
||||
render_icon_texture(m_plate_settings_icon.model, m_partplate_list->m_plate_settings_texture);
|
||||
@@ -6002,6 +5992,24 @@ void PartPlateList::render(const Transform3d& view_matrix, const Transform3d& pr
|
||||
}
|
||||
}
|
||||
|
||||
void PartPlateList::render_hover_tooltip() const
|
||||
{
|
||||
if (m_hover_tooltip.empty())
|
||||
return;
|
||||
|
||||
const auto scale = m_plater->get_current_canvas3D()->get_scale();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {6 * scale, 3 * scale});
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 3 * scale);
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND);
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, {0, 0, 0, 0});
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 1.00f));
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::TextUnformatted(m_hover_tooltip.c_str());
|
||||
ImGui::EndTooltip();
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopStyleVar(2);
|
||||
}
|
||||
|
||||
/*int PartPlateList::select_plate_by_hover_id(int hover_id)
|
||||
{
|
||||
int index = hover_id / PartPlate::GRABBER_COUNT;
|
||||
|
||||
@@ -197,7 +197,7 @@ private:
|
||||
// void render_left_arrow(const ColorRGBA render_color, bool use_lighting) const;
|
||||
// void render_right_arrow(const ColorRGBA render_color, bool use_lighting) const;
|
||||
void render_icon_texture(GLModel &buffer, GLTexture &texture);
|
||||
void show_tooltip(const std::string tooltip);
|
||||
void set_hover_tooltip(const std::string& tooltip);
|
||||
void render_icons(bool bottom, bool only_name = false, int hover_id = -1);
|
||||
void render_only_numbers(bool bottom);
|
||||
void render_plate_name_texture();
|
||||
@@ -640,6 +640,8 @@ class PartPlateList : public ObjectBase
|
||||
bool render_bedtype_logo = true;
|
||||
bool render_plate_settings = true;
|
||||
bool render_cali_logo = true;
|
||||
// Tooltip of the plate icon the last scene pass drew hovered; the canvas overlay shows it.
|
||||
std::string m_hover_tooltip;
|
||||
|
||||
bool m_is_dark = false;
|
||||
|
||||
@@ -860,6 +862,8 @@ public:
|
||||
void render(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current = false, bool only_body = false, int hover_id = -1, bool render_cali = false, bool show_grid = true, bool hide_chrome = false);
|
||||
void set_render_option(bool bedtype_texture, bool plate_settings);
|
||||
void set_render_cali(bool value = true) { render_cali_logo = value; }
|
||||
void render_hover_tooltip() const;
|
||||
void clear_hover_tooltip() { m_hover_tooltip.clear(); }
|
||||
void register_raycasters_for_picking(GLCanvas3D& canvas)
|
||||
{
|
||||
for (auto plate : m_plate_list)
|
||||
|
||||
@@ -1005,6 +1005,7 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too
|
||||
checkbox->SetToolTip(tip);
|
||||
|
||||
if (param == "sync_user_preset") { m_sync_user_preset_checkbox = checkbox; }
|
||||
if (param == SETTING_OPENGL_SKIP_IDENTICAL_FRAMES) { m_skip_identical_frames_checkbox = checkbox; }
|
||||
|
||||
m_sizer->Add(checkbox, 0, wxALIGN_CENTER);
|
||||
|
||||
@@ -1031,6 +1032,9 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " sync_user_preset: " << (sync ? "true" : "false");
|
||||
}
|
||||
else if (param == SETTING_OPENGL_SCENE_CACHE) {
|
||||
if (m_skip_identical_frames_checkbox) m_skip_identical_frames_checkbox->Enable(checkbox->GetValue());
|
||||
}
|
||||
else if (param == "stealth_mode") {
|
||||
bool enabled = app_config->get_stealth_mode();
|
||||
if (enabled) wxGetApp().on_stealth_mode_enter();
|
||||
@@ -1959,9 +1963,32 @@ void PreferencesDialog::create_items()
|
||||
);
|
||||
g_sizer->Add(item_fps_cap);
|
||||
|
||||
auto item_scene_cache = create_item_checkbox(
|
||||
_L("Reuse the 3D scene while idle"),
|
||||
_L("Skips redrawing the 3D scene when only the mouse cursor moves over the viewport,\n"
|
||||
"and reuses the previous frame's scene instead. Reduces GPU load.\n"
|
||||
"Disable it if the viewport shows stale or missing contents.\n\n"
|
||||
"Takes effect immediately."),
|
||||
SETTING_OPENGL_SCENE_CACHE
|
||||
);
|
||||
g_sizer->Add(item_scene_cache);
|
||||
|
||||
auto item_skip_identical_frames = create_item_checkbox(
|
||||
_L("Skip unchanged frames"),
|
||||
_L("Skips drawing a frame altogether when it would be identical to the one already on screen.\n"
|
||||
"Only applies to frames that reuse the 3D scene, so it needs Reuse the 3D scene while idle.\n"
|
||||
"Disable it if a hover highlight, tooltip or animation stops updating.\n\n"
|
||||
"Takes effect immediately."),
|
||||
SETTING_OPENGL_SKIP_IDENTICAL_FRAMES
|
||||
);
|
||||
g_sizer->Add(item_skip_identical_frames);
|
||||
if (m_skip_identical_frames_checkbox) m_skip_identical_frames_checkbox->Enable(app_config->get_bool(SETTING_OPENGL_SCENE_CACHE));
|
||||
|
||||
auto item_fps_overlay = create_item_checkbox(
|
||||
_L("Show FPS overlay"),
|
||||
_L("Displays current viewport FPS in the top-right corner."),
|
||||
_L("Displays rendering counts in the top-right corner of the viewport.") + "\n" +
|
||||
_L("FPS: frames presented to the screen per second.") + "\n" +
|
||||
_L("3D: frames per second that redrew the 3D scene."),
|
||||
SETTING_OPENGL_SHOW_FPS_OVERLAY
|
||||
);
|
||||
g_sizer->Add(item_fps_overlay);
|
||||
|
||||
@@ -71,6 +71,7 @@ public:
|
||||
::CheckBox * m_dark_mode_ckeckbox = {nullptr};
|
||||
::CheckBox * m_sync_user_preset_checkbox = {nullptr};
|
||||
::CheckBox * m_bambu_cloud_checkbox = {nullptr};
|
||||
::CheckBox * m_skip_identical_frames_checkbox = {nullptr};
|
||||
::TextInput *m_backup_interval_textinput = {nullptr};
|
||||
::SpinInput *m_dim_previous_layers_brightness_input = {nullptr};
|
||||
::ComboBox * m_network_version_combo = {nullptr};
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "SceneCache.hpp"
|
||||
|
||||
#include "3DScene.hpp"
|
||||
#include "GLModel.hpp"
|
||||
#include "GLShader.hpp"
|
||||
#include "GLTexture.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
void SceneCache::capture(Key key)
|
||||
{
|
||||
m_valid = false;
|
||||
|
||||
if (wxGetApp().get_shader("flat_texture") == nullptr)
|
||||
return;
|
||||
|
||||
GLTexture::copy_from_framebuffer(m_texture_id, m_texture_size, key.size[0], key.size[1], GL_NEAREST);
|
||||
|
||||
m_key = std::move(key);
|
||||
m_valid = true;
|
||||
}
|
||||
|
||||
void SceneCache::render(GLModel& quad)
|
||||
{
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("flat_texture");
|
||||
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
|
||||
shader->start_using();
|
||||
shader->set_uniform("view_model_matrix", Transform3d::Identity());
|
||||
shader->set_uniform("projection_matrix", Transform3d::Identity());
|
||||
shader->set_uniform("uniform_texture", 0);
|
||||
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_texture_id));
|
||||
quad.render();
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
shader->stop_using();
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
}
|
||||
|
||||
void SceneCache::reset()
|
||||
{
|
||||
m_valid = false;
|
||||
if (m_texture_id != 0) {
|
||||
glsafe(::glDeleteTextures(1, &m_texture_id));
|
||||
m_texture_id = 0;
|
||||
}
|
||||
m_texture_size = { { 0, 0 } };
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class GLModel;
|
||||
|
||||
// The last 3D scene pass, kept as a texture for frames that only rebuild the overlay.
|
||||
class SceneCache
|
||||
{
|
||||
public:
|
||||
// Scene pass inputs that change without any frame request.
|
||||
struct Key
|
||||
{
|
||||
std::array<unsigned int, 2> size{ { 0, 0 } };
|
||||
Transform3d view_matrix{ Transform3d::Identity() };
|
||||
Transform3d projection_matrix{ Transform3d::Identity() };
|
||||
// Hovered volumes that draw their sinking contour, hovered plate icons, hovered gizmo grabber.
|
||||
std::vector<int> sinking_hover_volume_idxs;
|
||||
std::vector<int> hover_plate_icon_idxs;
|
||||
int gizmo_hover_id{ -1 };
|
||||
bool render_preview{ true };
|
||||
|
||||
bool operator == (const Key& other) const {
|
||||
return size == other.size && render_preview == other.render_preview &&
|
||||
gizmo_hover_id == other.gizmo_hover_id &&
|
||||
sinking_hover_volume_idxs == other.sinking_hover_volume_idxs &&
|
||||
hover_plate_icon_idxs == other.hover_plate_icon_idxs &&
|
||||
view_matrix.isApprox(other.view_matrix) &&
|
||||
projection_matrix.isApprox(other.projection_matrix);
|
||||
}
|
||||
};
|
||||
|
||||
// Copies the bound read framebuffer, sized by key.size, and remembers key.
|
||||
void capture(Key key);
|
||||
bool matches(const Key& key) const { return m_valid && m_key == key; }
|
||||
// Draws the last capture over the whole viewport, on the given full screen quad.
|
||||
void render(GLModel& quad);
|
||||
void invalidate() { m_valid = false; }
|
||||
// Frees the texture.
|
||||
void reset();
|
||||
|
||||
private:
|
||||
unsigned int m_texture_id{ 0 };
|
||||
std::array<unsigned int, 2> m_texture_size{ { 0, 0 } };
|
||||
Key m_key;
|
||||
bool m_valid{ false };
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
Reference in New Issue
Block a user