mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-28 19:31:22 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6d89d6978 | ||
|
|
abecef53b0 | ||
|
|
44d5a22a32 | ||
|
|
dec146f1e6 | ||
|
|
a9fec0ffc4 | ||
|
|
9ab6bbebf0 | ||
|
|
cda1588578 |
@@ -0,0 +1,80 @@
|
|||||||
|
# G-code preview while dragging
|
||||||
|
|
||||||
|
The sliced preview draws every toolpath segment of the plate as an instanced box. On a large
|
||||||
|
plate that is tens of millions of segments, and the frame is GPU-bound: the cost is the number of
|
||||||
|
instances drawn, not anything the CPU does per frame. Dragging the camera over such a plate cannot
|
||||||
|
keep up. The `preview_reduced_detail_mode` preference (*Graphics > G-code Preview*, off by
|
||||||
|
default) lets the preview draw less while the user drags and put the full toolpaths back when they
|
||||||
|
let go.
|
||||||
|
|
||||||
|
| Preference | Values | Effect |
|
||||||
|
|---|---|---|
|
||||||
|
| `preview_reduced_detail_mode` | `off`, `solid`, `shell` | what is drawn while dragging |
|
||||||
|
|
||||||
|
libvgcode (`src/libvgcode`) builds and binds the reduced toolpath set, `GCodeViewer` maps the
|
||||||
|
preferences onto it and draws the solid model, and `GLCanvas3D` decides when the user is dragging.
|
||||||
|
The OpenGL ES path keeps a single set and ignores the preference.
|
||||||
|
|
||||||
|
## Two sets, one walk
|
||||||
|
|
||||||
|
`ViewerImpl::update_enabled_entities()` walks the visible vertex range once and fills two segment
|
||||||
|
index buffers side by side: the **full** set and the **reduced** set (segments and options).
|
||||||
|
Building them together is what makes switching free: starting or ending a drag is a buffer
|
||||||
|
binding, never a rebuild. A change of mode does rebuild. Nothing is built while the mode
|
||||||
|
is off, and the reduced buffers are then uploaded empty so that the last set does not stay
|
||||||
|
allocated.
|
||||||
|
|
||||||
|
Whatever the mode leaves out, the bottom and top layers of the visible range are kept whole: they
|
||||||
|
are the faces the range cuts open, and the top is what the user is looking at.
|
||||||
|
|
||||||
|
### Modes
|
||||||
|
|
||||||
|
- `EndLayersOnly` (`solid` in the preference) keeps only the two end layers. `GCodeViewer` then
|
||||||
|
draws the sliced objects and the prime tower as opaque solids, see below.
|
||||||
|
- `ShellOnly` (`shell`) keeps every role but the sparse infill, internal solid infill, internal
|
||||||
|
bridge infill and gap fill, which lie under the skins or between the walls. Walls, top and
|
||||||
|
bottom surfaces, bridges, supports and the prime tower are drawn whole, so from outside the
|
||||||
|
print looks as it does at rest. The roles come from
|
||||||
|
the slicer, so the classification is exact wherever the slicer's is; what it cannot express is
|
||||||
|
the inside of the prime tower and of a support, which share one role with their outside and are
|
||||||
|
kept whole. A profile with no top or bottom shell layers, or no walls, leaves the infill on the
|
||||||
|
surface, so `GCodeViewer` turns the hiding off for such a print when it loads it, from the
|
||||||
|
print's default region settings; per-object overrides are not consulted.
|
||||||
|
|
||||||
|
## The solid model
|
||||||
|
|
||||||
|
The preview already loads the sliced objects as shells for its translucent ghost.
|
||||||
|
`GCodeViewer::render_solid_model()` draws those shells opaque, in their filament colors, with the
|
||||||
|
`gouraud` shader, whose z range cuts them to the visible layer range. The two toolpath layers of
|
||||||
|
the reduced set are drawn afterwards and cap the cut with what was really printed there. The
|
||||||
|
shells hold only the objects, so while this mode is on the prime tower is added from its sliced
|
||||||
|
mesh, positioned as the print placed it. It is added or removed on its own when the mode changes,
|
||||||
|
without reloading the objects, keeps its opaque color so that it never appears among the
|
||||||
|
translucent shells, and stays out of their bounding box. Supports have no mesh and are not shown,
|
||||||
|
and `load_shells()` drops every non-model-part volume, so a negative volume is not cut out.
|
||||||
|
|
||||||
|
A plate whose shells are not loaded keeps drawing toolpaths, since the solid model would leave
|
||||||
|
only the end layers.
|
||||||
|
|
||||||
|
## Deciding that the user is dragging
|
||||||
|
|
||||||
|
`GLCanvas3D::_update_preview_interaction()` runs at the top of every preview frame, before the
|
||||||
|
canvas decides whether to reuse its cached scene, so that the switch lands in that frame. Dragging
|
||||||
|
is `GLCanvas3D::is_user_interacting()`, the same answer the scene cache reads: the camera, the
|
||||||
|
navigator, a gizmo, the rectangle selection or either slider being held. A slider reports this from
|
||||||
|
ImGui's active id rather than its dirty flag, which is raised and consumed inside one frame. A
|
||||||
|
wheel step has no duration, so it holds the reduced set for a 150 ms settle time instead, and the
|
||||||
|
frame that restores the toolpaths is scheduled for when that time runs out, since the render timer
|
||||||
|
only wakes the idle loop. A drag cut short by focus or capture loss is ended explicitly, and a
|
||||||
|
button release wakes the idle loop, because on some platforms nothing else would until the next
|
||||||
|
input.
|
||||||
|
|
||||||
|
## Reused scene frames
|
||||||
|
|
||||||
|
`GLCanvas3D` keeps its last scene pass for frames that only rebuild the overlay (`SceneCache`). Its
|
||||||
|
key covers the canvas size, the camera and hover state, not what the toolpath sets draw, so a frame
|
||||||
|
that reuses the scene must never be one on which the set is switched.
|
||||||
|
`_update_preview_interaction()` therefore reports whether the bound set changed, and a frame on
|
||||||
|
which it did redraws the scene. The canvas neither captures nor reuses the scene while the user
|
||||||
|
drags, so no reduced frame outlives a drag, and the frame that ends a wheel's settle time is
|
||||||
|
requested as a full frame.
|
||||||
@@ -205,6 +205,13 @@ void AppConfig::set_defaults()
|
|||||||
if (get("seq_top_layer_only").empty())
|
if (get("seq_top_layer_only").empty())
|
||||||
set("seq_top_layer_only", "1");
|
set("seq_top_layer_only", "1");
|
||||||
|
|
||||||
|
// what the preview draws while the user drags it
|
||||||
|
{
|
||||||
|
const std::string mode = get("preview_reduced_detail_mode");
|
||||||
|
if (mode != "off" && mode != "solid" && mode != "shell")
|
||||||
|
set("preview_reduced_detail_mode", "off");
|
||||||
|
}
|
||||||
|
|
||||||
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
||||||
if (get("preview_dim_previous_layers").empty())
|
if (get("preview_dim_previous_layers").empty())
|
||||||
set_bool("preview_dim_previous_layers", false);
|
set_bool("preview_dim_previous_layers", false);
|
||||||
|
|||||||
@@ -158,6 +158,21 @@ enum class EGCodeExtrusionRole : uint8_t
|
|||||||
|
|
||||||
static constexpr std::size_t GCODE_EXTRUSION_ROLES_COUNT = static_cast<std::size_t>(EGCodeExtrusionRole::COUNT);
|
static constexpr std::size_t GCODE_EXTRUSION_ROLES_COUNT = static_cast<std::size_t>(EGCodeExtrusionRole::COUNT);
|
||||||
|
|
||||||
|
//
|
||||||
|
// What the reduced set, drawn while the user is dragging, holds in place of the full toolpaths
|
||||||
|
//
|
||||||
|
enum class EReducedDetailMode : uint8_t
|
||||||
|
{
|
||||||
|
// nothing: no reduced set is built
|
||||||
|
Off,
|
||||||
|
// only the bottom and top layers of the visible range, for a caller that draws the print
|
||||||
|
// itself some other way
|
||||||
|
EndLayersOnly,
|
||||||
|
// every role but the infill under the skins and the gap fill: what can be seen from outside
|
||||||
|
ShellOnly,
|
||||||
|
COUNT
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Option types
|
// Option types
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -114,6 +114,19 @@ public:
|
|||||||
//
|
//
|
||||||
bool is_dim_previous_layers() const;
|
bool is_dim_previous_layers() const;
|
||||||
void set_dim_previous_layers(bool value);
|
void set_dim_previous_layers(bool value);
|
||||||
|
//
|
||||||
|
// The reduced set drawn while the user drags: what the mode keeps, and always the bottom and
|
||||||
|
// top layers of the visible range. While a mode is set it is built alongside the full set, so
|
||||||
|
// set_reduced_detail() rebuilds nothing. Ignored on the OpenGL ES path.
|
||||||
|
//
|
||||||
|
EReducedDetailMode get_reduced_detail_mode() const;
|
||||||
|
void set_reduced_detail_mode(EReducedDetailMode mode);
|
||||||
|
// Whether the shell mode hides the infill roles; off for a profile that leaves them on the
|
||||||
|
// surface, with no top or bottom shell or no walls.
|
||||||
|
bool get_reduced_detail_hide_infill() const;
|
||||||
|
void set_reduced_detail_hide_infill(bool value);
|
||||||
|
void set_reduced_detail(bool value);
|
||||||
|
bool is_reduced_detail() const;
|
||||||
float get_dim_previous_layers_brightness() const;
|
float get_dim_previous_layers_brightness() const;
|
||||||
void set_dim_previous_layers_brightness(float value);
|
void set_dim_previous_layers_brightness(float value);
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ struct Settings
|
|||||||
// ORCA: how bright those darkened layers are rendered, 1.0 = unchanged, 0.0 = black
|
// ORCA: how bright those darkened layers are rendered, 1.0 = unchanged, 0.0 = black
|
||||||
float dim_previous_layers_brightness{ 0.4f };
|
float dim_previous_layers_brightness{ 0.4f };
|
||||||
bool spiral_vase_mode{ false };
|
bool spiral_vase_mode{ false };
|
||||||
|
// what the reduced set holds and whether it is drawn. Ignored on the OpenGL ES path.
|
||||||
|
EReducedDetailMode reduced_detail_mode{ EReducedDetailMode::Off };
|
||||||
|
// whether the shell mode may hide the infill roles: false when the profile leaves them on the surface
|
||||||
|
bool reduced_detail_hide_infill{ true };
|
||||||
|
bool reduced_detail{ false };
|
||||||
//
|
//
|
||||||
// Required update flags
|
// Required update flags
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ static const char* Segments_Vertex_Shader =
|
|||||||
// ORCA: 0 during the shadow caster pass - the bias below shifts eye_position but not
|
// ORCA: 0 during the shadow caster pass - the bias below shifts eye_position but not
|
||||||
// world_position, so the caster would write a depth the receiver never looks up.
|
// world_position, so the caster would write a depth the receiver never looks up.
|
||||||
"uniform float bias_scale;\n"
|
"uniform float bias_scale;\n"
|
||||||
|
// draw the instances last to first, top layers before the ones they hide, so that early depth
|
||||||
|
// rejection discards most of the hidden fragments; set when the camera looks down on the print
|
||||||
|
"uniform int reverse_order;\n"
|
||||||
|
"uniform int instance_count;\n"
|
||||||
"in int vertex_id;\n"
|
"in int vertex_id;\n"
|
||||||
"out vec3 color;\n"
|
"out vec3 color;\n"
|
||||||
"// ORCA: realistic view - the light the shadow map is able to block, kept apart from the\n"
|
"// ORCA: realistic view - the light the shadow map is able to block, kept apart from the\n"
|
||||||
@@ -59,7 +63,8 @@ static const char* Segments_Vertex_Shader =
|
|||||||
" return top_diffuse + front_diffuse + top_specular;\n"
|
" return top_diffuse + front_diffuse + top_specular;\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"void main() {\n"
|
"void main() {\n"
|
||||||
" int id_a = int(texelFetch(segment_index_tex, gl_InstanceID).r);\n"
|
" int instance = (reverse_order != 0) ? instance_count - 1 - gl_InstanceID : gl_InstanceID;\n"
|
||||||
|
" int id_a = int(texelFetch(segment_index_tex, instance).r);\n"
|
||||||
" int id_b = id_a + 1;\n"
|
" int id_b = id_a + 1;\n"
|
||||||
" vec3 pos_a = texelFetch(position_tex, id_a).xyz;\n"
|
" vec3 pos_a = texelFetch(position_tex, id_a).xyz;\n"
|
||||||
" vec3 pos_b = texelFetch(position_tex, id_b).xyz;\n"
|
" vec3 pos_b = texelFetch(position_tex, id_b).xyz;\n"
|
||||||
|
|||||||
@@ -92,6 +92,36 @@ bool Viewer::is_dim_previous_layers() const
|
|||||||
return m_impl->is_dim_previous_layers();
|
return m_impl->is_dim_previous_layers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Viewer::set_reduced_detail(bool value)
|
||||||
|
{
|
||||||
|
m_impl->set_reduced_detail(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Viewer::is_reduced_detail() const
|
||||||
|
{
|
||||||
|
return m_impl->is_reduced_detail();
|
||||||
|
}
|
||||||
|
|
||||||
|
EReducedDetailMode Viewer::get_reduced_detail_mode() const
|
||||||
|
{
|
||||||
|
return m_impl->get_reduced_detail_mode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::set_reduced_detail_mode(EReducedDetailMode mode)
|
||||||
|
{
|
||||||
|
m_impl->set_reduced_detail_mode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Viewer::get_reduced_detail_hide_infill() const
|
||||||
|
{
|
||||||
|
return m_impl->get_reduced_detail_hide_infill();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::set_reduced_detail_hide_infill(bool value)
|
||||||
|
{
|
||||||
|
m_impl->set_reduced_detail_hide_infill(value);
|
||||||
|
}
|
||||||
|
|
||||||
void Viewer::set_dim_previous_layers(bool value)
|
void Viewer::set_dim_previous_layers(bool value)
|
||||||
{
|
{
|
||||||
m_impl->set_dim_previous_layers(value);
|
m_impl->set_dim_previous_layers(value);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
#include <cfloat>
|
||||||
|
|
||||||
namespace libvgcode {
|
namespace libvgcode {
|
||||||
|
|
||||||
@@ -763,6 +764,8 @@ void ViewerImpl::init(const std::string& opengl_context_version)
|
|||||||
m_uni_segments_height_width_angle_tex_id = glGetUniformLocation(m_segments_shader_id, "height_width_angle_tex");
|
m_uni_segments_height_width_angle_tex_id = glGetUniformLocation(m_segments_shader_id, "height_width_angle_tex");
|
||||||
m_uni_segments_colors_tex_id = glGetUniformLocation(m_segments_shader_id, "color_tex");
|
m_uni_segments_colors_tex_id = glGetUniformLocation(m_segments_shader_id, "color_tex");
|
||||||
m_uni_segments_segment_index_tex_id = glGetUniformLocation(m_segments_shader_id, "segment_index_tex");
|
m_uni_segments_segment_index_tex_id = glGetUniformLocation(m_segments_shader_id, "segment_index_tex");
|
||||||
|
m_uni_segments_reverse_order_id = glGetUniformLocation(m_segments_shader_id, "reverse_order");
|
||||||
|
m_uni_segments_instance_count_id = glGetUniformLocation(m_segments_shader_id, "instance_count");
|
||||||
// ORCA: realistic view
|
// ORCA: realistic view
|
||||||
m_uni_segments_shadow_map_id = glGetUniformLocation(m_segments_shader_id, "shadow_map");
|
m_uni_segments_shadow_map_id = glGetUniformLocation(m_segments_shader_id, "shadow_map");
|
||||||
m_uni_segments_shadow_light_vp_id = glGetUniformLocation(m_segments_shader_id, "shadow_light_vp");
|
m_uni_segments_shadow_light_vp_id = glGetUniformLocation(m_segments_shader_id, "shadow_light_vp");
|
||||||
@@ -899,9 +902,17 @@ void ViewerImpl::reset()
|
|||||||
#else
|
#else
|
||||||
m_enabled_segments_count = 0;
|
m_enabled_segments_count = 0;
|
||||||
m_enabled_options_count = 0;
|
m_enabled_options_count = 0;
|
||||||
|
m_enabled_segments_reduced_count = 0;
|
||||||
|
m_enabled_options_reduced_count = 0;
|
||||||
|
m_enabled_segments_reduced_tex_size = 0;
|
||||||
|
m_enabled_options_reduced_tex_size = 0;
|
||||||
|
|
||||||
m_settings_used_for_ranges = std::nullopt;
|
m_settings_used_for_ranges = std::nullopt;
|
||||||
|
|
||||||
|
delete_textures(m_enabled_options_reduced_tex_id);
|
||||||
|
delete_buffers(m_enabled_options_reduced_buf_id);
|
||||||
|
delete_textures(m_enabled_segments_reduced_tex_id);
|
||||||
|
delete_buffers(m_enabled_segments_reduced_buf_id);
|
||||||
delete_textures(m_enabled_options_tex_id);
|
delete_textures(m_enabled_options_tex_id);
|
||||||
delete_buffers(m_enabled_options_buf_id);
|
delete_buffers(m_enabled_options_buf_id);
|
||||||
delete_textures(m_enabled_segments_tex_id);
|
delete_textures(m_enabled_segments_tex_id);
|
||||||
@@ -1161,6 +1172,17 @@ void ViewerImpl::load(GCodeInputData&& gcode_data)
|
|||||||
glsafe(glGenTextures(1, &m_enabled_options_tex_id));
|
glsafe(glGenTextures(1, &m_enabled_options_tex_id));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_tex_id));
|
||||||
|
|
||||||
|
// create (but do not fill) the reduced counterparts of the two buffers above
|
||||||
|
glsafe(glGenBuffers(1, &m_enabled_segments_reduced_buf_id));
|
||||||
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_buf_id));
|
||||||
|
glsafe(glGenTextures(1, &m_enabled_segments_reduced_tex_id));
|
||||||
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_tex_id));
|
||||||
|
|
||||||
|
glsafe(glGenBuffers(1, &m_enabled_options_reduced_buf_id));
|
||||||
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_options_reduced_buf_id));
|
||||||
|
glsafe(glGenTextures(1, &m_enabled_options_reduced_tex_id));
|
||||||
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_reduced_tex_id));
|
||||||
|
|
||||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, old_bound_texture));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, old_bound_texture));
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
@@ -1172,6 +1194,18 @@ void ViewerImpl::load(GCodeInputData&& gcode_data)
|
|||||||
update_colors();
|
update_colors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef ENABLE_OPENGL_ES
|
||||||
|
// the roles that lie under a skin or between walls, never seen from outside the print
|
||||||
|
static bool is_hidden_in_shell(EGCodeExtrusionRole role)
|
||||||
|
{
|
||||||
|
return role == EGCodeExtrusionRole::InternalInfill ||
|
||||||
|
role == EGCodeExtrusionRole::SolidInfill ||
|
||||||
|
role == EGCodeExtrusionRole::InternalBridgeInfill ||
|
||||||
|
role == EGCodeExtrusionRole::GapFill;
|
||||||
|
}
|
||||||
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
|
|
||||||
void ViewerImpl::update_enabled_entities()
|
void ViewerImpl::update_enabled_entities()
|
||||||
{
|
{
|
||||||
if (m_vertices.empty())
|
if (m_vertices.empty())
|
||||||
@@ -1179,6 +1213,17 @@ void ViewerImpl::update_enabled_entities()
|
|||||||
|
|
||||||
std::vector<uint32_t> enabled_segments;
|
std::vector<uint32_t> enabled_segments;
|
||||||
std::vector<uint32_t> enabled_options;
|
std::vector<uint32_t> enabled_options;
|
||||||
|
#ifndef ENABLE_OPENGL_ES
|
||||||
|
// the reduced set is filled by the same walk, so switching to it costs no rebuild. Whatever the
|
||||||
|
// mode leaves out, the bottom and top layers of the visible range are kept whole: they are the
|
||||||
|
// surfaces the range cuts open
|
||||||
|
const EReducedDetailMode reduced_mode = m_settings.reduced_detail_mode;
|
||||||
|
const bool build_reduced = reduced_mode != EReducedDetailMode::Off;
|
||||||
|
const bool hide_infill = m_settings.reduced_detail_hide_infill;
|
||||||
|
std::vector<uint32_t> enabled_segments_reduced;
|
||||||
|
std::vector<uint32_t> enabled_options_reduced;
|
||||||
|
const Interval& layers_range = m_layers.get_view_range();
|
||||||
|
#endif // ENABLE_OPENGL_ES
|
||||||
Interval range = m_view_range.get_visible();
|
Interval range = m_view_range.get_visible();
|
||||||
|
|
||||||
// when top layer only visualization is enabled, we need to render
|
// when top layer only visualization is enabled, we need to render
|
||||||
@@ -1226,6 +1271,20 @@ void ViewerImpl::update_enabled_entities()
|
|||||||
enabled_options.push_back(static_cast<uint32_t>(i));
|
enabled_options.push_back(static_cast<uint32_t>(i));
|
||||||
else
|
else
|
||||||
enabled_segments.push_back(static_cast<uint32_t>(i));
|
enabled_segments.push_back(static_cast<uint32_t>(i));
|
||||||
|
|
||||||
|
#ifndef ENABLE_OPENGL_ES
|
||||||
|
if (build_reduced) {
|
||||||
|
const bool end_layer = v.layer_id == layers_range[0] || v.layer_id == layers_range[1];
|
||||||
|
if (end_layer)
|
||||||
|
(v.is_option() ? enabled_options_reduced : enabled_segments_reduced).push_back(static_cast<uint32_t>(i));
|
||||||
|
else if (reduced_mode == EReducedDetailMode::ShellOnly) {
|
||||||
|
if (v.is_option())
|
||||||
|
enabled_options_reduced.push_back(static_cast<uint32_t>(i));
|
||||||
|
else if (!v.is_extrusion() || !hide_infill || !is_hidden_in_shell(v.role))
|
||||||
|
enabled_segments_reduced.push_back(static_cast<uint32_t>(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // ENABLE_OPENGL_ES
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_OPENGL_ES
|
#ifdef ENABLE_OPENGL_ES
|
||||||
@@ -1254,6 +1313,23 @@ void ViewerImpl::update_enabled_entities()
|
|||||||
else
|
else
|
||||||
glsafe(glBufferData(GL_TEXTURE_BUFFER, 0, nullptr, GL_STATIC_DRAW));
|
glsafe(glBufferData(GL_TEXTURE_BUFFER, 0, nullptr, GL_STATIC_DRAW));
|
||||||
|
|
||||||
|
m_enabled_segments_reduced_count = enabled_segments_reduced.size();
|
||||||
|
m_enabled_options_reduced_count = enabled_options_reduced.size();
|
||||||
|
m_enabled_segments_reduced_tex_size = enabled_segments_reduced.size() * sizeof(uint32_t);
|
||||||
|
m_enabled_options_reduced_tex_size = enabled_options_reduced.size() * sizeof(uint32_t);
|
||||||
|
|
||||||
|
// uploaded even when nothing was built, so that the last reduced set is released as soon as
|
||||||
|
// the preference is switched off
|
||||||
|
assert(m_enabled_segments_reduced_buf_id > 0);
|
||||||
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_buf_id));
|
||||||
|
glsafe(glBufferData(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_tex_size,
|
||||||
|
enabled_segments_reduced.empty() ? nullptr : enabled_segments_reduced.data(), GL_STATIC_DRAW));
|
||||||
|
|
||||||
|
assert(m_enabled_options_reduced_buf_id > 0);
|
||||||
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_options_reduced_buf_id));
|
||||||
|
glsafe(glBufferData(GL_TEXTURE_BUFFER, m_enabled_options_reduced_tex_size,
|
||||||
|
enabled_options_reduced.empty() ? nullptr : enabled_options_reduced.data(), GL_STATIC_DRAW));
|
||||||
|
|
||||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
@@ -1461,6 +1537,23 @@ void ViewerImpl::toggle_top_layer_only_view_range()
|
|||||||
update_colors_texture();
|
update_colors_texture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Both decide which vertices land in the reduced set, so the sets are rebuilt.
|
||||||
|
void ViewerImpl::set_reduced_detail_mode(EReducedDetailMode mode)
|
||||||
|
{
|
||||||
|
if (m_settings.reduced_detail_mode == mode)
|
||||||
|
return;
|
||||||
|
m_settings.reduced_detail_mode = mode;
|
||||||
|
m_settings.update_enabled_entities = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewerImpl::set_reduced_detail_hide_infill(bool value)
|
||||||
|
{
|
||||||
|
if (m_settings.reduced_detail_hide_infill == value)
|
||||||
|
return;
|
||||||
|
m_settings.reduced_detail_hide_infill = value;
|
||||||
|
m_settings.update_enabled_entities = true;
|
||||||
|
}
|
||||||
|
|
||||||
// ORCA: enable/disable darkening of the layers the layer slider is not scrubbed to
|
// ORCA: enable/disable darkening of the layers the layer slider is not scrubbed to
|
||||||
void ViewerImpl::set_dim_previous_layers(bool value)
|
void ViewerImpl::set_dim_previous_layers(bool value)
|
||||||
{
|
{
|
||||||
@@ -1854,6 +1947,8 @@ size_t ViewerImpl::get_used_gpu_memory() const
|
|||||||
ret += m_colors_tex_size;
|
ret += m_colors_tex_size;
|
||||||
ret += m_enabled_segments_tex_size;
|
ret += m_enabled_segments_tex_size;
|
||||||
ret += m_enabled_options_tex_size;
|
ret += m_enabled_options_tex_size;
|
||||||
|
ret += m_enabled_segments_reduced_tex_size;
|
||||||
|
ret += m_enabled_options_reduced_tex_size;
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -2070,7 +2165,8 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
|||||||
#ifdef ENABLE_OPENGL_ES
|
#ifdef ENABLE_OPENGL_ES
|
||||||
if (m_texture_data.get_enabled_segments_count() == 0)
|
if (m_texture_data.get_enabled_segments_count() == 0)
|
||||||
#else
|
#else
|
||||||
if (m_enabled_segments_count == 0)
|
const ActiveSet segments = active_segments();
|
||||||
|
if (segments.count == 0)
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -2090,6 +2186,15 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
|||||||
glsafe(glUniformMatrix4fv(m_uni_segments_view_matrix_id, 1, GL_FALSE, view_matrix.data()));
|
glsafe(glUniformMatrix4fv(m_uni_segments_view_matrix_id, 1, GL_FALSE, view_matrix.data()));
|
||||||
glsafe(glUniformMatrix4fv(m_uni_segments_projection_matrix_id, 1, GL_FALSE, projection_matrix.data()));
|
glsafe(glUniformMatrix4fv(m_uni_segments_projection_matrix_id, 1, GL_FALSE, projection_matrix.data()));
|
||||||
glsafe(glUniform3fv(m_uni_segments_camera_position_id, 1, camera_position.data()));
|
glsafe(glUniform3fv(m_uni_segments_camera_position_id, 1, camera_position.data()));
|
||||||
|
// The segments come in print order, bottom layer first. Seen from above, that is back to front,
|
||||||
|
// and every hidden fragment is shaded before the one that covers it. Drawing them last to first
|
||||||
|
// lets the depth test reject the hidden ones instead. The camera looks down when the world's
|
||||||
|
// up axis points towards it, which is the view matrix's (2, 2) entry being positive.
|
||||||
|
const bool top_down = !m_rendering_shadow_casters && view_matrix[10] > 0.0f;
|
||||||
|
glsafe(glUniform1i(m_uni_segments_reverse_order_id, top_down ? 1 : 0));
|
||||||
|
#ifndef ENABLE_OPENGL_ES
|
||||||
|
glsafe(glUniform1i(m_uni_segments_instance_count_id, static_cast<int>(segments.count)));
|
||||||
|
#endif // ENABLE_OPENGL_ES
|
||||||
// ORCA: realistic view. The depth pass writes the map it would otherwise read, so it shades
|
// ORCA: realistic view. The depth pass writes the map it would otherwise read, so it shades
|
||||||
// with the lookup off.
|
// with the lookup off.
|
||||||
glsafe(glUniform1i(m_uni_segments_shadow_map_id, m_shadow_map_texture_unit));
|
glsafe(glUniform1i(m_uni_segments_shadow_map_id, m_shadow_map_texture_unit));
|
||||||
@@ -2138,10 +2243,10 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
|||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
||||||
glsafe(glActiveTexture(GL_TEXTURE3));
|
glsafe(glActiveTexture(GL_TEXTURE3));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_segments_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, segments.tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, m_enabled_segments_buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, segments.buf_id));
|
||||||
|
|
||||||
m_segment_template.render(m_enabled_segments_count);
|
m_segment_template.render(segments.count);
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
if (curr_cull_face)
|
if (curr_cull_face)
|
||||||
@@ -2167,7 +2272,8 @@ void ViewerImpl::render_options(const Mat4x4& view_matrix, const Mat4x4& project
|
|||||||
#ifdef ENABLE_OPENGL_ES
|
#ifdef ENABLE_OPENGL_ES
|
||||||
if (m_texture_data.get_enabled_options_count() == 0)
|
if (m_texture_data.get_enabled_options_count() == 0)
|
||||||
#else
|
#else
|
||||||
if (m_enabled_options_count == 0)
|
const ActiveSet options = active_options();
|
||||||
|
if (options.count == 0)
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -2225,10 +2331,10 @@ void ViewerImpl::render_options(const Mat4x4& view_matrix, const Mat4x4& project
|
|||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
||||||
glsafe(glActiveTexture(GL_TEXTURE3));
|
glsafe(glActiveTexture(GL_TEXTURE3));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, options.tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, m_enabled_options_buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, options.buf_id));
|
||||||
|
|
||||||
m_option_template.render(m_enabled_options_count);
|
m_option_template.render(options.count);
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
if (!curr_cull_face)
|
if (!curr_cull_face)
|
||||||
|
|||||||
@@ -109,6 +109,21 @@ public:
|
|||||||
// 0.0 = black
|
// 0.0 = black
|
||||||
bool is_dim_previous_layers() const { return m_settings.dim_previous_layers; }
|
bool is_dim_previous_layers() const { return m_settings.dim_previous_layers; }
|
||||||
void set_dim_previous_layers(bool value);
|
void set_dim_previous_layers(bool value);
|
||||||
|
//
|
||||||
|
// Draw from the reduced set; it is already built, so this is just a buffer binding.
|
||||||
|
//
|
||||||
|
void set_reduced_detail(bool value) {
|
||||||
|
#ifdef ENABLE_OPENGL_ES
|
||||||
|
// no reduced set is built on OpenGL ES
|
||||||
|
value = false;
|
||||||
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
m_settings.reduced_detail = value;
|
||||||
|
}
|
||||||
|
bool is_reduced_detail() const { return m_settings.reduced_detail; }
|
||||||
|
EReducedDetailMode get_reduced_detail_mode() const { return m_settings.reduced_detail_mode; }
|
||||||
|
void set_reduced_detail_mode(EReducedDetailMode mode);
|
||||||
|
bool get_reduced_detail_hide_infill() const { return m_settings.reduced_detail_hide_infill; }
|
||||||
|
void set_reduced_detail_hide_infill(bool value);
|
||||||
float get_dim_previous_layers_brightness() const { return m_settings.dim_previous_layers_brightness; }
|
float get_dim_previous_layers_brightness() const { return m_settings.dim_previous_layers_brightness; }
|
||||||
void set_dim_previous_layers_brightness(float value);
|
void set_dim_previous_layers_brightness(float value);
|
||||||
|
|
||||||
@@ -362,6 +377,8 @@ private:
|
|||||||
int m_uni_segments_height_width_angle_tex_id{ -1 };
|
int m_uni_segments_height_width_angle_tex_id{ -1 };
|
||||||
int m_uni_segments_colors_tex_id{ -1 };
|
int m_uni_segments_colors_tex_id{ -1 };
|
||||||
int m_uni_segments_segment_index_tex_id{ -1 };
|
int m_uni_segments_segment_index_tex_id{ -1 };
|
||||||
|
int m_uni_segments_reverse_order_id{ -1 };
|
||||||
|
int m_uni_segments_instance_count_id{ -1 };
|
||||||
int m_uni_segments_shadow_map_id{ -1 };
|
int m_uni_segments_shadow_map_id{ -1 };
|
||||||
int m_uni_segments_shadow_light_vp_id{ -1 };
|
int m_uni_segments_shadow_light_vp_id{ -1 };
|
||||||
int m_uni_segments_shadow_intensity_id{ -1 };
|
int m_uni_segments_shadow_intensity_id{ -1 };
|
||||||
@@ -499,6 +516,15 @@ private:
|
|||||||
unsigned int m_enabled_options_tex_id{ 0 };
|
unsigned int m_enabled_options_tex_id{ 0 };
|
||||||
size_t m_enabled_options_count{ 0 };
|
size_t m_enabled_options_count{ 0 };
|
||||||
//
|
//
|
||||||
|
// OpenGL buffers to store the reduced set drawn while Settings::reduced_detail is set
|
||||||
|
//
|
||||||
|
unsigned int m_enabled_segments_reduced_buf_id{ 0 };
|
||||||
|
unsigned int m_enabled_segments_reduced_tex_id{ 0 };
|
||||||
|
size_t m_enabled_segments_reduced_count{ 0 };
|
||||||
|
unsigned int m_enabled_options_reduced_buf_id{ 0 };
|
||||||
|
unsigned int m_enabled_options_reduced_tex_id{ 0 };
|
||||||
|
size_t m_enabled_options_reduced_count{ 0 };
|
||||||
|
//
|
||||||
// Caches for size of data sent to gpu, in bytes
|
// Caches for size of data sent to gpu, in bytes
|
||||||
//
|
//
|
||||||
size_t m_positions_tex_size{ 0 };
|
size_t m_positions_tex_size{ 0 };
|
||||||
@@ -506,6 +532,27 @@ private:
|
|||||||
size_t m_colors_tex_size{ 0 };
|
size_t m_colors_tex_size{ 0 };
|
||||||
size_t m_enabled_segments_tex_size{ 0 };
|
size_t m_enabled_segments_tex_size{ 0 };
|
||||||
size_t m_enabled_options_tex_size{ 0 };
|
size_t m_enabled_options_tex_size{ 0 };
|
||||||
|
size_t m_enabled_segments_reduced_tex_size{ 0 };
|
||||||
|
size_t m_enabled_options_reduced_tex_size{ 0 };
|
||||||
|
|
||||||
|
// The set the next draw reads from: the reduced one while dragging, if one is built.
|
||||||
|
bool use_reduced_set() const { return m_settings.reduced_detail && m_settings.reduced_detail_mode != EReducedDetailMode::Off; }
|
||||||
|
struct ActiveSet
|
||||||
|
{
|
||||||
|
size_t count{ 0 };
|
||||||
|
unsigned int buf_id{ 0 };
|
||||||
|
unsigned int tex_id{ 0 };
|
||||||
|
};
|
||||||
|
ActiveSet active_segments() const {
|
||||||
|
if (use_reduced_set())
|
||||||
|
return { m_enabled_segments_reduced_count, m_enabled_segments_reduced_buf_id, m_enabled_segments_reduced_tex_id };
|
||||||
|
return { m_enabled_segments_count, m_enabled_segments_buf_id, m_enabled_segments_tex_id };
|
||||||
|
}
|
||||||
|
ActiveSet active_options() const {
|
||||||
|
if (use_reduced_set())
|
||||||
|
return { m_enabled_options_reduced_count, m_enabled_options_reduced_buf_id, m_enabled_options_reduced_tex_id };
|
||||||
|
return { m_enabled_options_count, m_enabled_options_buf_id, m_enabled_options_tex_id };
|
||||||
|
}
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1267,6 +1267,8 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
|
|||||||
if (current_top_layer_only != required_top_layer_only)
|
if (current_top_layer_only != required_top_layer_only)
|
||||||
m_viewer.toggle_top_layer_only_view_range();
|
m_viewer.toggle_top_layer_only_view_range();
|
||||||
|
|
||||||
|
read_reduced_detail_preferences();
|
||||||
|
|
||||||
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
||||||
m_viewer.set_dim_previous_layers(get_app_config()->get_bool("preview_dim_previous_layers"));
|
m_viewer.set_dim_previous_layers(get_app_config()->get_bool("preview_dim_previous_layers"));
|
||||||
m_viewer.set_dim_previous_layers_brightness(0.01f * std::stoi(get_app_config()->get("preview_dim_previous_layers_brightness")));
|
m_viewer.set_dim_previous_layers_brightness(0.01f * std::stoi(get_app_config()->get("preview_dim_previous_layers_brightness")));
|
||||||
@@ -1490,6 +1492,10 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
|
|||||||
|
|
||||||
// BBS: data for rendering color arrangement recommendation
|
// BBS: data for rendering color arrangement recommendation
|
||||||
m_nozzle_nums = print.config().option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
m_nozzle_nums = print.config().option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
||||||
|
// the shell drag mode hides the infill only where the profile covers it: with no top or bottom
|
||||||
|
// shell, or no walls, the infill is the surface. Per-object settings are not consulted.
|
||||||
|
const PrintRegionConfig& region = print.default_region_config();
|
||||||
|
m_viewer.set_reduced_detail_hide_infill(region.top_shell_layers.value > 0 && region.bottom_shell_layers.value > 0 && region.wall_loops.value > 0);
|
||||||
// Orca hack: Hide filament group for non-bbl printers
|
// Orca hack: Hide filament group for non-bbl printers
|
||||||
if (!print.is_BBL_printer()) m_nozzle_nums = 1;
|
if (!print.is_BBL_printer()) m_nozzle_nums = 1;
|
||||||
std::vector<int> filament_maps = print.get_filament_maps();
|
std::vector<int> filament_maps = print.get_filament_maps();
|
||||||
@@ -1660,6 +1666,7 @@ void GCodeViewer::reset_shell()
|
|||||||
{
|
{
|
||||||
m_shells.volumes.clear();
|
m_shells.volumes.clear();
|
||||||
m_shells.print_id = -1;
|
m_shells.print_id = -1;
|
||||||
|
m_shells.with_wipe_tower = false;
|
||||||
m_shell_bounding_box = BoundingBoxf3();
|
m_shell_bounding_box = BoundingBoxf3();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1696,6 +1703,11 @@ void GCodeViewer::reset()
|
|||||||
void GCodeViewer::render_scene(int canvas_width, int canvas_height)
|
void GCodeViewer::render_scene(int canvas_width, int canvas_height)
|
||||||
{
|
{
|
||||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||||
|
// while dragging in the solid model mode, the objects stand in for their toolpaths, cut to the
|
||||||
|
// visible layer range; the toolpath set then holds only the range's bottom and top layers
|
||||||
|
if (m_viewer.is_reduced_detail() && solid_model_enabled())
|
||||||
|
render_solid_model(canvas_width, canvas_height);
|
||||||
|
else
|
||||||
render_shells(canvas_width, canvas_height);
|
render_shells(canvas_width, canvas_height);
|
||||||
|
|
||||||
if (m_viewer.get_extrusion_roles_count() == 0)
|
if (m_viewer.get_extrusion_roles_count() == 0)
|
||||||
@@ -2019,6 +2031,56 @@ void GCodeViewer::update_layers_slider_mode()
|
|||||||
// TODO m_layers_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder);
|
// TODO m_layers_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GCodeViewer::set_interacting(bool interacting)
|
||||||
|
{
|
||||||
|
// with no shells to stand in for the toolpaths, the solid model would leave only the end layers
|
||||||
|
const bool usable = !solid_model_enabled() || !m_shells.volumes.empty();
|
||||||
|
m_viewer.set_reduced_detail(interacting && usable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodeViewer::read_reduced_detail_preferences()
|
||||||
|
{
|
||||||
|
m_reduced_detail_mode = reduced_detail_mode_from_string(get_app_config()->get("preview_reduced_detail_mode"));
|
||||||
|
apply_reduced_detail_settings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodeViewer::apply_reduced_detail_settings()
|
||||||
|
{
|
||||||
|
m_viewer.set_reduced_detail_mode(m_reduced_detail_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodeViewer::set_reduced_detail_mode(const std::string& mode)
|
||||||
|
{
|
||||||
|
const bool was_solid = solid_model_enabled();
|
||||||
|
m_reduced_detail_mode = reduced_detail_mode_from_string(mode);
|
||||||
|
apply_reduced_detail_settings();
|
||||||
|
reload_shells_if_solid_model_changed(was_solid);
|
||||||
|
}
|
||||||
|
|
||||||
|
libvgcode::EReducedDetailMode GCodeViewer::reduced_detail_mode_from_string(const std::string& mode)
|
||||||
|
{
|
||||||
|
if (mode == "solid")
|
||||||
|
return libvgcode::EReducedDetailMode::EndLayersOnly;
|
||||||
|
if (mode == "shell")
|
||||||
|
return libvgcode::EReducedDetailMode::ShellOnly;
|
||||||
|
return libvgcode::EReducedDetailMode::Off;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodeViewer::reload_shells_if_solid_model_changed(bool was_enabled)
|
||||||
|
{
|
||||||
|
if (was_enabled == solid_model_enabled() || m_shells.print_id == -1)
|
||||||
|
return;
|
||||||
|
// only the prime tower comes and goes with the mode: a full reload would drop the shells
|
||||||
|
// whenever the print has moved on since they were loaded, leaving the solid model nothing to draw
|
||||||
|
if (wxGetApp().plater() == nullptr)
|
||||||
|
return;
|
||||||
|
// the shells are loaded from the current plate's print, which is not the plater's own
|
||||||
|
const Print& print = wxGetApp().plater()->get_partplate_list().get_current_fff_print();
|
||||||
|
if (static_cast<int>(print.id().id) != m_shells.print_id)
|
||||||
|
return;
|
||||||
|
update_shell_wipe_tower(print, m_gl_data_initialized);
|
||||||
|
}
|
||||||
|
|
||||||
void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range)
|
void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range)
|
||||||
{
|
{
|
||||||
m_viewer.set_layers_view_range(static_cast<uint32_t>(layers_z_range[0]), static_cast<uint32_t>(layers_z_range[1]));
|
m_viewer.set_layers_view_range(static_cast<uint32_t>(layers_z_range[0]), static_cast<uint32_t>(layers_z_range[1]));
|
||||||
@@ -2343,7 +2405,11 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
|
|||||||
void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_previewing)
|
void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_previewing)
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": initialized=%1%, force_previewing=%2%")%initialized %force_previewing;
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": initialized=%1%, force_previewing=%2%")%initialized %force_previewing;
|
||||||
|
// the shells can load before the first G-code does, so the preferences are read here as well
|
||||||
|
read_reduced_detail_preferences();
|
||||||
if ((print.id().id == m_shells.print_id)&&(print.get_modified_count() == m_shells.print_modify_count)) {
|
if ((print.id().id == m_shells.print_id)&&(print.get_modified_count() == m_shells.print_modify_count)) {
|
||||||
|
// the prime tower comes and goes on its own, without reloading the objects
|
||||||
|
update_shell_wipe_tower(print, initialized);
|
||||||
//BBS: update force previewing logic
|
//BBS: update force previewing logic
|
||||||
if (force_previewing)
|
if (force_previewing)
|
||||||
m_shells.previewing = force_previewing;
|
m_shells.previewing = force_previewing;
|
||||||
@@ -2452,10 +2518,45 @@ void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_p
|
|||||||
m_shells.print_id = print.id().id;
|
m_shells.print_id = print.id().id;
|
||||||
m_shells.print_modify_count = print.get_modified_count();
|
m_shells.print_modify_count = print.get_modified_count();
|
||||||
m_shells.previewing = true;
|
m_shells.previewing = true;
|
||||||
|
update_shell_wipe_tower(print, initialized);
|
||||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": shell loaded, id change to %1%, modify_count %2%, object count %3%, glvolume count %4%")
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": shell loaded, id change to %1%, modify_count %2%, object count %3%, glvolume count %4%")
|
||||||
% m_shells.print_id % m_shells.print_modify_count % object_count %m_shells.volumes.volumes.size();
|
% m_shells.print_id % m_shells.print_modify_count % object_count %m_shells.volumes.volumes.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The prime tower as it was sliced, so that the solid model shows what the print shows. It keeps its
|
||||||
|
// opaque colour, so it never appears among the translucent shells, and stays out of their bounding box.
|
||||||
|
void GCodeViewer::update_shell_wipe_tower(const Print& print, bool initialized)
|
||||||
|
{
|
||||||
|
const bool with_wipe_tower = solid_model_enabled() && print.is_step_done(psWipeTower) && print.wipe_tower_data().wipe_tower_mesh_data;
|
||||||
|
if (with_wipe_tower == m_shells.with_wipe_tower)
|
||||||
|
return;
|
||||||
|
m_shells.with_wipe_tower = with_wipe_tower;
|
||||||
|
GLVolumePtrs& volumes = m_shells.volumes.volumes;
|
||||||
|
if (!with_wipe_tower) {
|
||||||
|
volumes.erase(std::remove_if(volumes.begin(), volumes.end(), [](GLVolume* volume) {
|
||||||
|
if (!volume->is_wipe_tower)
|
||||||
|
return false;
|
||||||
|
delete volume;
|
||||||
|
return true;
|
||||||
|
}), volumes.end());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const PrintConfig& config = print.config();
|
||||||
|
const int plate_idx = print.get_plate_index();
|
||||||
|
const Vec3d plate_origin = print.get_plate_origin();
|
||||||
|
const float x = static_cast<float>(config.wipe_tower_x.get_at(plate_idx) + plate_origin.x());
|
||||||
|
const float y = static_cast<float>(config.wipe_tower_y.get_at(plate_idx) + plate_origin.y());
|
||||||
|
const size_t first_new = volumes.size();
|
||||||
|
m_shells.volumes.load_real_wipe_tower_preview(1000 + plate_idx, x, y, print.wipe_tower_data().wipe_tower_mesh_data->real_wipe_tower_mesh,
|
||||||
|
print.wipe_tower_data().wipe_tower_mesh_data->real_brim_mesh, true,
|
||||||
|
static_cast<float>(config.wipe_tower_rotation_angle), false, initialized);
|
||||||
|
for (size_t i = first_new; i < volumes.size(); ++i) {
|
||||||
|
volumes[i]->zoom_to_volumes = false;
|
||||||
|
volumes[i]->force_native_color = true;
|
||||||
|
volumes[i]->set_render_color();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GCodeViewer::render_toolpaths()
|
void GCodeViewer::render_toolpaths()
|
||||||
{
|
{
|
||||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||||
@@ -2656,6 +2757,50 @@ void GCodeViewer::render_shells(int canvas_width, int canvas_height)
|
|||||||
glsafe(::glDepthMask(GL_TRUE));
|
glsafe(::glDepthMask(GL_TRUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The sliced objects and the prime tower drawn opaque, in their filament colours, cut to the
|
||||||
|
// visible layer range by the shader's z range. The toolpaths of the range's bottom and top layers
|
||||||
|
// are drawn afterwards and cap the cut.
|
||||||
|
void GCodeViewer::render_solid_model(int canvas_width, int canvas_height)
|
||||||
|
{
|
||||||
|
if (m_shells.volumes.empty())
|
||||||
|
return;
|
||||||
|
// gouraud_light has no z range, so it could not cut the model
|
||||||
|
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
|
||||||
|
if (shader == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const libvgcode::Interval& layers = m_viewer.get_layers_view_range();
|
||||||
|
const float z_top = m_viewer.get_layer_z(layers[1]) - m_z_offset + 0.001f;
|
||||||
|
const float z_bottom = (layers[0] > 0) ? m_viewer.get_layer_z(layers[0] - 1) - m_z_offset - 0.001f : -FLT_MAX;
|
||||||
|
|
||||||
|
std::vector<float> alphas;
|
||||||
|
alphas.reserve(m_shells.volumes.volumes.size());
|
||||||
|
for (GLVolume* volume : m_shells.volumes.volumes) {
|
||||||
|
alphas.push_back(volume->color.a());
|
||||||
|
volume->color.a(1.0f);
|
||||||
|
volume->set_render_color();
|
||||||
|
}
|
||||||
|
m_shells.volumes.set_z_range(z_bottom, z_top);
|
||||||
|
// gouraud also clips by this plane, which nothing else sets on the shells
|
||||||
|
m_shells.volumes.set_clipping_plane(ClippingPlane::ClipsNothing().get_data());
|
||||||
|
|
||||||
|
shader->start_using();
|
||||||
|
// the 3D view leaves its shadow settings on the shared program
|
||||||
|
shader->set_uniform("shadow_intensity", 0.0f);
|
||||||
|
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||||
|
shader->set_uniform("z_far", camera.get_far_z());
|
||||||
|
shader->set_uniform("z_near", camera.get_near_z());
|
||||||
|
m_shells.volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix(), {canvas_width, canvas_height});
|
||||||
|
shader->stop_using();
|
||||||
|
|
||||||
|
m_shells.volumes.set_z_range(-FLT_MAX, FLT_MAX);
|
||||||
|
size_t k = 0;
|
||||||
|
for (GLVolume* volume : m_shells.volumes.volumes) {
|
||||||
|
volume->color.a(alphas[k++]);
|
||||||
|
volume->set_render_color();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//BBS
|
//BBS
|
||||||
void GCodeViewer::render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show /*= true*/) const {
|
void GCodeViewer::render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show /*= true*/) const {
|
||||||
if (!show)
|
if (!show)
|
||||||
|
|||||||
@@ -174,6 +174,8 @@ public:
|
|||||||
int print_id{-1};
|
int print_id{-1};
|
||||||
int print_modify_count{-1};
|
int print_modify_count{-1};
|
||||||
bool previewing{false};
|
bool previewing{false};
|
||||||
|
// the prime tower was loaded with the objects, for the solid model
|
||||||
|
bool with_wipe_tower{false};
|
||||||
};
|
};
|
||||||
//BBS
|
//BBS
|
||||||
ConflictResultOpt m_conflict_result;
|
ConflictResultOpt m_conflict_result;
|
||||||
@@ -234,6 +236,17 @@ private:
|
|||||||
|
|
||||||
bool m_legend_visible{ true };
|
bool m_legend_visible{ true };
|
||||||
bool m_legend_enabled{ true };
|
bool m_legend_enabled{ true };
|
||||||
|
// the reduced-detail preferences, pushed to libvgcode by apply_reduced_detail_settings()
|
||||||
|
libvgcode::EReducedDetailMode m_reduced_detail_mode{ libvgcode::EReducedDetailMode::Off };
|
||||||
|
void read_reduced_detail_preferences();
|
||||||
|
void apply_reduced_detail_settings();
|
||||||
|
static libvgcode::EReducedDetailMode reduced_detail_mode_from_string(const std::string& mode);
|
||||||
|
// in the solid model mode, the sliced objects are drawn as solid shapes instead of toolpaths
|
||||||
|
bool solid_model_enabled() const { return m_reduced_detail_mode == libvgcode::EReducedDetailMode::EndLayersOnly; }
|
||||||
|
void render_solid_model(int canvas_width, int canvas_height);
|
||||||
|
// the prime tower is only among the shells for the solid model, so it is added or removed when that changes
|
||||||
|
void reload_shells_if_solid_model_changed(bool was_enabled);
|
||||||
|
void update_shell_wipe_tower(const Print& print, bool initialized);
|
||||||
|
|
||||||
float m_legend_height;
|
float m_legend_height;
|
||||||
PrintEstimatedStatistics m_print_statistics;
|
PrintEstimatedStatistics m_print_statistics;
|
||||||
@@ -291,7 +304,7 @@ public:
|
|||||||
// void _render_calibration_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
// 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);
|
// void _render_calibration_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||||
// void render_calibration_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
// void render_calibration_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||||
bool has_data() const { return !m_viewer.get_extrusion_roles().empty(); }
|
bool has_data() const { return m_viewer.get_extrusion_roles_count() != 0; }
|
||||||
|
|
||||||
bool can_export_toolpaths() const;
|
bool can_export_toolpaths() const;
|
||||||
std::vector<int> get_plater_extruder();
|
std::vector<int> get_plater_extruder();
|
||||||
@@ -363,6 +376,14 @@ public:
|
|||||||
void set_dim_previous_layers_brightness(float value) { m_viewer.set_dim_previous_layers_brightness(value); }
|
void set_dim_previous_layers_brightness(float value) { m_viewer.set_dim_previous_layers_brightness(value); }
|
||||||
float get_dim_previous_layers_brightness() const { return m_viewer.get_dim_previous_layers_brightness(); }
|
float get_dim_previous_layers_brightness() const { return m_viewer.get_dim_previous_layers_brightness(); }
|
||||||
|
|
||||||
|
// whether the mouse is holding either slider's handle
|
||||||
|
bool is_slider_dragging() const { return m_layers_slider->is_dragging() || m_moves_slider->is_dragging(); }
|
||||||
|
// while the user drags the camera or a slider, draw the reduced set, if the preference asks for one
|
||||||
|
void set_interacting(bool interacting);
|
||||||
|
bool is_reduced_detail() const { return m_viewer.is_reduced_detail(); }
|
||||||
|
// the preference's string value: "off", "solid" or "shell"
|
||||||
|
void set_reduced_detail_mode(const std::string& mode);
|
||||||
|
|
||||||
void set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range);
|
void set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range);
|
||||||
|
|
||||||
bool is_legend_shown() const { return m_legend_visible && m_legend_enabled; }
|
bool is_legend_shown() const { return m_legend_visible && m_legend_enabled; }
|
||||||
|
|||||||
@@ -2084,6 +2084,11 @@ void GLCanvas3D::_render_frame(bool scene_dirty, bool only_init)
|
|||||||
const bool overlay_tick = m_fps_overlay_tick;
|
const bool overlay_tick = m_fps_overlay_tick;
|
||||||
m_fps_overlay_tick = false;
|
m_fps_overlay_tick = false;
|
||||||
|
|
||||||
|
// Whether the preview draws its reduced set is decided before the cached scene is consulted,
|
||||||
|
// since switching changes what the scene pass draws.
|
||||||
|
if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview && m_gcode_viewer.has_data() && _update_preview_interaction())
|
||||||
|
scene_dirty = true;
|
||||||
|
|
||||||
// An overlay-only frame reuses the last scene pass. The overlay is rebuilt either way, and drawn
|
// 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.
|
// 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);
|
const bool reuse_scene = !scene_dirty && _can_reuse_cached_scene(camera);
|
||||||
@@ -3277,9 +3282,16 @@ void GLCanvas3D::bind_event_handlers()
|
|||||||
if (m_selection_edit.kind != SelectionEdit::None)
|
if (m_selection_edit.kind != SelectionEdit::None)
|
||||||
finish_selection_edit();
|
finish_selection_edit();
|
||||||
ImGui::SetWindowFocus(nullptr);
|
ImGui::SetWindowFocus(nullptr);
|
||||||
|
// a drag cut short never sees its button release, which would leave the reduced set drawn
|
||||||
|
if (m_canvas_type == CanvasPreview && m_mouse.dragging && m_gcode_viewer.is_reduced_detail())
|
||||||
|
mouse_up_cleanup();
|
||||||
render();
|
render();
|
||||||
evt.Skip();
|
evt.Skip();
|
||||||
});
|
});
|
||||||
|
m_canvas->Bind(wxEVT_MOUSE_CAPTURE_LOST, [this](wxMouseCaptureLostEvent&) {
|
||||||
|
if (m_canvas_type == CanvasPreview && m_mouse.dragging && m_gcode_viewer.is_reduced_detail())
|
||||||
|
mouse_up_cleanup();
|
||||||
|
});
|
||||||
m_event_handlers_bound = true;
|
m_event_handlers_bound = true;
|
||||||
|
|
||||||
m_canvas->Bind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
|
m_canvas->Bind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
|
||||||
@@ -3355,6 +3367,17 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
|||||||
m_overlay_dirty |= imgui_requires_extra_frame;
|
m_overlay_dirty |= imgui_requires_extra_frame;
|
||||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||||
m_dirty |= GLTexture::Compressor::has_compressed_texture_to_refresh();
|
m_dirty |= GLTexture::Compressor::has_compressed_texture_to_refresh();
|
||||||
|
// the render timer only wakes the idle loop; the frame that puts the preview's toolpaths back
|
||||||
|
// after a wheel burst has to be asked for here, once the settle time is really up
|
||||||
|
if (m_preview_settle_pending) {
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
if (now >= m_preview_interaction_until) {
|
||||||
|
m_preview_settle_pending = false;
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
|
else // the timer fired early
|
||||||
|
schedule_extra_frame(static_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(m_preview_interaction_until - now).count()) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_dirty && !m_overlay_dirty)
|
if (!m_dirty && !m_overlay_dirty)
|
||||||
return;
|
return;
|
||||||
@@ -3885,6 +3908,10 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only a wheel the panels did not take moves the camera
|
||||||
|
if (m_canvas_type == CanvasPreview)
|
||||||
|
note_preview_interaction();
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
// For some reason the Idle event is not being generated after the mouse scroll event in case of scrolling with the two fingers on the touch pad,
|
// For some reason the Idle event is not being generated after the mouse scroll event in case of scrolling with the two fingers on the touch pad,
|
||||||
// if the event is not allowed to be passed further.
|
// if the event is not allowed to be passed further.
|
||||||
@@ -3985,6 +4012,11 @@ void GLCanvas3D::on_fps_overlay_timer(wxTimerEvent& evt)
|
|||||||
wxWakeUpIdle();
|
wxWakeUpIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLCanvas3D::note_preview_interaction()
|
||||||
|
{
|
||||||
|
m_preview_interaction_until = std::chrono::steady_clock::now() + std::chrono::milliseconds(150);
|
||||||
|
}
|
||||||
|
|
||||||
void GLCanvas3D::schedule_extra_frame(int milliseconds)
|
void GLCanvas3D::schedule_extra_frame(int milliseconds)
|
||||||
{
|
{
|
||||||
// Schedule idle event right now
|
// Schedule idle event right now
|
||||||
@@ -5631,6 +5663,9 @@ void GLCanvas3D::mouse_up_cleanup()
|
|||||||
m_mouse.ignore_left_up = false;
|
m_mouse.ignore_left_up = false;
|
||||||
m_mouse.ignore_right_up = false;
|
m_mouse.ignore_right_up = false;
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
|
// the frame that follows a release puts the preview's toolpaths back, and on some platforms
|
||||||
|
// no idle event follows a button release until the next input
|
||||||
|
wxWakeUpIdle();
|
||||||
|
|
||||||
if (m_canvas->HasCapture())
|
if (m_canvas->HasCapture())
|
||||||
m_canvas->ReleaseMouse();
|
m_canvas->ReleaseMouse();
|
||||||
@@ -7779,13 +7814,20 @@ bool GLCanvas3D::_is_scene_cacheable() const
|
|||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The scene follows the cursor during a drag, under a gizmo that draws at the cursor, and while
|
// The scene follows the cursor while the user drags, under a gizmo that draws at the cursor, and
|
||||||
// the cursor is on the layer height bar, where the object shader draws a band at its height.
|
// 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 GLGizmoBase* gizmo = m_gizmos.get_current();
|
||||||
const bool cursor_on_layers_bar = is_layers_editing_enabled() &&
|
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());
|
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() &&
|
return !is_user_interacting() && (gizmo == nullptr || !gizmo->render_follows_cursor()) && !cursor_on_layers_bar;
|
||||||
(gizmo == nullptr || !gizmo->render_follows_cursor()) && !cursor_on_layers_bar;
|
}
|
||||||
|
|
||||||
|
// Whether the user is holding something that moves the scene: the camera, the navigator, a gizmo,
|
||||||
|
// the rectangle selection or a preview slider.
|
||||||
|
bool GLCanvas3D::is_user_interacting() const
|
||||||
|
{
|
||||||
|
return m_mouse.dragging || m_navigator_dragging || m_gizmos.is_dragging() || m_rectangle_selection.is_dragging() ||
|
||||||
|
m_gcode_viewer.is_slider_dragging();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLCanvas3D::_is_frame_skipping_enabled() const
|
bool GLCanvas3D::_is_frame_skipping_enabled() const
|
||||||
@@ -8776,6 +8818,24 @@ void GLCanvas3D::_render_wireframe_overlay()
|
|||||||
shader->stop_using();
|
shader->stop_using();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The reduced set is drawn while the camera, the navigator or either slider is dragged. A wheel
|
||||||
|
// step has no duration, so it holds the reduced set for a settle time instead, and the frame that
|
||||||
|
// restores the full toolpaths is scheduled for when that time runs out. Returns whether what the scene
|
||||||
|
// pass draws changed, since a frame that reuses the cached scene would hide the change.
|
||||||
|
bool GLCanvas3D::_update_preview_interaction()
|
||||||
|
{
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
const bool settling = now < m_preview_interaction_until;
|
||||||
|
const bool dragging = is_user_interacting();
|
||||||
|
const bool was_reduced = m_gcode_viewer.is_reduced_detail();
|
||||||
|
m_gcode_viewer.set_interacting(dragging || settling);
|
||||||
|
if (settling && !dragging && m_gcode_viewer.is_reduced_detail()) {
|
||||||
|
m_preview_settle_pending = true;
|
||||||
|
schedule_extra_frame(static_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(m_preview_interaction_until - now).count()) + 1);
|
||||||
|
}
|
||||||
|
return m_gcode_viewer.is_reduced_detail() != was_reduced;
|
||||||
|
}
|
||||||
|
|
||||||
//BBS: GUI refactor: add canvas size as parameters
|
//BBS: GUI refactor: add canvas size as parameters
|
||||||
void GLCanvas3D::_render_gcode(int canvas_width, int canvas_height)
|
void GLCanvas3D::_render_gcode(int canvas_width, int canvas_height)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -656,6 +656,10 @@ private:
|
|||||||
ECursorType m_cursor_type;
|
ECursorType m_cursor_type;
|
||||||
GLSelectionRectangle m_rectangle_selection;
|
GLSelectionRectangle m_rectangle_selection;
|
||||||
bool m_navigator_dragging{ false };
|
bool m_navigator_dragging{ false };
|
||||||
|
// until when a wheel step keeps the preview's reduced set drawn
|
||||||
|
std::chrono::time_point<std::chrono::steady_clock> m_preview_interaction_until{};
|
||||||
|
// whether the frame that restores the toolpaths once that time is up is still owed
|
||||||
|
bool m_preview_settle_pending{ false };
|
||||||
|
|
||||||
//BBS:add plate related logic
|
//BBS:add plate related logic
|
||||||
mutable std::vector<int> m_hover_volume_idxs;
|
mutable std::vector<int> m_hover_volume_idxs;
|
||||||
@@ -1222,6 +1226,10 @@ public:
|
|||||||
void msw_rescale() { m_gcode_viewer.invalidate_legend(); }
|
void msw_rescale() { m_gcode_viewer.invalidate_legend(); }
|
||||||
|
|
||||||
void request_extra_frame() { m_extra_frame_requested = true; }
|
void request_extra_frame() { m_extra_frame_requested = true; }
|
||||||
|
// whether the user is holding the camera, the navigator, a gizmo, the rectangle selection or a preview slider
|
||||||
|
bool is_user_interacting() const;
|
||||||
|
// a wheel step is over before the next frame, so it holds the preview's reduced set for a settle time
|
||||||
|
void note_preview_interaction();
|
||||||
|
|
||||||
void schedule_extra_frame(int milliseconds);
|
void schedule_extra_frame(int milliseconds);
|
||||||
|
|
||||||
@@ -1372,6 +1380,9 @@ private:
|
|||||||
//BBS: GUI refactor: add canvas size as parameters
|
//BBS: GUI refactor: add canvas size as parameters
|
||||||
void _render_gcode(int canvas_width, int canvas_height);
|
void _render_gcode(int canvas_width, int canvas_height);
|
||||||
void _render_gcode_overlay(int canvas_width, int canvas_height);
|
void _render_gcode_overlay(int canvas_width, int canvas_height);
|
||||||
|
// decides whether the preview draws its reduced set this frame and returns whether what the scene
|
||||||
|
// pass draws changed; runs before the cached scene is consulted
|
||||||
|
bool _update_preview_interaction();
|
||||||
//BBS: render a plane for assemble
|
//BBS: render a plane for assemble
|
||||||
void _render_plane() const;
|
void _render_plane() const;
|
||||||
void _render_selection();
|
void _render_selection();
|
||||||
|
|||||||
@@ -483,6 +483,11 @@ void IMSlider::draw_background_and_groove(const ImRect& bg_rect, const ImRect& g
|
|||||||
ImGui::RenderFrame(groove.Min, groove.Max, groove_col, false, 0.5 * groove.GetWidth());
|
ImGui::RenderFrame(groove.Min, groove.Max, groove_col, false, 0.5 * groove.GetWidth());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IMSlider::is_dragging() const
|
||||||
|
{
|
||||||
|
return GImGui != nullptr && m_imgui_id != 0 && GImGui->ActiveId == m_imgui_id && GImGui->IO.MouseDown[0];
|
||||||
|
}
|
||||||
|
|
||||||
bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int v_max, const ImVec2& size, float scale)
|
bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int v_max, const ImVec2& size, float scale)
|
||||||
{
|
{
|
||||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||||
@@ -491,6 +496,7 @@ bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int
|
|||||||
|
|
||||||
ImGuiContext& context = *GImGui;
|
ImGuiContext& context = *GImGui;
|
||||||
const ImGuiID id = window->GetID(str_id);
|
const ImGuiID id = window->GetID(str_id);
|
||||||
|
m_imgui_id = id;
|
||||||
|
|
||||||
const ImVec2 pos = window->DC.CursorPos;
|
const ImVec2 pos = window->DC.CursorPos;
|
||||||
const ImRect draw_region(pos, pos + size);
|
const ImRect draw_region(pos, pos + size);
|
||||||
@@ -883,6 +889,7 @@ bool IMSlider::vertical_slider(const char* str_id, int* higher_value, int* lower
|
|||||||
|
|
||||||
ImGuiContext& context = *GImGui;
|
ImGuiContext& context = *GImGui;
|
||||||
const ImGuiID id = window->GetID(str_id);
|
const ImGuiID id = window->GetID(str_id);
|
||||||
|
m_imgui_id = id;
|
||||||
|
|
||||||
const ImVec2 pos = window->DC.CursorPos;
|
const ImVec2 pos = window->DC.CursorPos;
|
||||||
const ImRect draw_region(pos, pos + size);
|
const ImRect draw_region(pos, pos + size);
|
||||||
|
|||||||
@@ -118,6 +118,9 @@ public:
|
|||||||
|
|
||||||
//BBS update scroll value changed
|
//BBS update scroll value changed
|
||||||
bool is_dirty() { return m_dirty; }
|
bool is_dirty() { return m_dirty; }
|
||||||
|
// whether the mouse is holding this slider's handle, read from ImGui's active id rather than
|
||||||
|
// from the dirty flag, which is raised and consumed inside a single frame
|
||||||
|
bool is_dragging() const;
|
||||||
void set_as_dirty(bool dirty = true) { m_dirty = dirty; }
|
void set_as_dirty(bool dirty = true) { m_dirty = dirty; }
|
||||||
bool is_need_post_tick_event() { return m_is_need_post_tick_changed_event; }
|
bool is_need_post_tick_event() { return m_is_need_post_tick_changed_event; }
|
||||||
void reset_post_tick_event(bool val = false) {
|
void reset_post_tick_event(bool val = false) {
|
||||||
@@ -182,6 +185,8 @@ private:
|
|||||||
int m_higher_value;
|
int m_higher_value;
|
||||||
int m_one_layer_value; // ORCA
|
int m_one_layer_value; // ORCA
|
||||||
bool m_dirty = false;
|
bool m_dirty = false;
|
||||||
|
// the ImGui id of the slider widget, as of its last render
|
||||||
|
unsigned int m_imgui_id = 0;
|
||||||
|
|
||||||
bool m_render_as_disabled{ false };
|
bool m_render_as_disabled{ false };
|
||||||
|
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ wxBoxSizer* PreferencesDialog::create_item_combobox(wxString title, wxString too
|
|||||||
return sizer;
|
return sizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, const wxString wiki_url)
|
wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, std::function<void(std::string)> onchange, const wxString wiki_url)
|
||||||
{
|
{
|
||||||
assert(vlist.size() == config_name_index.size());
|
assert(vlist.size() == config_name_index.size());
|
||||||
unsigned int current_index = 0;
|
unsigned int current_index = 0;
|
||||||
@@ -338,8 +338,9 @@ wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString too
|
|||||||
auto [sizer, combobox] = create_item_combobox_base(title, tooltip, param, vlist, current_index);
|
auto [sizer, combobox] = create_item_combobox_base(title, tooltip, param, vlist, current_index);
|
||||||
|
|
||||||
//// save config
|
//// save config
|
||||||
combobox->GetDropDown().Bind(wxEVT_COMBOBOX, [this, param, config_name_index](wxCommandEvent& e) {
|
combobox->GetDropDown().Bind(wxEVT_COMBOBOX, [this, param, config_name_index, onchange](wxCommandEvent& e) {
|
||||||
app_config->set(param, config_name_index[e.GetSelection()]);
|
app_config->set(param, config_name_index[e.GetSelection()]);
|
||||||
|
if (onchange != nullptr) onchange(config_name_index[e.GetSelection()]);
|
||||||
e.Skip();
|
e.Skip();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2044,6 +2045,32 @@ void PreferencesDialog::create_items()
|
|||||||
"preview_default_view_type", PreviewViewTypeLabels, PreviewViewTypeValues);
|
"preview_default_view_type", PreviewViewTypeLabels, PreviewViewTypeValues);
|
||||||
g_sizer->Add(item_preview_view_type);
|
g_sizer->Add(item_preview_view_type);
|
||||||
|
|
||||||
|
auto item_reduced_detail_mode = create_item_combobox(
|
||||||
|
_L("Simplify preview while dragging"),
|
||||||
|
_L("What the sliced preview draws while you drag the camera or a preview slider, or zoom with the mouse wheel, so that large prints stay responsive. "
|
||||||
|
"The full toolpaths are restored as soon as you let go.\n"
|
||||||
|
"Off: the full toolpaths.\n"
|
||||||
|
"Solid model: the sliced objects and the prime tower as solid shapes in their filament colors, cut to the visible layer range, "
|
||||||
|
"with its bottom and top layers drawn as toolpaths. Supports are not shown, and negative volumes are not cut out.\n"
|
||||||
|
"Shell only: every layer without its sparse infill, internal solid infill and gap fill, which lie under the walls and skins. "
|
||||||
|
"Walls, top and bottom surfaces, bridges, supports and the prime tower are drawn whole, so the print looks the same from outside.\n"
|
||||||
|
"The bottom and top of the visible layer range are always drawn whole."),
|
||||||
|
"preview_reduced_detail_mode",
|
||||||
|
{_L("Off"), _L("Solid model"), _L("Shell only")},
|
||||||
|
{"off", "solid", "shell"},
|
||||||
|
// apply the new mode immediately to the currently loaded preview
|
||||||
|
[](std::string value) {
|
||||||
|
if (Plater* plater = wxGetApp().plater()) {
|
||||||
|
if (GLCanvas3D* canvas = plater->get_preview_canvas3D()) {
|
||||||
|
canvas->get_gcode_viewer().set_reduced_detail_mode(value);
|
||||||
|
canvas->set_as_dirty();
|
||||||
|
canvas->request_extra_frame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
g_sizer->Add(item_reduced_detail_mode);
|
||||||
|
|
||||||
auto item_dim_previous_layers = create_item_checkbox(
|
auto item_dim_previous_layers = create_item_checkbox(
|
||||||
_L("Dim lower layers"),
|
_L("Dim lower layers"),
|
||||||
_L("When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."),
|
_L("When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."),
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ public:
|
|||||||
wxBoxSizer *create_item_title(wxString title);
|
wxBoxSizer *create_item_title(wxString title);
|
||||||
wxBoxSizer *create_item_label(wxString label, const wxString tooltip = "", const wxString wiki_url = "");
|
wxBoxSizer *create_item_label(wxString label, const wxString tooltip = "", const wxString wiki_url = "");
|
||||||
wxBoxSizer *create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::function<void(wxString)> onchange = {}, const wxString wiki_url = "");
|
wxBoxSizer *create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::function<void(wxString)> onchange = {}, const wxString wiki_url = "");
|
||||||
wxBoxSizer *create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, const wxString wiki_url = "");
|
wxBoxSizer *create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, std::function<void(std::string)> onchange = {}, const wxString wiki_url = "");
|
||||||
wxBoxSizer *create_item_region_combobox(wxString title, wxString tooltip);
|
wxBoxSizer *create_item_region_combobox(wxString title, wxString tooltip);
|
||||||
wxBoxSizer *create_item_language_combobox(wxString title, wxString tooltip);
|
wxBoxSizer *create_item_language_combobox(wxString title, wxString tooltip);
|
||||||
wxBoxSizer *create_item_loglevel_combobox(wxString title, wxString tooltip, std::vector<wxString> vlist);
|
wxBoxSizer *create_item_loglevel_combobox(wxString title, wxString tooltip, std::vector<wxString> vlist);
|
||||||
|
|||||||
Reference in New Issue
Block a user