From 92aa6540f57993936f595ccecbce5dd259100e30 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 8 Feb 2022 12:57:56 +0100 Subject: [PATCH 01/23] Tech ENABLE_SHOW_TOOLPATHS_COG - Show toolpaths center of gravity --- resources/icons/legend_cog.svg | 50 +++++++++ resources/shaders/toolpaths_cog.fs | 18 ++++ resources/shaders/toolpaths_cog.vs | 40 ++++++++ src/imgui/imconfig.h | 5 +- src/libslic3r/Technologies.hpp | 2 + src/slic3r/GUI/GCodeViewer.cpp | 153 +++++++++++++++++++++++----- src/slic3r/GUI/GCodeViewer.hpp | 52 ++++++++++ src/slic3r/GUI/GLCanvas3D.cpp | 11 ++ src/slic3r/GUI/GLCanvas3D.hpp | 3 + src/slic3r/GUI/GLModel.cpp | 70 ++++++++++++- src/slic3r/GUI/GLModel.hpp | 8 ++ src/slic3r/GUI/GLShadersManager.cpp | 4 + src/slic3r/GUI/GUI_Preview.hpp | 3 + src/slic3r/GUI/ImGuiWrapper.cpp | 3 + src/slic3r/GUI/Selection.cpp | 22 ++-- 15 files changed, 407 insertions(+), 37 deletions(-) create mode 100644 resources/icons/legend_cog.svg create mode 100644 resources/shaders/toolpaths_cog.fs create mode 100644 resources/shaders/toolpaths_cog.vs diff --git a/resources/icons/legend_cog.svg b/resources/icons/legend_cog.svg new file mode 100644 index 0000000000..9a55fb7f5c --- /dev/null +++ b/resources/icons/legend_cog.svg @@ -0,0 +1,50 @@ + + + + + Svg Vector Icons : http://www.onlinewebfonts.com/icon + + diff --git a/resources/shaders/toolpaths_cog.fs b/resources/shaders/toolpaths_cog.fs new file mode 100644 index 0000000000..f88d79b969 --- /dev/null +++ b/resources/shaders/toolpaths_cog.fs @@ -0,0 +1,18 @@ +#version 110 + +const vec4 BLACK = vec4(vec3(0.1), 1.0); +const vec4 WHITE = vec4(vec3(1.0), 1.0); + +const float emission_factor = 0.25; + +// x = tainted, y = specular; +varying vec2 intensity; +varying vec3 world_position; +uniform vec3 world_center; + +void main() +{ + vec3 delta = world_position - world_center; + vec4 color = delta.x * delta.y * delta.z > 0.0 ? BLACK : WHITE; + gl_FragColor = vec4(vec3(intensity.y) + color.rgb * (intensity.x + emission_factor), 1.0); +} diff --git a/resources/shaders/toolpaths_cog.vs b/resources/shaders/toolpaths_cog.vs new file mode 100644 index 0000000000..c7b1abfdbd --- /dev/null +++ b/resources/shaders/toolpaths_cog.vs @@ -0,0 +1,40 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +// x = tainted, y = specular; +varying vec2 intensity; +varying vec3 world_position; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 normal = normalize(gl_NormalMatrix * gl_Normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + world_position = gl_Vertex.xyz; + gl_Position = ftransform(); +} diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index db0e54e60d..f2c3ef0837 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -165,8 +165,9 @@ namespace ImGui const wchar_t LegendColorChanges = 0x2612; const wchar_t LegendPausePrints = 0x2613; const wchar_t LegendCustomGCodes = 0x2614; - const wchar_t LegendShells = 0x2615; - const wchar_t LegendToolMarker = 0x2616; + const wchar_t LegendCOG = 0x2615; + const wchar_t LegendShells = 0x2616; + const wchar_t LegendToolMarker = 0x2617; // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index da55ae4ff4..80a4284a78 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -72,6 +72,8 @@ #define ENABLE_SHOW_NON_MANIFOLD_EDGES (1 && ENABLE_2_5_0_ALPHA1) // Enable rework of Reload from disk command #define ENABLE_RELOAD_FROM_DISK_REWORK (1 && ENABLE_2_5_0_ALPHA1) +// Enable showing toolpaths center of gravity +#define ENABLE_SHOW_TOOLPATHS_COG (1 && ENABLE_2_5_0_ALPHA1) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 367846f0c8..a4808a0cf0 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -160,6 +160,66 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessorResult::MoveVertex& move move.volumetric_rate(), move.extruder_id, move.cp_color_id, { { endpoint, endpoint } } }); } +#if ENABLE_SHOW_TOOLPATHS_COG +void GCodeViewer::COG::render() +{ + if (!m_visible) + return; + + init(); + + GLShaderProgram* shader = wxGetApp().get_shader("toolpaths_cog"); + if (shader == nullptr) + return; + + shader->start_using(); + + glsafe(::glDisable(GL_DEPTH_TEST)); + + glsafe(::glPushMatrix()); + const Vec3d position = cog(); + glsafe(::glTranslated(position.x(), position.y(), position.z())); + if (m_fixed_size) { + const double inv_zoom = wxGetApp().plater()->get_camera().get_inv_zoom(); + glsafe(::glScaled(inv_zoom, inv_zoom, inv_zoom)); + } + m_model.render(); + + glsafe(::glPopMatrix()); + + shader->stop_using(); + + ////Show ImGui window + //static float last_window_width = 0.0f; + //static size_t last_text_length = 0; + + //ImGuiWrapper& imgui = *wxGetApp().imgui(); + //const Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); + //imgui.set_next_window_pos(0.5f * static_cast(cnv_size.get_width()), 0.0f, ImGuiCond_Always, 0.5f, 0.0f); + //ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + //ImGui::SetNextWindowBgAlpha(0.25f); + //imgui.begin(std::string("COG"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + //imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Center of mass") + ":"); + //ImGui::SameLine(); + //char buf[1024]; + //const Vec3d position = cog(); + //sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", position.x(), position.y(), position.z()); + //imgui.text(std::string(buf)); + + //// force extra frame to automatically update window size + //const float width = ImGui::GetWindowWidth(); + //const size_t length = strlen(buf); + //if (width != last_window_width || length != last_text_length) { + // last_window_width = width; + // last_text_length = length; + // imgui.set_requires_extra_frame(); + //} + + //imgui.end(); + //ImGui::PopStyleVar(); +} +#endif // ENABLE_SHOW_TOOLPATHS_COG + #if ENABLE_PREVIEW_LAYER_TIME float GCodeViewer::Extrusions::Range::step_size(EType type) const { @@ -955,6 +1015,9 @@ unsigned int GCodeViewer::get_options_visibility_flags() const flags = set_flag(flags, static_cast(Preview::OptionType::ColorChanges), is_toolpath_move_type_visible(EMoveType::Color_change)); flags = set_flag(flags, static_cast(Preview::OptionType::PausePrints), is_toolpath_move_type_visible(EMoveType::Pause_Print)); flags = set_flag(flags, static_cast(Preview::OptionType::CustomGCodes), is_toolpath_move_type_visible(EMoveType::Custom_GCode)); +#if ENABLE_SHOW_TOOLPATHS_COG + flags = set_flag(flags, static_cast(Preview::OptionType::CenterOfGravity), m_cog.is_visible()); +#endif // ENABLE_SHOW_TOOLPATHS_COG flags = set_flag(flags, static_cast(Preview::OptionType::Shells), m_shells.visible); flags = set_flag(flags, static_cast(Preview::OptionType::ToolMarker), m_sequential_view.marker.is_visible()); #if !ENABLE_PREVIEW_LAYOUT @@ -978,6 +1041,9 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags) set_toolpath_move_type_visible(EMoveType::Color_change, is_flag_set(static_cast(Preview::OptionType::ColorChanges))); set_toolpath_move_type_visible(EMoveType::Pause_Print, is_flag_set(static_cast(Preview::OptionType::PausePrints))); set_toolpath_move_type_visible(EMoveType::Custom_GCode, is_flag_set(static_cast(Preview::OptionType::CustomGCodes))); +#if ENABLE_SHOW_TOOLPATHS_COG + m_cog.set_visible(is_flag_set(static_cast(Preview::OptionType::CenterOfGravity))); +#endif // ENABLE_SHOW_TOOLPATHS_COG m_shells.visible = is_flag_set(static_cast(Preview::OptionType::Shells)); m_sequential_view.marker.set_visible(is_flag_set(static_cast(Preview::OptionType::ToolMarker))); #if !ENABLE_PREVIEW_LAYOUT @@ -1537,6 +1603,10 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) if (wxGetApp().is_editor()) m_contained_in_bed = wxGetApp().plater()->build_volume().all_paths_inside(gcode_result, m_paths_bounding_box); +#if ENABLE_SHOW_TOOLPATHS_COG + m_cog.reset(); +#endif // ENABLE_SHOW_TOOLPATHS_COG + m_sequential_view.gcode_ids.clear(); for (size_t i = 0; i < gcode_result.moves.size(); ++i) { const GCodeProcessorResult::MoveVertex& move = gcode_result.moves[i]; @@ -1551,18 +1621,15 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) std::vector instances_offsets(m_buffers.size()); std::vector options_zs; - size_t seams_count = 0; std::vector biased_seams_ids; // toolpaths data -> extract vertices from result for (size_t i = 0; i < m_moves_count; ++i) { const GCodeProcessorResult::MoveVertex& curr = gcode_result.moves[i]; - if (curr.type == EMoveType::Seam) { - ++seams_count; + if (curr.type == EMoveType::Seam) biased_seams_ids.push_back(i - biased_seams_ids.size() - 1); - } - size_t move_id = i - seams_count; + const size_t move_id = i - biased_seams_ids.size(); // skip first vertex if (i == 0) @@ -1570,6 +1637,20 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) const GCodeProcessorResult::MoveVertex& prev = gcode_result.moves[i - 1]; +#if ENABLE_SHOW_TOOLPATHS_COG + if (curr.type == EMoveType::Extrude && + curr.extrusion_role != erSkirt && + curr.extrusion_role != erSupportMaterial && + curr.extrusion_role != erSupportMaterialInterface && + curr.extrusion_role != erWipeTower && + curr.extrusion_role != erCustom && + curr.extrusion_role != erMixed) { + const Vec3d curr_pos = curr.position.cast(); + const Vec3d prev_pos = prev.position.cast(); + m_cog.add_segment(curr_pos, prev_pos, curr.mm3_per_mm * (curr_pos - prev_pos).norm()); + } +#endif // ENABLE_SHOW_TOOLPATHS_COG + // update progress dialog ++progress_count; if (progress_dialog != nullptr && progress_count % progress_threshold == 0) { @@ -1893,14 +1974,14 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) using VboIndexList = std::vector; std::vector vbo_indices(m_buffers.size()); - seams_count = 0; + size_t seams_count = 0; for (size_t i = 0; i < m_moves_count; ++i) { const GCodeProcessorResult::MoveVertex& curr = gcode_result.moves[i]; if (curr.type == EMoveType::Seam) ++seams_count; - size_t move_id = i - seams_count; + const size_t move_id = i - seams_count; // skip first vertex if (i == 0) @@ -4074,15 +4155,6 @@ void GCodeViewer::render_legend(float& legend_height) }; #if ENABLE_LEGEND_TOOLBAR_ICONS -// auto circle_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const Color& color) { -// const float margin = 3.0f; -// const ImVec2 center(0.5f * (pos.x + pos.x + size), 0.5f * (pos.y + pos.y + size)); -// window.DrawList->AddCircleFilled(center, 0.5f * (size - 2.0f * margin), ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); -// }; -// auto line_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const Color& color) { -// const float margin = 3.0f; -// window.DrawList->AddLine({ pos.x + margin, pos.y + size - margin }, { pos.x + size - margin, pos.y + margin }, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 3.0f); -// }; auto image_icon = [&imgui](ImGuiWindow& window, const ImVec2& pos, float size, const wchar_t& icon_id) { ImGuiIO& io = ImGui::GetIO(); const ImTextureID tex_id = io.Fonts->TexID; @@ -4091,17 +4163,17 @@ void GCodeViewer::render_legend(float& legend_height) const ImFontAtlas::CustomRect* const rect = imgui.GetTextureCustomRect(icon_id); const ImVec2 uv0 = { static_cast(rect->X) / tex_w, static_cast(rect->Y) / tex_h }; const ImVec2 uv1 = { static_cast(rect->X + rect->Width) / tex_w, static_cast(rect->Y + rect->Height) / tex_h }; - window.DrawList->AddImage(tex_id, pos, { pos.x + size, pos.y + size }, uv0, uv1, ImGui::GetColorU32({ 1.0f, 1.0f, 1.0f, 1.0f })); + window.DrawList->AddImage(tex_id, pos, { pos.x + size, pos.y + size }, uv0, uv1, ImGuiWrapper::to_ImU32({ 1.0f, 1.0f, 1.0f, 1.0f })); }; #else - auto circle_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const Color& color) { - const float margin = 3.0f; + auto circle_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const ColorRGBA& color) { + const float margin = 3.0f; const ImVec2 center(0.5f * (pos.x + pos.x + size), 0.5f * (pos.y + pos.y + size)); - window.DrawList->AddCircleFilled(center, 0.5f * (size - 2.0f * margin), ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); + window.DrawList->AddCircleFilled(center, 0.5f * (size - 2.0f * margin), ImGuiWrapper::to_ImU32(color), 16); }; - auto line_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const Color& color) { + auto line_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const ColorRGBA& color) { const float margin = 3.0f; - window.DrawList->AddLine({ pos.x + margin, pos.y + size - margin }, { pos.x + size - margin, pos.y + margin }, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 3.0f); + window.DrawList->AddLine({ pos.x + margin, pos.y + size - margin }, { pos.x + size - margin, pos.y + margin }, ImGuiWrapper::to_ImU32(color), 3.0f); }; #endif // ENABLE_LEGEND_TOOLBAR_ICONS @@ -4190,12 +4262,41 @@ void GCodeViewer::render_legend(float& legend_height) #endif // ENABLE_LEGEND_TOOLBAR_ICONS }); ImGui::SameLine(); +#if ENABLE_SHOW_TOOLPATHS_COG +#if ENABLE_LEGEND_TOOLBAR_ICONS + toggle_button(Preview::OptionType::CenterOfGravity, _u8L("Center of gravity"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + image_icon(window, pos, size, ImGui::LegendCOG); + }); +#else + toggle_button(Preview::OptionType::CenterOfGravity, _u8L("Center of gravity"), [](ImGuiWindow& window, const ImVec2& pos, float size) { + const ImU32 black = ImGuiWrapper::to_ImU32({ 0.0f, 0.0f, 0.0f, 1.0f }); + const ImU32 white = ImGuiWrapper::to_ImU32({ 1.0f, 1.0f, 1.0f, 1.0f }); + const float margin = 3.0f; + const ImVec2 center(0.5f * (pos.x + pos.x + size), 0.5f * (pos.y + pos.y + size)); + const float radius = 0.5f * (size - 2.0f * margin); + window.DrawList->PathArcToFast(center, radius, 0, 3); + window.DrawList->PathLineTo(center); + window.DrawList->PathFillConvex(black); + window.DrawList->PathArcToFast(center, radius, 3, 6); + window.DrawList->PathLineTo(center); + window.DrawList->PathFillConvex(white); + window.DrawList->PathArcToFast(center, radius, 6, 9); + window.DrawList->PathLineTo(center); + window.DrawList->PathFillConvex(black); + window.DrawList->PathArcToFast(center, radius, 9, 12); + window.DrawList->PathLineTo(center); + window.DrawList->PathFillConvex(white); + window.DrawList->AddCircle(center, radius, black, 16); + }); +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + ImGui::SameLine(); +#endif // ENABLE_SHOW_TOOLPATHS_COG #if ENABLE_LEGEND_TOOLBAR_ICONS toggle_button(Preview::OptionType::Shells, _u8L("Shells"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { image_icon(window, pos, size, ImGui::LegendShells); #else toggle_button(Preview::OptionType::Shells, _u8L("Shells"), [](ImGuiWindow& window, const ImVec2& pos, float size) { - const ImU32 color = ImGui::GetColorU32({ 1.0f, 1.0f, 1.0f, 1.0f }); + const ImU32 color = ImGuiWrapper::to_ImU32({ 1.0f, 1.0f, 1.0f, 1.0f }); const float margin = 3.0f; const float proj = 0.25f * size; window.DrawList->AddRect({ pos.x + margin, pos.y + size - margin }, { pos.x + size - margin - proj, pos.y + margin + proj }, color); @@ -4212,11 +4313,11 @@ void GCodeViewer::render_legend(float& legend_height) image_icon(window, pos, size, ImGui::LegendToolMarker); #else toggle_button(Preview::OptionType::ToolMarker, _u8L("Tool marker"), [](ImGuiWindow& window, const ImVec2& pos, float size) { - const ImU32 color = ImGui::GetColorU32({ 1.0f, 1.0f, 1.0f, 0.8f }); + const ImU32 color = ImGuiWrapper::to_ImU32({ 1.0f, 1.0f, 1.0f, 0.8f }); const float margin = 3.0f; const ImVec2 p1(0.5f * (pos.x + pos.x + size), pos.y + size - margin); - const ImVec2 p2 = ImVec2(p1.x + 0.25f * size, p1.y - 0.25f * size); - const ImVec2 p3 = ImVec2(p1.x - 0.25f * size, p1.y - 0.25f * size); + const ImVec2 p2(p1.x + 0.25f * size, p1.y - 0.25f * size); + const ImVec2 p3(p1.x - 0.25f * size, p1.y - 0.25f * size); window.DrawList->AddTriangleFilled(p1, p2, p3, color); const float mid_x = 0.5f * (pos.x + pos.x + size); window.DrawList->AddRectFilled({ mid_x - 0.09375f * size, p1.y - 0.25f * size }, { mid_x + 0.09375f * size, pos.y + margin }, color); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index ecebb26414..22e866fe6e 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -380,6 +380,52 @@ class GCodeViewer bool visible{ false }; }; +#if ENABLE_SHOW_TOOLPATHS_COG + // helper to render center of gravity + class COG + { + GLModel m_model; + bool m_visible{ false }; + // whether or not to render the model with fixed screen size + bool m_fixed_size{ true }; + double m_total_mass{ 0.0 }; + Vec3d m_position{ Vec3d::Zero() }; + + public: + void render(); + + void reset() { + m_position = Vec3d::Zero(); + m_total_mass = 0.0; + } + + bool is_visible() const { return m_visible; } + void set_visible(bool visible) { m_visible = visible; } + + void add_segment(const Vec3d& v1, const Vec3d& v2, double mass) { + assert(mass > 0.0); + m_position += mass * 0.5 * (v1 + v2); + m_total_mass += mass; + } + + Vec3d cog() const { return (m_total_mass > 0.0) ? (Vec3d)(m_position / m_total_mass) : Vec3d::Zero(); } + + private: + void init() { + if (m_model.is_initialized()) + return; + + const float radius = m_fixed_size ? 10.0f : 1.0f; + +#if ENABLE_GLBEGIN_GLEND_REMOVAL + m_model.init_from(smooth_sphere(32, radius)); +#else + m_model.init_from(its_make_sphere(radius, PI / 32.0)); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL + } + }; +#endif // ENABLE_SHOW_TOOLPATHS_COG + // helper to render extrusion paths struct Extrusions { @@ -734,6 +780,9 @@ private: Extrusions m_extrusions; SequentialView m_sequential_view; Shells m_shells; +#if ENABLE_SHOW_TOOLPATHS_COG + COG m_cog; +#endif // ENABLE_SHOW_TOOLPATHS_COG EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; #if ENABLE_PREVIEW_LAYOUT @@ -779,6 +828,9 @@ public: void reset(); void render(); +#if ENABLE_SHOW_TOOLPATHS_COG + void render_cog() { m_cog.render(); } +#endif // ENABLE_SHOW_TOOLPATHS_COG bool has_data() const { return !m_roles.empty(); } bool can_export_toolpaths() const; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 84cc2e5552..e16f7cfb1a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1600,6 +1600,10 @@ void GLCanvas3D::render() #if ENABLE_RENDER_SELECTION_CENTER _render_selection_center(); #endif // ENABLE_RENDER_SELECTION_CENTER +#if ENABLE_SHOW_TOOLPATHS_COG + if (!m_main_toolbar.is_enabled()) + _render_gcode_cog(); +#endif // ENABLE_SHOW_TOOLPATHS_COG // we need to set the mouse's scene position here because the depth buffer // could be invalidated by the following gizmo render methods @@ -5415,6 +5419,13 @@ void GLCanvas3D::_render_gcode() m_gcode_viewer.render(); } +#if ENABLE_SHOW_TOOLPATHS_COG +void GLCanvas3D::_render_gcode_cog() +{ + m_gcode_viewer.render_cog(); +} +#endif // ENABLE_SHOW_TOOLPATHS_COG + void GLCanvas3D::_render_selection() { float scale_factor = 1.0; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 93cd8f4242..33e17d5dd2 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -938,6 +938,9 @@ private: void _render_bed_for_picking(bool bottom); void _render_objects(GLVolumeCollection::ERenderType type); void _render_gcode(); +#if ENABLE_SHOW_TOOLPATHS_COG + void _render_gcode_cog(); +#endif // ENABLE_SHOW_TOOLPATHS_COG void _render_selection(); void _render_sequential_clearance(); #if ENABLE_RENDER_SELECTION_CENTER diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 8ac1e5534d..2cf5990011 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -1724,7 +1724,7 @@ GLModel::Geometry diamond(unsigned short resolution) #if ENABLE_GLBEGIN_GLEND_REMOVAL // vertices for (unsigned short i = 0; i < resolution; ++i) { - float ii = float(i) * step; + const float ii = float(i) * step; const Vec3f p = { 0.5f * ::cos(ii), 0.5f * ::sin(ii), 0.0f }; append_vertex(data, p, p.normalized()); } @@ -1785,5 +1785,73 @@ GLModel::Geometry diamond(unsigned short resolution) return data; } +#if ENABLE_GLBEGIN_GLEND_REMOVAL +#if ENABLE_SHOW_TOOLPATHS_COG +GLModel::Geometry smooth_sphere(unsigned short resolution, float radius) +{ + resolution = std::max(4, resolution); + resolution = std::min(256, resolution); // ensure no unsigned short overflow of indices + + const unsigned short sectorCount = /*2 **/ resolution; + const unsigned short stackCount = resolution; + + const float sectorStep = float(2.0 * M_PI / sectorCount); + const float stackStep = float(M_PI / stackCount); + + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::USHORT }; + data.vertices.reserve(((stackCount - 1) * sectorCount + 2) * GLModel::Geometry::vertex_stride_floats(data.format)); + data.indices.reserve(((2 * (stackCount - 1) * sectorCount) * 3) * GLModel::Geometry::index_stride_bytes(data.format)); + + // vertices + for (unsigned short i = 0; i <= stackCount; ++i) { + // from pi/2 to -pi/2 + const double stackAngle = 0.5 * M_PI - stackStep * i; + const double xy = double(radius) * ::cos(stackAngle); + const double z = double(radius) * ::sin(stackAngle); + if (i == 0 || i == stackCount) { + const Vec3f v(float(xy), 0.0f, float(z)); + data.add_vertex(v, (Vec3f)v.normalized()); + } + else { + for (unsigned short j = 0; j < sectorCount; ++j) { + // from 0 to 2pi + const double sectorAngle = sectorStep * j; + const Vec3f v(float(xy * std::cos(sectorAngle)), float(xy * std::sin(sectorAngle)), float(z)); + data.add_vertex(v, (Vec3f)v.normalized()); + } + } + } + + // triangles + for (unsigned short i = 0; i < stackCount; ++i) { + // Beginning of current stack. + unsigned short k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount); + const unsigned short k1_first = k1; + // Beginning of next stack. + unsigned short k2 = (i == 0) ? 1 : (k1 + sectorCount); + const unsigned short k2_first = k2; + for (unsigned short j = 0; j < sectorCount; ++j) { + // 2 triangles per sector excluding first and last stacks + unsigned short k1_next = k1; + unsigned short k2_next = k2; + if (i != 0) { + k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1); + data.add_ushort_triangle(k1, k2, k1_next); + } + if (i + 1 != stackCount) { + k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1); + data.add_ushort_triangle(k1_next, k2, k2_next); + } + k1 = k1_next; + k2 = k2_next; + } + } + + return data; +} +#endif // ENABLE_SHOW_TOOLPATHS_COG +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index c02f5186ca..f830094122 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -256,6 +256,14 @@ namespace GUI { // the diamond is contained into a box with size [1, 1, 1] GLModel::Geometry diamond(unsigned short resolution); +#if ENABLE_GLBEGIN_GLEND_REMOVAL +#if ENABLE_SHOW_TOOLPATHS_COG + // create a sphere with the given resolution and smooth normals + // the origin of the sphere is in its center + GLModel::Geometry smooth_sphere(unsigned short resolution, float radius); +#endif // ENABLE_SHOW_TOOLPATHS_COG +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index ae71e90afe..33ac9b6bcd 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -41,6 +41,10 @@ std::pair GLShadersManager::init() // used to render 3D scene background valid &= append_shader("background", { "background.vs", "background.fs" }); #endif // ENABLE_GLBEGIN_GLEND_REMOVAL +#if ENABLE_SHOW_TOOLPATHS_COG + // used to render toolpaths center of gravity + valid &= append_shader("toolpaths_cog", { "toolpaths_cog.vs", "toolpaths_cog.fs" }); +#endif // ENABLE_SHOW_TOOLPATHS_COG // used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview valid &= append_shader("gouraud_light", { "gouraud_light.vs", "gouraud_light.fs" }); // used to render printbed diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index d9c32e8293..97f8edde33 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -126,6 +126,9 @@ public: ColorChanges, PausePrints, CustomGCodes, +#if ENABLE_SHOW_TOOLPATHS_COG + CenterOfGravity, +#endif // ENABLE_SHOW_TOOLPATHS_COG Shells, ToolMarker, #if !ENABLE_PREVIEW_LAYOUT diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 4616eee85d..e70c1111b9 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -64,6 +64,9 @@ static const std::map font_icons = { {ImGui::LegendColorChanges , "legend_colorchanges" }, {ImGui::LegendPausePrints , "legend_pauseprints" }, {ImGui::LegendCustomGCodes , "legend_customgcodes" }, +#if ENABLE_SHOW_TOOLPATHS_COG + {ImGui::LegendCOG , "legend_cog" }, +#endif // ENABLE_SHOW_TOOLPATHS_COG {ImGui::LegendShells , "legend_shells" }, {ImGui::LegendToolMarker , "legend_toolmarker" }, #endif // ENABLE_LEGEND_TOOLBAR_ICONS diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index c680f6e097..3ef0fcdd9a 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1276,6 +1276,14 @@ void Selection::render_center(bool gizmo_is_dragging) if (!m_valid || is_empty()) return; +#if ENABLE_GLBEGIN_GLEND_REMOVAL + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + shader->start_using(); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL + const Vec3d center = gizmo_is_dragging ? m_cache.dragging_center : get_bounding_box().center(); glsafe(::glDisable(GL_DEPTH_TEST)); @@ -1284,19 +1292,17 @@ void Selection::render_center(bool gizmo_is_dragging) glsafe(::glTranslated(center.x(), center.y(), center.z())); #if ENABLE_GLBEGIN_GLEND_REMOVAL - GLShaderProgram* shader = wxGetApp().get_shader("flat"); - if (shader == nullptr) - return; - - shader->start_using(); -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL + m_vbo_sphere.set_color(ColorRGBA::WHITE()); +#else m_vbo_sphere.set_color(-1, ColorRGBA::WHITE()); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL m_vbo_sphere.render(); + + glsafe(::glPopMatrix()); + #if ENABLE_GLBEGIN_GLEND_REMOVAL shader->stop_using(); #endif // ENABLE_GLBEGIN_GLEND_REMOVAL - - glsafe(::glPopMatrix()); } #endif // ENABLE_RENDER_SELECTION_CENTER From a74bea5a2dda33fd8420e6a9e1d0e53c10003d6d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 8 Feb 2022 13:09:19 +0100 Subject: [PATCH 02/23] Follow-up of 6b041429f6f137add530c900c3160d431286366f - Fixed typo due to missing code edit after copy and paste --- src/slic3r/GUI/3DBed.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index c8ca2397bb..545f5ab103 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -701,7 +701,9 @@ void Bed3D::render_default(bool bottom, bool picking) glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - if (m_model.get_filename().empty() && !bottom) { + const bool has_model = !m_model.get_filename().empty(); + + if (!has_model && !bottom) { // draw background glsafe(::glDepthMask(GL_FALSE)); m_triangles.set_color(picking ? PICKING_MODEL_COLOR : DEFAULT_MODEL_COLOR); @@ -712,7 +714,7 @@ void Bed3D::render_default(bool bottom, bool picking) if (!picking) { // draw grid glsafe(::glLineWidth(1.5f * m_scale_factor)); - m_gridlines.set_color(picking ? DEFAULT_SOLID_GRID_COLOR : DEFAULT_TRANSPARENT_GRID_COLOR); + m_gridlines.set_color(has_model && !bottom ? DEFAULT_SOLID_GRID_COLOR : DEFAULT_TRANSPARENT_GRID_COLOR); m_gridlines.render(); } From ffa3a1d8fc6b2c85a6399cd91b3a9b2a1d92f82f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 9 Feb 2022 09:08:37 +0100 Subject: [PATCH 03/23] Tech ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL - Replace GLIndexedVertexArray with GLModel - GLGizmoFlatten::PlaneData::vbo --- src/libslic3r/Technologies.hpp | 2 + src/slic3r/GUI/GLModel.cpp | 42 ++++++++++++------- src/slic3r/GUI/GLModel.hpp | 3 ++ src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp | 53 ++++++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp | 8 ++++ 5 files changed, 94 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 80a4284a78..6b4274b11f 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -68,6 +68,8 @@ #define ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL (1 && ENABLE_2_5_0_ALPHA1) // Enable removal of old OpenGL render calls #define ENABLE_GLBEGIN_GLEND_REMOVAL (1 && ENABLE_2_5_0_ALPHA1) +// Enable replace GLIndexedVertexArray with GLModel +#define ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL (1 && ENABLE_GLBEGIN_GLEND_REMOVAL) // Enable show non-manifold edges #define ENABLE_SHOW_NON_MANIFOLD_EDGES (1 && ENABLE_2_5_0_ALPHA1) // Enable rework of Reload from disk command diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 2cf5990011..8b5de5ec45 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -18,6 +18,16 @@ namespace Slic3r { namespace GUI { #if ENABLE_GLBEGIN_GLEND_REMOVAL +void GLModel::Geometry::reserve_vertices(size_t vertices_count) +{ + vertices.reserve(vertices_count * vertex_stride_floats(format)); +} + +void GLModel::Geometry::reserve_indices(size_t indices_count) +{ + indices.reserve(indices_count * index_stride_bytes(format)); +} + void GLModel::Geometry::add_vertex(const Vec2f& position) { assert(format.vertex_layout == EVertexLayout::P2); @@ -470,8 +480,8 @@ void GLModel::init_from(const indexed_triangle_set& its, const BoundingBoxf3 &bb Geometry& data = m_render_data.geometry; data.format = { Geometry::EPrimitiveType::Triangles, Geometry::EVertexLayout::P3N3, Geometry::EIndexType::UINT }; - data.vertices.reserve(3 * its.indices.size() * Geometry::vertex_stride_floats(data.format)); - data.indices.reserve(3 * its.indices.size() * Geometry::index_stride_bytes(data.format)); + data.reserve_vertices(3 * its.indices.size()); + data.reserve_indices(3 * its.indices.size()); // vertices + indices unsigned int vertices_counter = 0; @@ -552,8 +562,8 @@ void GLModel::init_from(const Polygons& polygons, float z) segments_count += polygon.points.size(); } - data.vertices.reserve(2 * segments_count * Geometry::vertex_stride_floats(data.format)); - data.indices.reserve(2 * segments_count * Geometry::index_stride_bytes(data.format)); + data.reserve_vertices(2 * segments_count); + data.reserve_indices(2 * segments_count); // vertices + indices unsigned int vertices_counter = 0; @@ -1034,8 +1044,8 @@ GLModel::Geometry stilized_arrow(unsigned short resolution, float tip_radius, fl GLModel::Geometry data; #if ENABLE_GLBEGIN_GLEND_REMOVAL data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::USHORT }; - data.vertices.reserve((6 * resolution + 2) * GLModel::Geometry::vertex_stride_floats(data.format)); - data.indices.reserve((6 * resolution * 3) * GLModel::Geometry::index_stride_bytes(data.format)); + data.reserve_vertices(6 * resolution + 2); + data.reserve_indices(6 * resolution * 3); #else GLModel::Geometry::Entity entity; entity.type = GLModel::EPrimitiveType::Triangles; @@ -1175,6 +1185,7 @@ GLModel::Geometry stilized_arrow(unsigned short resolution, float tip_radius, fl data.entities.emplace_back(entity); #endif // ENABLE_GLBEGIN_GLEND_REMOVAL + return data; } @@ -1200,8 +1211,8 @@ GLModel::Geometry circular_arrow(unsigned short resolution, float radius, float GLModel::Geometry data; #if ENABLE_GLBEGIN_GLEND_REMOVAL data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::USHORT }; - data.vertices.reserve((8 * (resolution + 1) + 30) * GLModel::Geometry::vertex_stride_floats(data.format)); - data.indices.reserve(((8 * resolution + 16) * 3) * GLModel::Geometry::index_stride_bytes(data.format)); + data.reserve_vertices(8 * (resolution + 1) + 30); + data.reserve_indices((8 * resolution + 16) * 3); #else GLModel::Geometry::Entity entity; entity.type = GLModel::EPrimitiveType::Triangles; @@ -1506,6 +1517,7 @@ GLModel::Geometry circular_arrow(unsigned short resolution, float radius, float data.entities.emplace_back(entity); #endif // ENABLE_GLBEGIN_GLEND_REMOVAL + return data; } @@ -1526,8 +1538,8 @@ GLModel::Geometry straight_arrow(float tip_width, float tip_height, float stem_w GLModel::Geometry data; #if ENABLE_GLBEGIN_GLEND_REMOVAL data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::USHORT }; - data.vertices.reserve(42 * GLModel::Geometry::vertex_stride_floats(data.format)); - data.indices.reserve((24 * 3) * GLModel::Geometry::index_stride_bytes(data.format)); + data.reserve_vertices(42); + data.reserve_indices(72); #else GLModel::Geometry::Entity entity; entity.type = GLModel::EPrimitiveType::Triangles; @@ -1699,6 +1711,7 @@ GLModel::Geometry straight_arrow(float tip_width, float tip_height, float stem_w data.entities.emplace_back(entity); #endif // ENABLE_GLBEGIN_GLEND_REMOVAL + return data; } @@ -1712,8 +1725,8 @@ GLModel::Geometry diamond(unsigned short resolution) GLModel::Geometry data; #if ENABLE_GLBEGIN_GLEND_REMOVAL data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::USHORT }; - data.vertices.reserve((resolution + 2) * GLModel::Geometry::vertex_stride_floats(data.format)); - data.indices.reserve(((2 * (resolution + 1)) * 3) * GLModel::Geometry::index_stride_bytes(data.format)); + data.reserve_vertices(resolution + 2); + data.reserve_indices((2 * (resolution + 1)) * 3); #else GLModel::Geometry::Entity entity; entity.type = GLModel::EPrimitiveType::Triangles; @@ -1782,6 +1795,7 @@ GLModel::Geometry diamond(unsigned short resolution) data.entities.emplace_back(entity); #endif // ENABLE_GLBEGIN_GLEND_REMOVAL + return data; } @@ -1800,8 +1814,8 @@ GLModel::Geometry smooth_sphere(unsigned short resolution, float radius) GLModel::Geometry data; data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::USHORT }; - data.vertices.reserve(((stackCount - 1) * sectorCount + 2) * GLModel::Geometry::vertex_stride_floats(data.format)); - data.indices.reserve(((2 * (stackCount - 1) * sectorCount) * 3) * GLModel::Geometry::index_stride_bytes(data.format)); + data.reserve_vertices((stackCount - 1) * sectorCount + 2); + data.reserve_indices((2 * (stackCount - 1) * sectorCount) * 3); // vertices for (unsigned short i = 0; i <= stackCount; ++i) { diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index f830094122..fd62652901 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -80,6 +80,9 @@ namespace GUI { std::vector indices; ColorRGBA color{ ColorRGBA::BLACK() }; + void reserve_vertices(size_t vertices_count); + void reserve_indices(size_t indices_count); + void add_vertex(const Vec2f& position); void add_vertex(const Vec2f& position, const Vec2f& tex_coord); void add_vertex(const Vec3f& position); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index fd32c68fc1..9218558b71 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -1,6 +1,9 @@ // Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoFlatten.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#include "slic3r/GUI/GUI_App.hpp" +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" #include "libslic3r/Geometry/ConvexHull.hpp" @@ -63,6 +66,14 @@ void GLGizmoFlatten::on_render() { const Selection& selection = m_parent.get_selection(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + shader->start_using(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_DEPTH_TEST)); @@ -76,21 +87,38 @@ void GLGizmoFlatten::on_render() if (this->is_plane_update_necessary()) update_planes(); for (int i = 0; i < (int)m_planes.size(); ++i) { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + m_planes[i].vbo.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); + m_planes[i].vbo.render(); +#else glsafe(::glColor4fv(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR.data() : DEFAULT_PLANE_COLOR.data())); if (m_planes[i].vbo.has_VBOs()) m_planes[i].vbo.render(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } glsafe(::glPopMatrix()); } glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glDisable(GL_BLEND)); + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + shader->stop_using(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } void GLGizmoFlatten::on_render_for_picking() { const Selection& selection = m_parent.get_selection(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + shader->start_using(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_BLEND)); @@ -102,13 +130,21 @@ void GLGizmoFlatten::on_render_for_picking() if (this->is_plane_update_necessary()) update_planes(); for (int i = 0; i < (int)m_planes.size(); ++i) { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + m_planes[i].vbo.set_color(picking_color_component(i)); +#else glsafe(::glColor4fv(picking_color_component(i).data())); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL m_planes[i].vbo.render(); } glsafe(::glPopMatrix()); } glsafe(::glEnable(GL_CULL_FACE)); + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + shader->stop_using(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) @@ -324,12 +360,29 @@ void GLGizmoFlatten::update_planes() // And finally create respective VBOs. The polygon is convex with // the vertices in order, so triangulation is trivial. for (auto& plane : m_planes) { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLModel::Geometry init_data; + const GLModel::Geometry::EIndexType index_type = (plane.vertices.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT; + init_data.format = { GLModel::Geometry::EPrimitiveType::TriangleFan, GLModel::Geometry::EVertexLayout::P3N3, index_type }; + init_data.reserve_vertices(plane.vertices.size()); + init_data.reserve_indices(plane.vertices.size()); + // vertices + indices + for (size_t i = 0; i < plane.vertices.size(); ++i) { + init_data.add_vertex((Vec3f)plane.vertices[i].cast(), (Vec3f)plane.normal.cast()); + if (index_type == GLModel::Geometry::EIndexType::USHORT) + init_data.add_ushort_index(i); + else + init_data.add_uint_index(i); + } + plane.vbo.init_from(std::move(init_data)); +#else plane.vbo.reserve(plane.vertices.size()); for (const auto& vert : plane.vertices) plane.vbo.push_geometry(vert, plane.normal); for (size_t i=1; i vertices; // should be in fact local in update_planes() +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLModel vbo; +#else GLIndexedVertexArray vbo; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL Vec3d normal; float area; }; From be6b6590be4010d03ead377ba192eacac8c8d14b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 9 Feb 2022 09:15:20 +0100 Subject: [PATCH 04/23] Tech ENABLE_GLBEGIN_GLEND_REMOVAL - Refactoring to simplify client code of GLModel::Geometry --- src/slic3r/GUI/3DScene.cpp | 8 +++---- src/slic3r/GUI/GLCanvas3D.cpp | 16 ++++++------- src/slic3r/GUI/GLSelectionRectangle.cpp | 4 ++-- src/slic3r/GUI/GLTexture.cpp | 4 ++-- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 8 +++---- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 4 ++-- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 4 ++-- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 24 ++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 4 ++-- src/slic3r/GUI/Selection.cpp | 14 ++++++------ 10 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 4e929d060c..846c828943 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -326,8 +326,8 @@ void GLVolume::SinkingContours::update() for (const ExPolygon& expoly : diff_ex(expand(polygons, float(scale_(HalfWidth))), shrink(polygons, float(scale_(HalfWidth))))) { #if ENABLE_GLBEGIN_GLEND_REMOVAL const std::vector triangulation = triangulate_expolygon_3d(expoly); - init_data.vertices.reserve(init_data.vertices.size() + triangulation.size() * GUI::GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(init_data.indices.size() + triangulation.size() * GUI::GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(init_data.vertices_count() + triangulation.size()); + init_data.reserve_indices(init_data.indices_count() + triangulation.size()); for (const Vec3d& v : triangulation) { init_data.add_vertex((Vec3f)(v.cast() + 0.015f * Vec3f::UnitZ())); // add a small positive z to avoid z-fighting ++vertices_counter; @@ -401,8 +401,8 @@ void GLVolume::NonManifoldEdges::update() GUI::GLModel::Geometry init_data; #if ENABLE_GLBEGIN_GLEND_REMOVAL init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Lines, GUI::GLModel::Geometry::EVertexLayout::P3, GUI::GLModel::Geometry::EIndexType::UINT }; - init_data.vertices.reserve(2 * edges.size() * GUI::GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(2 * edges.size() * GUI::GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(2 * edges.size()); + init_data.reserve_indices(2 * edges.size()); // vertices + indices unsigned int vertices_count = 0; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e16f7cfb1a..871680cb52 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -368,8 +368,8 @@ void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3 GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P2T2, GLModel::Geometry::EIndexType::USHORT }; - init_data.vertices.reserve(4 * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(6 * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(4); + init_data.reserve_indices(6); // vertices const float l = bar_rect.get_left(); @@ -428,8 +428,8 @@ void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P2, GLModel::Geometry::EIndexType::USHORT }; init_data.color = ColorRGBA::BLACK(); - init_data.vertices.reserve(2 * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(2 * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); // vertices const float x = bar_rect.get_left() + float(m_slicing_parameters->layer_height) * scale_x; @@ -449,8 +449,8 @@ void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P2, GLModel::Geometry::EIndexType::UINT }; init_data.color = ColorRGBA::BLUE(); - init_data.vertices.reserve(m_layer_height_profile.size() * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(m_layer_height_profile.size() * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(m_layer_height_profile.size() / 2); + init_data.reserve_indices(m_layer_height_profile.size() / 2); // vertices + indices for (unsigned int i = 0; i < (unsigned int)m_layer_height_profile.size(); i += 2) { @@ -5238,8 +5238,8 @@ void GLCanvas3D::_render_background() GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P2T2, GLModel::Geometry::EIndexType::USHORT }; - init_data.vertices.reserve(4 * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(6 * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(4); + init_data.reserve_indices(6); // vertices init_data.add_vertex(Vec2f(-1.0f, -1.0f), Vec2f(0.0f, 0.0f)); diff --git a/src/slic3r/GUI/GLSelectionRectangle.cpp b/src/slic3r/GUI/GLSelectionRectangle.cpp index 5331d517d8..515da6de3a 100644 --- a/src/slic3r/GUI/GLSelectionRectangle.cpp +++ b/src/slic3r/GUI/GLSelectionRectangle.cpp @@ -126,8 +126,8 @@ namespace GUI { GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::LineLoop, GLModel::Geometry::EVertexLayout::P2, GLModel::Geometry::EIndexType::USHORT }; - init_data.vertices.reserve(4 * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(4 * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(4); + init_data.reserve_indices(4); // vertices init_data.add_vertex(Vec2f(left, bottom)); diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index 3b99397ad5..340bb78c3c 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -342,8 +342,8 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, #if ENABLE_GLBEGIN_GLEND_REMOVAL GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P2T2, GLModel::Geometry::EIndexType::USHORT }; - init_data.vertices.reserve(4 * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(6 * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(4); + init_data.reserve_indices(6); // vertices init_data.add_vertex(Vec2f(left, bottom), Vec2f(uvs.left_bottom.u, uvs.left_bottom.v)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index ab29e90262..ff5d89f5eb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -115,8 +115,8 @@ void GLGizmoCut::on_render() GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; init_data.color = { 0.8f, 0.8f, 0.8f, 0.5f }; - init_data.vertices.reserve(4 * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(6 * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(4); + init_data.reserve_indices(6); // vertices init_data.add_vertex(Vec3f(min_x, min_y, plane_center.z())); @@ -160,8 +160,8 @@ void GLGizmoCut::on_render() GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; init_data.color = ColorRGBA::YELLOW(); - init_data.vertices.reserve(2 * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(2 * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); // vertices init_data.add_vertex((Vec3f)plane_center.cast()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index fbbcb32560..ff921ea7ce 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -117,8 +117,8 @@ void GLGizmoMove3D::on_render() GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; init_data.color = AXES_COLOR[id]; - init_data.vertices.reserve(2 * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(2 * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); // vertices init_data.add_vertex((Vec3f)center.cast()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 4c76767bd7..1c17071519 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -185,8 +185,8 @@ void GLGizmoPainterBase::render_cursor_circle() static const float StepSize = 2.0f * float(PI) / float(StepsCount); init_data.format = { GLModel::Geometry::EPrimitiveType::LineLoop, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; init_data.color = { 0.0f, 1.0f, 0.3f, 1.0f }; - init_data.vertices.reserve(StepsCount * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(StepsCount * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(StepsCount); + init_data.reserve_indices(StepsCount); // vertices + indices for (unsigned short i = 0; i < StepsCount; ++i) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 9bedc86cc8..362e253090 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -235,8 +235,8 @@ void GLGizmoRotate::render_circle() const GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::LineLoop, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; - init_data.vertices.reserve(ScaleStepsCount * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(ScaleStepsCount * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(ScaleStepsCount); + init_data.reserve_indices(ScaleStepsCount); // vertices + indices for (unsigned short i = 0; i < ScaleStepsCount; ++i) { @@ -278,8 +278,8 @@ void GLGizmoRotate::render_scale() const GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; - init_data.vertices.reserve(2 * ScaleStepsCount * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(2 * ScaleStepsCount * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(2 * ScaleStepsCount); + init_data.reserve_indices(2 * ScaleStepsCount); // vertices + indices for (unsigned short i = 0; i < ScaleStepsCount; ++i) { @@ -337,8 +337,8 @@ void GLGizmoRotate::render_snap_radii() const GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; - init_data.vertices.reserve(2 * ScaleStepsCount * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(2 * ScaleStepsCount * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(2 * ScaleStepsCount); + init_data.reserve_indices(2 * ScaleStepsCount); // vertices + indices for (unsigned short i = 0; i < ScaleStepsCount; ++i) { @@ -388,8 +388,8 @@ void GLGizmoRotate::render_reference_radius(const ColorRGBA& color, bool radius_ GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; - init_data.vertices.reserve(2 * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(2 * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); // vertices init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); @@ -429,8 +429,8 @@ void GLGizmoRotate::render_angle() const GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; - init_data.vertices.reserve((1 + AngleResolution) * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve((1 + AngleResolution) * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(1 + AngleResolution); + init_data.reserve_indices(1 + AngleResolution); // vertices + indices for (unsigned short i = 0; i <= AngleResolution; ++i) { @@ -466,8 +466,8 @@ void GLGizmoRotate::render_grabber_connection(const ColorRGBA& color, bool radiu GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; - init_data.vertices.reserve(2 * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(2 * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); // vertices init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 7d0e3ec347..ce7d4705b3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -403,8 +403,8 @@ void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; - init_data.vertices.reserve(2 * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(2 * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); // vertices init_data.add_vertex((Vec3f)m_grabbers[id_1].center.cast()); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 3ef0fcdd9a..89fd43acbf 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1261,10 +1261,10 @@ void Selection::render(float scale_factor) return; m_scale_factor = scale_factor; + // render cumulative bounding box of selected volumes #if ENABLE_GLBEGIN_GLEND_REMOVAL render_bounding_box(get_bounding_box(), ColorRGB::WHITE()); #else - // render cumulative bounding box of selected volumes render_selected_volumes(); #endif // ENABLE_GLBEGIN_GLEND_REMOVAL render_synchronized_volumes(); @@ -1900,8 +1900,8 @@ void Selection::render_bounding_box(const BoundingBoxf3 & box, float* color) con GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; - init_data.vertices.reserve(48 * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(48 * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(48); + init_data.reserve_indices(48); // vertices init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_min.z())); @@ -2209,8 +2209,8 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; - init_data.vertices.reserve(4 * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(6 * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(4); + init_data.reserve_indices(6); // vertices init_data.add_vertex(Vec3f(p1.x(), p1.y(), z1)); @@ -2231,8 +2231,8 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; - init_data.vertices.reserve(4 * GLModel::Geometry::vertex_stride_floats(init_data.format)); - init_data.indices.reserve(6 * GLModel::Geometry::index_stride_bytes(init_data.format)); + init_data.reserve_vertices(4); + init_data.reserve_indices(6); // vertices init_data.add_vertex(Vec3f(p1.x(), p1.y(), z2)); From 4d2d77e99c244c9da18983f6d624d277a9db4fec Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 9 Feb 2022 10:05:49 +0100 Subject: [PATCH 05/23] Tech ENABLE_GLBEGIN_GLEND_REMOVAL - Adapt GLModel::Geometry index format in dependence of data size, where possible --- src/slic3r/GUI/3DBed.cpp | 11 +++++++---- src/slic3r/GUI/3DScene.cpp | 8 ++++++-- src/slic3r/GUI/GLCanvas3D.cpp | 10 ++++++++-- src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp | 4 ++-- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 545f5ab103..8488c1148f 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -311,10 +311,11 @@ void Bed3D::init_triangles() if (triangles.empty() || triangles.size() % 3 != 0) return; - const GLModel::Geometry::EIndexType index_type = (triangles.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT; - GLModel::Geometry init_data; + const GLModel::Geometry::EIndexType index_type = (triangles.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT; init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3T2, index_type }; + init_data.reserve_vertices(triangles.size()); + init_data.reserve_indices(triangles.size() / 3); Vec2f min = triangles.front(); Vec2f max = min; @@ -330,6 +331,7 @@ void Bed3D::init_triangles() Vec2f inv_size = size.cwiseInverse(); inv_size.y() *= -1.0f; + // vertices + indices unsigned int vertices_counter = 0; for (const Vec2f& v : triangles) { const Vec3f p = { v.x(), v.y(), GROUND_Z }; @@ -378,10 +380,11 @@ void Bed3D::init_gridlines() Lines contour_lines = to_lines(m_contour); std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines)); - const GLModel::Geometry::EIndexType index_type = (gridlines.size() < 65536 / 2) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT; - GLModel::Geometry init_data; + const GLModel::Geometry::EIndexType index_type = (2 * gridlines.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, index_type }; + init_data.reserve_vertices(2 * gridlines.size()); + init_data.reserve_indices(2 * gridlines.size()); for (const Line& l : gridlines) { init_data.add_vertex(Vec3f(unscale(l.a.x()), unscale(l.a.y()), GROUND_Z)); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 846c828943..339754790f 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -400,7 +400,8 @@ void GLVolume::NonManifoldEdges::update() if (!edges.empty()) { GUI::GLModel::Geometry init_data; #if ENABLE_GLBEGIN_GLEND_REMOVAL - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Lines, GUI::GLModel::Geometry::EVertexLayout::P3, GUI::GLModel::Geometry::EIndexType::UINT }; + const GUI::GLModel::Geometry::EIndexType index_type = (2 * edges.size() < 65536) ? GUI::GLModel::Geometry::EIndexType::USHORT : GUI::GLModel::Geometry::EIndexType::UINT; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Lines, GUI::GLModel::Geometry::EVertexLayout::P3, index_type }; init_data.reserve_vertices(2 * edges.size()); init_data.reserve_indices(2 * edges.size()); @@ -410,7 +411,10 @@ void GLVolume::NonManifoldEdges::update() init_data.add_vertex((Vec3f)mesh.its.vertices[edge.first].cast()); init_data.add_vertex((Vec3f)mesh.its.vertices[edge.second].cast()); vertices_count += 2; - init_data.add_uint_line(vertices_count - 2, vertices_count - 1); + if (index_type == GUI::GLModel::Geometry::EIndexType::USHORT) + init_data.add_ushort_line((unsigned short)vertices_count - 2, (unsigned short)vertices_count - 1); + else + init_data.add_uint_line(vertices_count - 2, vertices_count - 1); } m_model.init_from(std::move(init_data)); #else diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 871680cb52..95600c074a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -447,7 +447,8 @@ void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) m_profile.profile.reset(); GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P2, GLModel::Geometry::EIndexType::UINT }; + const GLModel::Geometry::EIndexType index_type = (m_layer_height_profile.size() / 2 < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P2, index_type }; init_data.color = ColorRGBA::BLUE(); init_data.reserve_vertices(m_layer_height_profile.size() / 2); init_data.reserve_indices(m_layer_height_profile.size() / 2); @@ -456,7 +457,10 @@ void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) for (unsigned int i = 0; i < (unsigned int)m_layer_height_profile.size(); i += 2) { init_data.add_vertex(Vec2f(bar_rect.get_left() + float(m_layer_height_profile[i + 1]) * scale_x, bar_rect.get_bottom() + float(m_layer_height_profile[i]) * scale_y)); - init_data.add_uint_index(i / 2); + if (index_type == GLModel::Geometry::EIndexType::USHORT) + init_data.add_ushort_index((unsigned short)i / 2); + else + init_data.add_uint_index(i / 2); } m_profile.profile.init_from(std::move(init_data)); @@ -898,6 +902,8 @@ void GLCanvas3D::SequentialPrintClearance::set_polygons(const Polygons& polygons unsigned int vertices_counter = 0; for (const ExPolygon& poly : polygons_union) { const std::vector triangulation = triangulate_expolygon_3d(poly); + fill_data.reserve_vertices(fill_data.vertices_count() + triangulation.size()); + fill_data.reserve_indices(fill_data.indices_count() + triangulation.size()); for (const Vec3d& v : triangulation) { fill_data.add_vertex((Vec3f)(v.cast() + 0.0125f * Vec3f::UnitZ())); // add a small positive z to avoid z-fighting ++vertices_counter; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 9218558b71..0956a9047b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -370,9 +370,9 @@ void GLGizmoFlatten::update_planes() for (size_t i = 0; i < plane.vertices.size(); ++i) { init_data.add_vertex((Vec3f)plane.vertices[i].cast(), (Vec3f)plane.normal.cast()); if (index_type == GLModel::Geometry::EIndexType::USHORT) - init_data.add_ushort_index(i); + init_data.add_ushort_index((unsigned short)i); else - init_data.add_uint_index(i); + init_data.add_uint_index((unsigned int)i); } plane.vbo.init_from(std::move(init_data)); #else From 8916a0082114c9ebcb7a45ca135a22874fd43c39 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 9 Feb 2022 13:13:10 +0100 Subject: [PATCH 06/23] Tech ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL - Replace GLIndexedVertexArray with GLModel: TriangleSelectorGUI::m_iva_enforcers TriangleSelectorGUI::m_iva_blockers TriangleSelectorGUI::m_iva_seed_fills TriangleSelectorGUI::m_varrays GLGizmoPainterBase::s_sphere --- src/slic3r/GUI/GLModel.cpp | 4 +- src/slic3r/GUI/GLModel.hpp | 2 + src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 178 ++++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 19 +- 4 files changed, 191 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 8b5de5ec45..e1dc3305c7 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -730,8 +730,8 @@ void GLModel::render() const const Geometry& data = m_render_data.geometry; - GLenum mode = get_primitive_mode(data.format); - GLenum index_type = get_index_type(data.format); + const GLenum mode = get_primitive_mode(data.format); + const GLenum index_type = get_index_type(data.format); const size_t vertex_stride_bytes = Geometry::vertex_stride_bytes(data.format); const bool position = Geometry::has_position(data.format); diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index fd62652901..70220a75cb 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -106,6 +106,8 @@ namespace GUI { unsigned int extract_uint_index(size_t id) const; unsigned short extract_ushort_index(size_t id) const; + bool is_empty() const { return vertices.empty() || indices.empty(); } + size_t vertices_count() const { return vertices.size() / vertex_stride_floats(format); } size_t indices_count() const { return indices.size() / index_stride_bytes(format); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 1c17071519..cf316afd16 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -18,7 +18,11 @@ namespace Slic3r::GUI { - std::shared_ptr GLGizmoPainterBase::s_sphere = nullptr; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +std::shared_ptr GLGizmoPainterBase::s_sphere = nullptr; +#else +std::shared_ptr GLGizmoPainterBase::s_sphere = nullptr; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) @@ -27,8 +31,13 @@ GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& ic GLGizmoPainterBase::~GLGizmoPainterBase() { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (s_sphere != nullptr) + s_sphere.reset(); +#else if (s_sphere != nullptr && s_sphere->has_VBOs()) s_sphere->release_geometry(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } void GLGizmoPainterBase::set_painter_gizmo_data(const Selection& selection) @@ -220,11 +229,20 @@ void GLGizmoPainterBase::render_cursor_circle() void GLGizmoPainterBase::render_cursor_sphere(const Transform3d& trafo) const { if (s_sphere == nullptr) { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + s_sphere = std::make_shared(); + s_sphere->init_from(its_make_sphere(1.0, double(PI) / 12.0)); +#else s_sphere = std::make_shared(); s_sphere->load_its_flat_shading(its_make_sphere(1.0, double(PI) / 12.0)); s_sphere->finalize_geometry(true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + const Transform3d complete_scaling_matrix_inverse = Geometry::Transformation(trafo).get_matrix(true, true, false, true).inverse(); const bool is_left_handed = Geometry::Transformation(trafo).is_left_handed(); @@ -243,11 +261,22 @@ void GLGizmoPainterBase::render_cursor_sphere(const Transform3d& trafo) const render_color = this->get_cursor_sphere_left_button_color(); else if (m_button_down == Button::Right) render_color = this->get_cursor_sphere_right_button_color(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + shader->start_using(); + + assert(s_sphere != nullptr); + s_sphere->set_color(render_color); +#else glsafe(::glColor4fv(render_color.data())); assert(s_sphere != nullptr); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL s_sphere->render(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + shader->stop_using(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (is_left_handed) glFrontFace(GL_CCW); @@ -763,13 +792,28 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) shader->set_uniform("offset_depth_buffer", true); for (auto iva : {std::make_pair(&m_iva_enforcers, enforcers_color), std::make_pair(&m_iva_blockers, blockers_color)}) { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + iva.first->set_color(iva.second); + iva.first->render(); +#else if (iva.first->has_VBOs()) { shader->set_uniform("uniform_color", iva.second); iva.first->render(); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } - for (auto &iva : m_iva_seed_fills) +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + for (auto& iva : m_iva_seed_fills) { + size_t color_idx = &iva - &m_iva_seed_fills.front(); + const ColorRGBA& color = TriangleSelectorGUI::get_seed_fill_color(color_idx == 1 ? enforcers_color : + color_idx == 2 ? blockers_color : + GLVolume::NEUTRAL_COLOR); + iva.set_color(color); + iva.render(); + } +#else + for (auto& iva : m_iva_seed_fills) if (iva.has_VBOs()) { size_t color_idx = &iva - &m_iva_seed_fills.front(); const ColorRGBA& color = TriangleSelectorGUI::get_seed_fill_color(color_idx == 1 ? enforcers_color : @@ -778,6 +822,7 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) shader->set_uniform("uniform_color", color); iva.render(); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL if (m_paint_contour.has_VBO()) { ScopeGuard guard_gouraud([shader]() { shader->start_using(); }); @@ -798,7 +843,7 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) render_debug(imgui); else assert(false); // If you want debug output, pass ptr to ImGuiWrapper. -#endif +#endif // PRUSASLICER_TRIANGLE_SELECTOR_DEBUG } void TriangleSelectorGUI::update_render_data() @@ -807,20 +852,44 @@ void TriangleSelectorGUI::update_render_data() int blc_cnt = 0; std::vector seed_fill_cnt(m_iva_seed_fills.size(), 0); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + for (auto* iva : { &m_iva_enforcers, &m_iva_blockers }) { + iva->reset(); + } + + for (auto& iva : m_iva_seed_fills) { + iva.reset(); + } + + GLModel::Geometry iva_enforcers_data; + iva_enforcers_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; + GLModel::Geometry iva_blockers_data; + iva_blockers_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; + std::array iva_seed_fills_data; + for (auto& data : iva_seed_fills_data) + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; +#else for (auto *iva : {&m_iva_enforcers, &m_iva_blockers}) iva->release_geometry(); for (auto &iva : m_iva_seed_fills) iva.release_geometry(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL for (const Triangle &tr : m_triangles) { if (!tr.valid() || tr.is_split() || (tr.get_state() == EnforcerBlockerType::NONE && !tr.is_selected_by_seed_fill())) continue; int tr_state = int(tr.get_state()); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLModel::Geometry &iva = tr.is_selected_by_seed_fill() ? iva_seed_fills_data[tr_state] : + tr.get_state() == EnforcerBlockerType::ENFORCER ? iva_enforcers_data : + iva_blockers_data; +#else GLIndexedVertexArray &iva = tr.is_selected_by_seed_fill() ? m_iva_seed_fills[tr_state] : tr.get_state() == EnforcerBlockerType::ENFORCER ? m_iva_enforcers : m_iva_blockers; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL int &cnt = tr.is_selected_by_seed_fill() ? seed_fill_cnt[tr_state] : tr.get_state() == EnforcerBlockerType::ENFORCER ? enf_cnt : blc_cnt; @@ -830,18 +899,36 @@ void TriangleSelectorGUI::update_render_data() //FIXME the normal may likely be pulled from m_triangle_selectors, but it may not be worth the effort // or the current implementation may be more cache friendly. const Vec3f n = (v1 - v0).cross(v2 - v1).normalized(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + iva.add_vertex(v0, n); + iva.add_vertex(v1, n); + iva.add_vertex(v2, n); + iva.add_uint_triangle((unsigned int)cnt, (unsigned int)cnt + 1, (unsigned int)cnt + 2); +#else iva.push_geometry(v0, n); iva.push_geometry(v1, n); iva.push_geometry(v2, n); iva.push_triangle(cnt, cnt + 1, cnt + 2); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL cnt += 3; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (!iva_enforcers_data.is_empty()) + m_iva_enforcers.init_from(std::move(iva_enforcers_data)); + if (!iva_blockers_data.is_empty()) + m_iva_blockers.init_from(std::move(iva_blockers_data)); + for (size_t i = 0; i < m_iva_seed_fills.size(); ++i) { + if (!iva_seed_fills_data[i].is_empty()) + m_iva_seed_fills[i].init_from(std::move(iva_seed_fills_data[i])); + } +#else for (auto *iva : {&m_iva_enforcers, &m_iva_blockers}) iva->finalize_geometry(true); for (auto &iva : m_iva_seed_fills) iva.finalize_geometry(true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL m_paint_contour.release_geometry(); std::vector contour_edges = this->get_seed_fill_contour(); @@ -956,45 +1043,111 @@ void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui) INVALID }; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + for (auto& va : m_varrays) + va.reset(); +#else for (auto& va : m_varrays) va.release_geometry(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL std::array cnts; ::glScalef(1.01f, 1.01f, 1.01f); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + std::array varrays_data; + for (auto& data : varrays_data) + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + for (int tr_id=0; tr_idadd_vertex(m_vertices[tr.verts_idxs[i]].v, Vec3f(0.0f, 0.0f, 1.0f)); + } + va->add_uint_triangle((unsigned int)*cnt, (unsigned int)*cnt + 1, (unsigned int)*cnt + 2); +#else + for (int i = 0; i < 3; ++i) va->push_geometry(double(m_vertices[tr.verts_idxs[i]].v[0]), double(m_vertices[tr.verts_idxs[i]].v[1]), double(m_vertices[tr.verts_idxs[i]].v[2]), 0., 0., 1.); va->push_triangle(*cnt, - *cnt+1, - *cnt+2); + *cnt + 1, + *cnt + 2); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL *cnt += 3; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + for (int i = 0; i < 3; ++i) { + if (!varrays_data[i].is_empty()) + m_varrays[i].init_from(std::move(varrays_data[i])); + } +#else + for (auto* iva : { &m_iva_enforcers, &m_iva_blockers }) + iva->finalize_geometry(true); + + for (auto& iva : m_iva_seed_fills) + iva.finalize_geometry(true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLShaderProgram* curr_shader = wxGetApp().get_current_shader(); + if (curr_shader != nullptr) + curr_shader->stop_using(); + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + ::glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); for (vtype i : {ORIGINAL, SPLIT, INVALID}) { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLModel& va = m_varrays[i]; + switch (i) { + case ORIGINAL: va.set_color({ 0.0f, 0.0f, 1.0f, 1.0f }); break; + case SPLIT: va.set_color({ 1.0f, 0.0f, 0.0f, 1.0f }); break; + case INVALID: va.set_color({ 1.0f, 1.0f, 0.0f, 1.0f }); break; + } + va.render(); +#else GLIndexedVertexArray& va = m_varrays[i]; va.finalize_geometry(true); if (va.has_VBOs()) { @@ -1005,11 +1158,18 @@ void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui) } va.render(); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } ::glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + shader->stop_using(); + } + + if (curr_shader != nullptr) + curr_shader->start_using(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } -#endif - - +#endif // PRUSASLICER_TRIANGLE_SELECTOR_DEBUG } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 079f3f08ed..6d73c05059 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -3,7 +3,11 @@ #include "GLGizmoBase.hpp" +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#include "slic3r/GUI/GLModel.hpp" +#else #include "slic3r/GUI/3DScene.hpp" +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #include "libslic3r/ObjectID.hpp" #include "libslic3r/TriangleSelector.hpp" @@ -81,7 +85,7 @@ public: void render_debug(ImGuiWrapper* imgui); bool m_show_triangles{false}; bool m_show_invalid{false}; -#endif +#endif // PRUSASLICER_TRIANGLE_SELECTOR_DEBUG protected: bool m_update_render_data = false; @@ -91,10 +95,19 @@ protected: private: void update_render_data(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLModel m_iva_enforcers; + GLModel m_iva_blockers; + std::array m_iva_seed_fills; +#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG + std::array m_varrays; +#endif // PRUSASLICER_TRIANGLE_SELECTOR_DEBUG +#else GLIndexedVertexArray m_iva_enforcers; GLIndexedVertexArray m_iva_blockers; std::array m_iva_seed_fills; std::array m_varrays; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL protected: GLPaintContour m_paint_contour; @@ -209,7 +222,11 @@ private: const Camera& camera, const std::vector& trafo_matrices) const; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + static std::shared_ptr s_sphere; +#else static std::shared_ptr s_sphere; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL bool m_internal_stack_active = false; bool m_schedule_update = false; From 81e9582761982ad65f801ab541f3a27543c6355c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 8 Feb 2022 13:21:27 +0100 Subject: [PATCH 07/23] Changed how the libasan library path is determined for Perl unit tests to work also on the build server. On Centos 7 calling "gcc -print-file-name=libasan.so" returns a path to "ld script" instead of a path to a shared library. --- xs/CMakeLists.txt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index cfadaf8785..06fc98322f 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -210,15 +210,24 @@ else () endif () set(PERL_ENV_VARS "") -if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT CMAKE_CROSSCOMPILING AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) if (SLIC3R_ASAN OR SLIC3R_UBSAN) set(PERL_ENV_VARS env) endif () if (SLIC3R_ASAN) # Find the location of libasan.so for passing it into LD_PRELOAD. It works with GCC and Clang on Linux. - execute_process(COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=libasan.so OUTPUT_VARIABLE LIBASAN_PATH OUTPUT_STRIP_TRAILING_WHITESPACE) - set(PERL_ENV_VARS ${PERL_ENV_VARS} "LD_PRELOAD=${LIBASAN_PATH}") + # On Centos 7 calling "gcc -print-file-name=libasan.so" returns path to "ld script" instead of path to shared library. + set(_asan_compiled_bin ${CMAKE_CURRENT_BINARY_DIR}/detect_libasan) + set(_asan_source_file ${_asan_compiled_bin}.c) + # Compile and link simple C application with enabled address sanitizer. + file(WRITE ${_asan_source_file} "int main(){}") + include(GetPrerequisites) + execute_process(COMMAND ${CMAKE_C_COMPILER} ${_asan_source_file} -fsanitize=address -lasan -o ${_asan_compiled_bin}) + # Extract from the compiled application absolute path of libasan. + get_prerequisites(${_asan_compiled_bin} _asan_shared_libraries_list 0 0 "" "") + list(FILTER _asan_shared_libraries_list INCLUDE REGEX libasan) + set(PERL_ENV_VARS ${PERL_ENV_VARS} "LD_PRELOAD=${_asan_shared_libraries_list}") # Suppressed memory leak reports that come from Perl. set(PERL_LEAK_SUPPRESSION_FILE ${CMAKE_CURRENT_BINARY_DIR}/leak_suppression.txt) From df3d6703577041714da55ea13f21db18384b40fe Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 9 Feb 2022 15:11:56 +0100 Subject: [PATCH 08/23] Tech ENABLE_GLBEGIN_GLEND_REMOVAL - Removed Slic3r::GUI::GLPaintContour from GLGizmoPainterBase.hpp/.cpp and replaced with GLModel --- .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 8 ++ src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 75 +++++++++++++++++-- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 11 ++- 3 files changed, 85 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 4c204d0d4c..dd9cf0de2e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -589,6 +589,9 @@ void TriangleSelectorMmGui::render(ImGuiWrapper *imgui) m_gizmo_scene.render(color_idx); } +#if ENABLE_GLBEGIN_GLEND_REMOVAL + render_paint_contour(); +#else if (m_paint_contour.has_VBO()) { ScopeGuard guard_mm_gouraud([shader]() { shader->start_using(); }); shader->stop_using(); @@ -602,6 +605,7 @@ void TriangleSelectorMmGui::render(ImGuiWrapper *imgui) contour_shader->stop_using(); } +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL m_update_render_data = false; } @@ -636,6 +640,9 @@ void TriangleSelectorMmGui::update_render_data() m_gizmo_scene.finalize_triangle_indices(); +#if ENABLE_GLBEGIN_GLEND_REMOVAL + update_paint_contour(); +#else m_paint_contour.release_geometry(); std::vector contour_edges = this->get_seed_fill_contour(); m_paint_contour.contour_vertices.reserve(contour_edges.size() * 6); @@ -654,6 +661,7 @@ void TriangleSelectorMmGui::update_render_data() m_paint_contour.contour_indices_size = m_paint_contour.contour_indices.size(); m_paint_contour.finalize_geometry(); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL } wxString GLGizmoMmuSegmentation::handle_snapshot_action_name(bool shift_down, GLGizmoPainterBase::Button button_down) const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index cf316afd16..0fc57c909d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -249,7 +249,7 @@ void GLGizmoPainterBase::render_cursor_sphere(const Transform3d& trafo) const glsafe(::glPushMatrix()); glsafe(::glMultMatrixd(trafo.data())); // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. - glsafe(::glTranslatef(m_rr.hit(0), m_rr.hit(1), m_rr.hit(2))); + glsafe(::glTranslatef(m_rr.hit.x(), m_rr.hit.y(), m_rr.hit.z())); glsafe(::glMultMatrixd(complete_scaling_matrix_inverse.data())); glsafe(::glScaled(m_cursor_radius, m_cursor_radius, m_cursor_radius)); @@ -805,13 +805,13 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL for (auto& iva : m_iva_seed_fills) { - size_t color_idx = &iva - &m_iva_seed_fills.front(); - const ColorRGBA& color = TriangleSelectorGUI::get_seed_fill_color(color_idx == 1 ? enforcers_color : - color_idx == 2 ? blockers_color : - GLVolume::NEUTRAL_COLOR); - iva.set_color(color); - iva.render(); - } + size_t color_idx = &iva - &m_iva_seed_fills.front(); + const ColorRGBA& color = TriangleSelectorGUI::get_seed_fill_color(color_idx == 1 ? enforcers_color : + color_idx == 2 ? blockers_color : + GLVolume::NEUTRAL_COLOR); + iva.set_color(color); + iva.render(); + } #else for (auto& iva : m_iva_seed_fills) if (iva.has_VBOs()) { @@ -824,6 +824,9 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) } #endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_GLBEGIN_GLEND_REMOVAL + render_paint_contour(); +#else if (m_paint_contour.has_VBO()) { ScopeGuard guard_gouraud([shader]() { shader->start_using(); }); shader->stop_using(); @@ -837,6 +840,7 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) contour_shader->stop_using(); } +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG if (imgui) @@ -930,6 +934,9 @@ void TriangleSelectorGUI::update_render_data() iva.finalize_geometry(true); #endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_GLBEGIN_GLEND_REMOVAL + update_paint_contour(); +#else m_paint_contour.release_geometry(); std::vector contour_edges = this->get_seed_fill_contour(); m_paint_contour.contour_vertices.reserve(contour_edges.size() * 6); @@ -948,8 +955,10 @@ void TriangleSelectorGUI::update_render_data() m_paint_contour.contour_indices_size = m_paint_contour.contour_indices.size(); m_paint_contour.finalize_geometry(); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL } +#if !ENABLE_GLBEGIN_GLEND_REMOVAL void GLPaintContour::render() const { assert(this->m_contour_VBO_id != 0); @@ -1007,6 +1016,7 @@ void GLPaintContour::release_geometry() } this->clear(); } +#endif // !ENABLE_GLBEGIN_GLEND_REMOVAL #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui) @@ -1172,4 +1182,53 @@ void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui) } #endif // PRUSASLICER_TRIANGLE_SELECTOR_DEBUG +#if ENABLE_GLBEGIN_GLEND_REMOVAL +void TriangleSelectorGUI::update_paint_contour() +{ + m_paint_contour.reset(); + + GLModel::Geometry init_data; + const std::vector contour_edges = this->get_seed_fill_contour(); + const GLModel::Geometry::EIndexType index_type = (2 * contour_edges.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, index_type }; + init_data.reserve_vertices(2 * contour_edges.size()); + init_data.reserve_indices(2 * contour_edges.size()); + // vertices + indices + unsigned int vertices_count = 0; + for (const Vec2i& edge : contour_edges) { + init_data.add_vertex(m_vertices[edge(0)].v); + init_data.add_vertex(m_vertices[edge(1)].v); + vertices_count += 2; + if (index_type == GLModel::Geometry::EIndexType::USHORT) + init_data.add_ushort_line((unsigned short)vertices_count - 2, (unsigned short)vertices_count - 1); + else + init_data.add_uint_line(vertices_count - 2, vertices_count - 1); + } + + if (!init_data.is_empty()) + m_paint_contour.init_from(std::move(init_data)); +} + +void TriangleSelectorGUI::render_paint_contour() +{ + auto* curr_shader = wxGetApp().get_current_shader(); + if (curr_shader != nullptr) + curr_shader->stop_using(); + + auto* contour_shader = wxGetApp().get_shader("mm_contour"); + if (contour_shader != nullptr) { + contour_shader->start_using(); + + glsafe(::glDepthFunc(GL_LEQUAL)); + m_paint_contour.render(); + glsafe(::glDepthFunc(GL_LESS)); + + contour_shader->stop_using(); + } + + if (curr_shader != nullptr) + curr_shader->start_using(); +} +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL + } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 6d73c05059..37c7163e2f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -32,6 +32,7 @@ enum class PainterGizmoType { MMU_SEGMENTATION }; +#if !ENABLE_GLBEGIN_GLEND_REMOVAL class GLPaintContour { public: @@ -67,6 +68,7 @@ public: GLuint m_contour_VBO_id{0}; GLuint m_contour_EBO_id{0}; }; +#endif // !ENABLE_GLBEGIN_GLEND_REMOVAL class TriangleSelectorGUI : public TriangleSelector { public: @@ -79,7 +81,7 @@ public: virtual void render(ImGuiWrapper *imgui); void render() { this->render(nullptr); } - void request_update_render_data() { m_update_render_data = true; }; + void request_update_render_data() { m_update_render_data = true; } #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG void render_debug(ImGuiWrapper* imgui); @@ -110,7 +112,14 @@ private: #endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL protected: +#if ENABLE_GLBEGIN_GLEND_REMOVAL + GLModel m_paint_contour; + + void update_paint_contour(); + void render_paint_contour(); +#else GLPaintContour m_paint_contour; +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL }; From 5ed364910bcebb304cdcd4d75216bf882d420893 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 9 Feb 2022 15:30:11 +0100 Subject: [PATCH 09/23] Follow-up to 199dc121a55c0aa0723e5f58417bb2fad1dfc690 Quantization of G-code export to achieve more precise extrusion width control. Fix of wiping. --- src/libslic3r/GCode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6a53917de1..fb6dee37ba 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -174,7 +174,7 @@ namespace Slic3r { gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Start) + "\n"; auto end = this->path.points.end(); bool done = false; - for (; it != end; ++ it) { + for (; it != end && ! done; ++ it) { p = gcodegen.point_to_gcode_quantized(*it); double segment_length = (p - prev).norm(); double dE = GCodeFormatter::quantize_e(xy_to_e * segment_length); From be6922795d6408a31050e4a3878aacb11334f83b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 10 Feb 2022 08:24:36 +0100 Subject: [PATCH 10/23] Tech ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL - Replace GLIndexedVertexArray with GLModel: MeshClipper::m_vertex_array --- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 14 +++++++ src/slic3r/GUI/MeshUtils.cpp | 49 ++++++++++++++++++++++++ src/slic3r/GUI/MeshUtils.hpp | 15 +++++++- src/slic3r/Utils/UndoRedo.cpp | 4 ++ 4 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index cc53f267c0..1e49ebc8c3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -213,15 +213,21 @@ void InstancesHider::render_cut() const clipper->set_limiting_plane(ClippingPlane::ClipsNothing()); glsafe(::glPushMatrix()); +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL if (mv->is_model_part()) glsafe(::glColor3f(0.8f, 0.3f, 0.0f)); else { const ColorRGBA color = color_from_model_volume(*mv); glsafe(::glColor4fv(color.data())); } +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL glsafe(::glPushAttrib(GL_DEPTH_TEST)); glsafe(::glDisable(GL_DEPTH_TEST)); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + clipper->render_cut(mv->is_model_part() ? ColorRGBA(0.8f, 0.3f, 0.0f, 1.0f) : color_from_model_volume(*mv)); +#else clipper->render_cut(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL glsafe(::glPopAttrib()); glsafe(::glPopMatrix()); @@ -417,8 +423,12 @@ void ObjectClipper::render_cut() const clipper->set_transformation(trafo); clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); glsafe(::glPushMatrix()); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + clipper->render_cut({ 1.0f, 0.37f, 0.0f, 1.0f }); +#else glsafe(::glColor3f(1.0f, 0.37f, 0.0f)); clipper->render_cut(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL glsafe(::glPopMatrix()); ++clipper_id; @@ -530,8 +540,12 @@ void SupportsClipper::render_cut() const m_clipper->set_transformation(supports_trafo); glsafe(::glPushMatrix()); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + m_clipper->render_cut({ 1.0f, 0.f, 0.37f, 1.0f }); +#else glsafe(::glColor3f(1.0f, 0.f, 0.37f)); m_clipper->render_cut(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL glsafe(::glPopMatrix()); } diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 440b0ec0db..68762c67a6 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -6,6 +6,9 @@ #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/Model.hpp" +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#include "slic3r/GUI/GUI_App.hpp" +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #include "slic3r/GUI/Camera.hpp" #include @@ -66,13 +69,34 @@ void MeshClipper::set_transformation(const Geometry::Transformation& trafo) +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void MeshClipper::render_cut(const ColorRGBA& color) +#else void MeshClipper::render_cut() +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL { if (! m_triangles_valid) recalculate_triangles(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLShaderProgram* curr_shader = wxGetApp().get_current_shader(); + if (curr_shader != nullptr) + curr_shader->stop_using(); + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + m_model.set_color(color); + m_model.render(); + shader->stop_using(); + } + + if (curr_shader != nullptr) + curr_shader->start_using(); +#else if (m_vertex_array.has_VBOs()) m_vertex_array.render(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } @@ -161,6 +185,30 @@ void MeshClipper::recalculate_triangles() tr.pretranslate(0.001 * m_plane.get_normal().normalized()); // to avoid z-fighting +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + m_model.reset(); + + GLModel::Geometry init_data; + const GLModel::Geometry::EIndexType index_type = (m_triangles2d.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, index_type }; + init_data.reserve_vertices(m_triangles2d.size()); + init_data.reserve_indices(m_triangles2d.size()); + + // vertices + indices + for (auto it = m_triangles2d.cbegin(); it != m_triangles2d.cend(); it = it + 3) { + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + const size_t idx = it - m_triangles2d.cbegin(); + if (index_type == GLModel::Geometry::EIndexType::USHORT) + init_data.add_ushort_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2); + else + init_data.add_uint_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2); + } + + if (!init_data.is_empty()) + m_model.init_from(std::move(init_data)); +#else m_vertex_array.release_geometry(); for (auto it=m_triangles2d.cbegin(); it != m_triangles2d.cend(); it=it+3) { m_vertex_array.push_geometry(tr * Vec3d((*(it+0))(0), (*(it+0))(1), height_mesh), up); @@ -170,6 +218,7 @@ void MeshClipper::recalculate_triangles() m_vertex_array.push_triangle(idx, idx+1, idx+2); } m_vertex_array.finalize_geometry(true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL m_triangles_valid = true; } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index bb8a1aa618..11ebf93864 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -6,7 +6,11 @@ #include "libslic3r/SLA/IndexedMesh.hpp" #include "admesh/stl.h" +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#include "slic3r/GUI/GLModel.hpp" +#else #include "slic3r/GUI/3DScene.hpp" +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #include @@ -69,7 +73,8 @@ public: // MeshClipper class cuts a mesh and is able to return a triangulated cut. -class MeshClipper { +class MeshClipper +{ public: // Inform MeshClipper about which plane we want to use to cut the mesh // This is supposed to be in world coordinates. @@ -92,7 +97,11 @@ public: // Render the triangulated cut. Transformation matrices should // be set in world coords. +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + void render_cut(const ColorRGBA& color); +#else void render_cut(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL private: void recalculate_triangles(); @@ -103,7 +112,11 @@ private: ClippingPlane m_plane; ClippingPlane m_limiting_plane = ClippingPlane::ClipsNothing(); std::vector m_triangles2d; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLModel m_model; +#else GLIndexedVertexArray m_vertex_array; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL bool m_triangles_valid = false; }; diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 049de5400a..533c544a8a 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -21,6 +21,10 @@ #include #include +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#include "slic3r/GUI/3DScene.hpp" +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + #include #ifndef NDEBUG From 1d0af50a94dadf22126a3ebe2b9035f05644a1aa Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 10 Feb 2022 10:41:38 +0100 Subject: [PATCH 11/23] GCodeProcessor - Added processing of gcode line M220 --- src/libslic3r/GCode/GCodeProcessor.cpp | 20 ++++++++++++++++++-- src/libslic3r/GCode/GCodeProcessor.hpp | 14 ++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 58e8ed787a..50c0ce4a54 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1196,6 +1196,7 @@ void GCodeProcessor::reset() m_line_id = 0; m_last_line_id = 0; m_feedrate = 0.0f; + m_feed_multiply.reset(); m_width = 0.0f; m_height = 0.0f; m_forced_width = 0.0f; @@ -1698,6 +1699,7 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool break; case '2': switch (cmd[3]) { + case '0': { process_M220(line); break; } // Set Feedrate Percentage case '1': { process_M221(line); break; } // Set extrude factor override percentage default: break; } @@ -2498,7 +2500,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) // updates feedrate from line, if present if (line.has_f()) - m_feedrate = line.f() * MMMIN_TO_MMSEC; + m_feedrate = m_feed_multiply.current * line.f() * MMMIN_TO_MMSEC; // calculates movement deltas float max_abs_delta = 0.0f; @@ -2863,7 +2865,7 @@ void GCodeProcessor::process_G61(const GCodeReader::GCodeLine& line) modified = true; } if (line.has_f()) - m_feedrate = line.f(); + m_feedrate = m_feed_multiply.current * line.f(); if (!modified) m_end_position = m_saved_position; @@ -3136,6 +3138,20 @@ void GCodeProcessor::process_M205(const GCodeReader::GCodeLine& line) } } +void GCodeProcessor::process_M220(const GCodeReader::GCodeLine& line) +{ + if (m_flavor != gcfMarlinLegacy && m_flavor != gcfMarlinFirmware) + return; + + if (line.has('B')) + m_feed_multiply.saved = m_feed_multiply.current; + float value; + if (line.has_value('S', value)) + m_feed_multiply.current = value * 0.01f; + if (line.has('R')) + m_feed_multiply.current = m_feed_multiply.saved; +} + void GCodeProcessor::process_M221(const GCodeReader::GCodeLine& line) { float value_s; diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 7e4bb831d8..25375f61b6 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -525,6 +525,17 @@ namespace Slic3r { unsigned int m_line_id; unsigned int m_last_line_id; float m_feedrate; // mm/s + struct FeedMultiply + { + float current; // percentage + float saved; // percentage + + void reset() { + current = 1.0f; + saved = 1.0f; + } + }; + FeedMultiply m_feed_multiply; float m_width; // mm float m_height; // mm float m_forced_width; // mm @@ -719,6 +730,9 @@ namespace Slic3r { // Advanced settings void process_M205(const GCodeReader::GCodeLine& line); + // Set Feedrate Percentage + void process_M220(const GCodeReader::GCodeLine& line); + // Set extrude factor override percentage void process_M221(const GCodeReader::GCodeLine& line); From cca1454c38ece7f1361edf1effb0087647086e61 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 10 Feb 2022 12:06:12 +0100 Subject: [PATCH 12/23] Tech ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC - Recalculate toolpaths when switching to/from volumetric rate visualization --- src/libslic3r/Technologies.hpp | 2 + src/slic3r/GUI/GCodeViewer.cpp | 82 ++++++++++++++++++++++++++++++++-- src/slic3r/GUI/GCodeViewer.hpp | 7 +++ 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 6b4274b11f..8e7fba1264 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -76,6 +76,8 @@ #define ENABLE_RELOAD_FROM_DISK_REWORK (1 && ENABLE_2_5_0_ALPHA1) // Enable showing toolpaths center of gravity #define ENABLE_SHOW_TOOLPATHS_COG (1 && ENABLE_2_5_0_ALPHA1) +// Enable recalculating toolpaths when switching to/from volumetric rate visualization +#define ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC (1 && ENABLE_2_5_0_ALPHA1) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index a4808a0cf0..d54c28293f 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -103,7 +103,11 @@ void GCodeViewer::IBuffer::reset() count = 0; } +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC +bool GCodeViewer::Path::matches(const GCodeProcessorResult::MoveVertex& move, bool account_for_volumetric_rate) const +#else bool GCodeViewer::Path::matches(const GCodeProcessorResult::MoveVertex& move) const +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC { auto matches_percent = [](float value1, float value2, float max_percent) { return std::abs(value2 - value1) / value1 <= max_percent; @@ -120,10 +124,22 @@ bool GCodeViewer::Path::matches(const GCodeProcessorResult::MoveVertex& move) co case EMoveType::Seam: case EMoveType::Extrude: { // use rounding to reduce the number of generated paths +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + if (account_for_volumetric_rate) + return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role && + move.position.z() <= sub_paths.front().first.position.z() && feedrate == move.feedrate && fan_speed == move.fan_speed && + height == round_to_bin(move.height) && width == round_to_bin(move.width) && + matches_percent(volumetric_rate, move.volumetric_rate(), 0.001f); + else + return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role && + move.position.z() <= sub_paths.front().first.position.z() && feedrate == move.feedrate && fan_speed == move.fan_speed && + height == round_to_bin(move.height) && width == round_to_bin(move.width); +#else return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role && move.position.z() <= sub_paths.front().first.position.z() && feedrate == move.feedrate && fan_speed == move.fan_speed && height == round_to_bin(move.height) && width == round_to_bin(move.width) && matches_percent(volumetric_rate, move.volumetric_rate(), 0.05f); +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC } case EMoveType::Travel: { return type == move.type && feedrate == move.feedrate && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id; @@ -698,10 +714,19 @@ void GCodeViewer::init() void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& print, bool initialized) { // avoid processing if called with the same gcode_result +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + if (m_last_result_id == gcode_result.id && + (m_last_view_type == m_view_type || (m_last_view_type != EViewType::VolumetricRate && m_view_type != EViewType::VolumetricRate))) + return; +#else if (m_last_result_id == gcode_result.id) return; +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC m_last_result_id = gcode_result.id; +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + m_last_view_type = m_view_type; +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC // release gpu memory, if used reset(); @@ -1266,9 +1291,15 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) // add current vertex add_vertex(curr); }; +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + auto add_indices_as_line = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer, + unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id, bool account_for_volumetric_rate) { + if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr, account_for_volumetric_rate)) { +#else auto add_indices_as_line = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer, unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) { if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr)) { +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC // add starting index indices.push_back(static_cast(indices.size())); buffer.add_path(curr, ibuffer_id, indices.size() - 1, move_id - 1); @@ -1287,7 +1318,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) }; // format data into the buffers to be rendered as solid - auto add_vertices_as_solid = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer, unsigned int vbuffer_id, VertexBuffer& vertices, size_t move_id) { +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + auto add_vertices_as_solid = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer, + unsigned int vbuffer_id, VertexBuffer& vertices, size_t move_id, bool account_for_volumetric_rate) { +#else + auto add_vertices_as_solid = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer, + unsigned int vbuffer_id, VertexBuffer& vertices, size_t move_id) { +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC auto store_vertex = [](VertexBuffer& vertices, const Vec3f& position, const Vec3f& normal) { // append position vertices.push_back(position.x()); @@ -1299,7 +1336,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) vertices.push_back(normal.z()); }; +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr, account_for_volumetric_rate)) { +#else if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr)) { +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC buffer.add_path(curr, vbuffer_id, vertices.size(), move_id - 1); buffer.paths.back().sub_paths.back().first.position = prev.position; } @@ -1344,8 +1385,15 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) last_path.sub_paths.back().last = { vbuffer_id, vertices.size(), move_id, curr.position }; }; - auto add_indices_as_solid = [&](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, const GCodeProcessorResult::MoveVertex* next, - TBuffer& buffer, size_t& vbuffer_size, unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) { +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + auto add_indices_as_solid = [&](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, + const GCodeProcessorResult::MoveVertex* next, TBuffer& buffer, size_t& vbuffer_size, unsigned int ibuffer_id, + IndexBuffer& indices, size_t move_id, bool account_for_volumetric_rate) { +#else + auto add_indices_as_solid = [&](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, + const GCodeProcessorResult::MoveVertex* next, TBuffer& buffer, size_t& vbuffer_size, unsigned int ibuffer_id, + IndexBuffer& indices, size_t move_id) { +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC static Vec3f prev_dir; static Vec3f prev_up; static float sq_prev_length; @@ -1390,7 +1438,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) store_triangle(indices, v_offsets[4], v_offsets[5], v_offsets[6]); }; +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr, account_for_volumetric_rate)) { +#else if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr)) { +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC buffer.add_path(curr, ibuffer_id, indices.size(), move_id - 1); buffer.paths.back().sub_paths.back().first.position = prev.position; } @@ -1474,7 +1526,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) vbuffer_size += 6; } +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + if (next != nullptr && (curr.type != next->type || !last_path.matches(*next, account_for_volumetric_rate))) +#else if (next != nullptr && (curr.type != next->type || !last_path.matches(*next))) +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC // ending cap triangles append_ending_cap_triangles(indices, is_first_segment ? first_seg_v_offsets : non_first_seg_v_offsets); @@ -1614,6 +1670,10 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) m_sequential_view.gcode_ids.push_back(move.gcode_id); } +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + bool account_for_volumetric_rate = m_view_type == EViewType::VolumetricRate; +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + std::vector vertices(m_buffers.size()); std::vector indices(m_buffers.size()); std::vector instances(m_buffers.size()); @@ -1678,7 +1738,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) v_multibuffer.push_back(VertexBuffer()); if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) { Path& last_path = t_buffer.paths.back(); +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + if (prev.type == curr.type && last_path.matches(curr, account_for_volumetric_rate)) +#else if (prev.type == curr.type && last_path.matches(curr)) +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC last_path.add_sub_path(prev, static_cast(v_multibuffer.size()) - 1, 0, move_id - 1); } } @@ -1689,7 +1753,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) { case TBuffer::ERenderPrimitiveType::Point: { add_vertices_as_point(curr, v_buffer); break; } case TBuffer::ERenderPrimitiveType::Line: { add_vertices_as_line(prev, curr, v_buffer); break; } +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast(v_multibuffer.size()) - 1, v_buffer, move_id, account_for_volumetric_rate); break; } +#else case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast(v_multibuffer.size()) - 1, v_buffer, move_id); break; } +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC case TBuffer::ERenderPrimitiveType::InstancedModel: { add_model_instance(curr, inst_buffer, inst_id_buffer, move_id); @@ -2053,12 +2121,20 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) break; } case TBuffer::ERenderPrimitiveType::Line: { +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + add_indices_as_line(prev, curr, t_buffer, static_cast(i_multibuffer.size()) - 1, i_buffer, move_id, account_for_volumetric_rate); +#else add_indices_as_line(prev, curr, t_buffer, static_cast(i_multibuffer.size()) - 1, i_buffer, move_id); +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC curr_vertex_buffer.second += t_buffer.max_vertices_per_segment(); break; } case TBuffer::ERenderPrimitiveType::Triangle: { +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast(i_multibuffer.size()) - 1, i_buffer, move_id, account_for_volumetric_rate); +#else add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast(i_multibuffer.size()) - 1, i_buffer, move_id); +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC break; } case TBuffer::ERenderPrimitiveType::BatchedModel: { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 22e866fe6e..b2b0626d51 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -212,7 +212,11 @@ class GCodeViewer unsigned char cp_color_id{ 0 }; std::vector sub_paths; +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + bool matches(const GCodeProcessorResult::MoveVertex& move, bool account_for_volumetric_rate) const; +#else bool matches(const GCodeProcessorResult::MoveVertex& move) const; +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC size_t vertices_count() const { return sub_paths.empty() ? 0 : sub_paths.back().last.s_id - sub_paths.front().first.s_id + 1; } @@ -762,6 +766,9 @@ public: private: bool m_gl_data_initialized{ false }; unsigned int m_last_result_id{ 0 }; +#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC + EViewType m_last_view_type{ EViewType::Count }; +#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC size_t m_moves_count{ 0 }; std::vector m_buffers{ static_cast(EMoveType::Extrude) }; // bounding box of toolpaths From fa1ff1c35768e09fdb7270f563f62abb558cd966 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 10 Feb 2022 14:39:02 +0100 Subject: [PATCH 13/23] Tech ENABLE_GLBEGIN_GLEND_REMOVAL - Another refactoring to simplify client code of GLModel::Geometry --- src/slic3r/GUI/3DBed.cpp | 10 ++++------ src/slic3r/GUI/3DScene.cpp | 5 ++--- src/slic3r/GUI/GLCanvas3D.cpp | 5 ++--- src/slic3r/GUI/GLModel.cpp | 5 +++++ src/slic3r/GUI/GLModel.hpp | 12 +++++++----- src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp | 5 ++--- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 5 ++--- src/slic3r/GUI/MeshUtils.cpp | 5 ++--- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 8488c1148f..0facce33c4 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -312,8 +312,7 @@ void Bed3D::init_triangles() return; GLModel::Geometry init_data; - const GLModel::Geometry::EIndexType index_type = (triangles.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT; - init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3T2, index_type }; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3T2, GLModel::Geometry::index_type(triangles.size()) }; init_data.reserve_vertices(triangles.size()); init_data.reserve_indices(triangles.size() / 3); @@ -338,7 +337,7 @@ void Bed3D::init_triangles() init_data.add_vertex(p, (Vec2f)v.cwiseProduct(inv_size).eval()); ++vertices_counter; if (vertices_counter % 3 == 0) { - if (index_type == GLModel::Geometry::EIndexType::USHORT) + if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT) init_data.add_ushort_triangle((unsigned short)vertices_counter - 3, (unsigned short)vertices_counter - 2, (unsigned short)vertices_counter - 1); else init_data.add_uint_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); @@ -381,8 +380,7 @@ void Bed3D::init_gridlines() std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines)); GLModel::Geometry init_data; - const GLModel::Geometry::EIndexType index_type = (2 * gridlines.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT; - init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, index_type }; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::index_type(2 * gridlines.size()) }; init_data.reserve_vertices(2 * gridlines.size()); init_data.reserve_indices(2 * gridlines.size()); @@ -390,7 +388,7 @@ void Bed3D::init_gridlines() init_data.add_vertex(Vec3f(unscale(l.a.x()), unscale(l.a.y()), GROUND_Z)); init_data.add_vertex(Vec3f(unscale(l.b.x()), unscale(l.b.y()), GROUND_Z)); const unsigned int vertices_counter = (unsigned int)init_data.vertices_count(); - if (index_type == GLModel::Geometry::EIndexType::USHORT) + if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT) init_data.add_ushort_line((unsigned short)vertices_counter - 2, (unsigned short)vertices_counter - 1); else init_data.add_uint_line(vertices_counter - 2, vertices_counter - 1); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 339754790f..c9b647b07c 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -400,8 +400,7 @@ void GLVolume::NonManifoldEdges::update() if (!edges.empty()) { GUI::GLModel::Geometry init_data; #if ENABLE_GLBEGIN_GLEND_REMOVAL - const GUI::GLModel::Geometry::EIndexType index_type = (2 * edges.size() < 65536) ? GUI::GLModel::Geometry::EIndexType::USHORT : GUI::GLModel::Geometry::EIndexType::UINT; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Lines, GUI::GLModel::Geometry::EVertexLayout::P3, index_type }; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Lines, GUI::GLModel::Geometry::EVertexLayout::P3, GUI::GLModel::Geometry::index_type(2 * edges.size()) }; init_data.reserve_vertices(2 * edges.size()); init_data.reserve_indices(2 * edges.size()); @@ -411,7 +410,7 @@ void GLVolume::NonManifoldEdges::update() init_data.add_vertex((Vec3f)mesh.its.vertices[edge.first].cast()); init_data.add_vertex((Vec3f)mesh.its.vertices[edge.second].cast()); vertices_count += 2; - if (index_type == GUI::GLModel::Geometry::EIndexType::USHORT) + if (init_data.format.index_type == GUI::GLModel::Geometry::EIndexType::USHORT) init_data.add_ushort_line((unsigned short)vertices_count - 2, (unsigned short)vertices_count - 1); else init_data.add_uint_line(vertices_count - 2, vertices_count - 1); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 95600c074a..ee16aefef7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -447,8 +447,7 @@ void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) m_profile.profile.reset(); GLModel::Geometry init_data; - const GLModel::Geometry::EIndexType index_type = (m_layer_height_profile.size() / 2 < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT; - init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P2, index_type }; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P2, GLModel::Geometry::index_type(m_layer_height_profile.size() / 2) }; init_data.color = ColorRGBA::BLUE(); init_data.reserve_vertices(m_layer_height_profile.size() / 2); init_data.reserve_indices(m_layer_height_profile.size() / 2); @@ -457,7 +456,7 @@ void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) for (unsigned int i = 0; i < (unsigned int)m_layer_height_profile.size(); i += 2) { init_data.add_vertex(Vec2f(bar_rect.get_left() + float(m_layer_height_profile[i + 1]) * scale_x, bar_rect.get_bottom() + float(m_layer_height_profile[i]) * scale_y)); - if (index_type == GLModel::Geometry::EIndexType::USHORT) + if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT) init_data.add_ushort_index((unsigned short)i / 2); else init_data.add_uint_index(i / 2); diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index e1dc3305c7..28a6a1ce7a 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -328,6 +328,11 @@ size_t GLModel::Geometry::index_stride_bytes(const Format& format) }; } +GLModel::Geometry::EIndexType GLModel::Geometry::index_type(size_t vertices_count) +{ + return (vertices_count < 65536) ? EIndexType::USHORT : EIndexType::UINT; +} + bool GLModel::Geometry::has_position(const Format& format) { switch (format.vertex_layout) diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 70220a75cb..72c50ee11c 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -83,11 +83,11 @@ namespace GUI { void reserve_vertices(size_t vertices_count); void reserve_indices(size_t indices_count); - void add_vertex(const Vec2f& position); - void add_vertex(const Vec2f& position, const Vec2f& tex_coord); - void add_vertex(const Vec3f& position); - void add_vertex(const Vec3f& position, const Vec2f& tex_coord); - void add_vertex(const Vec3f& position, const Vec3f& normal); + void add_vertex(const Vec2f& position); // EVertexLayout::P2 + void add_vertex(const Vec2f& position, const Vec2f& tex_coord); // EVertexLayout::P2T2 + void add_vertex(const Vec3f& position); // EVertexLayout::P3 + void add_vertex(const Vec3f& position, const Vec2f& tex_coord); // EVertexLayout::P3T2 + void add_vertex(const Vec3f& position, const Vec3f& normal); // EVertexLayout::P3N3 void add_ushort_index(unsigned short id); void add_uint_index(unsigned int id); @@ -135,6 +135,8 @@ namespace GUI { static size_t index_stride_bytes(const Format& format); + static EIndexType index_type(size_t vertices_count); + static bool has_position(const Format& format); static bool has_normal(const Format& format); static bool has_tex_coord(const Format& format); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 0956a9047b..ea15deb169 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -362,14 +362,13 @@ void GLGizmoFlatten::update_planes() for (auto& plane : m_planes) { #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL GLModel::Geometry init_data; - const GLModel::Geometry::EIndexType index_type = (plane.vertices.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT; - init_data.format = { GLModel::Geometry::EPrimitiveType::TriangleFan, GLModel::Geometry::EVertexLayout::P3N3, index_type }; + init_data.format = { GLModel::Geometry::EPrimitiveType::TriangleFan, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(plane.vertices.size()) }; init_data.reserve_vertices(plane.vertices.size()); init_data.reserve_indices(plane.vertices.size()); // vertices + indices for (size_t i = 0; i < plane.vertices.size(); ++i) { init_data.add_vertex((Vec3f)plane.vertices[i].cast(), (Vec3f)plane.normal.cast()); - if (index_type == GLModel::Geometry::EIndexType::USHORT) + if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT) init_data.add_ushort_index((unsigned short)i); else init_data.add_uint_index((unsigned int)i); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 0fc57c909d..82a816d1b9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -1189,8 +1189,7 @@ void TriangleSelectorGUI::update_paint_contour() GLModel::Geometry init_data; const std::vector contour_edges = this->get_seed_fill_contour(); - const GLModel::Geometry::EIndexType index_type = (2 * contour_edges.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT; - init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, index_type }; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::index_type(2 * contour_edges.size()) }; init_data.reserve_vertices(2 * contour_edges.size()); init_data.reserve_indices(2 * contour_edges.size()); // vertices + indices @@ -1199,7 +1198,7 @@ void TriangleSelectorGUI::update_paint_contour() init_data.add_vertex(m_vertices[edge(0)].v); init_data.add_vertex(m_vertices[edge(1)].v); vertices_count += 2; - if (index_type == GLModel::Geometry::EIndexType::USHORT) + if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT) init_data.add_ushort_line((unsigned short)vertices_count - 2, (unsigned short)vertices_count - 1); else init_data.add_uint_line(vertices_count - 2, vertices_count - 1); diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 68762c67a6..3787abb2f7 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -189,8 +189,7 @@ void MeshClipper::recalculate_triangles() m_model.reset(); GLModel::Geometry init_data; - const GLModel::Geometry::EIndexType index_type = (m_triangles2d.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT; - init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, index_type }; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(m_triangles2d.size()) }; init_data.reserve_vertices(m_triangles2d.size()); init_data.reserve_indices(m_triangles2d.size()); @@ -200,7 +199,7 @@ void MeshClipper::recalculate_triangles() init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast(), (Vec3f)up.cast()); init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast(), (Vec3f)up.cast()); const size_t idx = it - m_triangles2d.cbegin(); - if (index_type == GLModel::Geometry::EIndexType::USHORT) + if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT) init_data.add_ushort_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2); else init_data.add_uint_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2); From a28dea0617d0342c79cadf99f64f72b5d06d0286 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 10 Feb 2022 15:11:22 +0100 Subject: [PATCH 14/23] Follow-up of 6b041429f6f137add530c900c3160d431286366f - Fixed uvmapping of printbed texture --- src/slic3r/GUI/3DBed.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 0facce33c4..244a802dbc 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -334,7 +334,7 @@ void Bed3D::init_triangles() unsigned int vertices_counter = 0; for (const Vec2f& v : triangles) { const Vec3f p = { v.x(), v.y(), GROUND_Z }; - init_data.add_vertex(p, (Vec2f)v.cwiseProduct(inv_size).eval()); + init_data.add_vertex(p, (Vec2f)(v - min).cwiseProduct(inv_size).eval()); ++vertices_counter; if (vertices_counter % 3 == 0) { if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT) From aa4714a239d9d224708156c6c15a0d239a7c8a71 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 10 Feb 2022 15:17:24 +0100 Subject: [PATCH 15/23] Tech ENABLE_GLBEGIN_GLEND_REMOVAL - Removed OpenGL legacy calls from GLCanvas3D::init() --- src/slic3r/GUI/GLCanvas3D.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ee16aefef7..398eef1de8 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1157,6 +1157,7 @@ bool GLCanvas3D::init() glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); +#if !ENABLE_GLBEGIN_GLEND_REMOVAL // Set antialiasing / multisampling glsafe(::glDisable(GL_LINE_SMOOTH)); glsafe(::glDisable(GL_POLYGON_SMOOTH)); @@ -1186,6 +1187,7 @@ bool GLCanvas3D::init() // A handy trick -- have surface material mirror the color. glsafe(::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)); glsafe(::glEnable(GL_COLOR_MATERIAL)); +#endif // !ENABLE_GLBEGIN_GLEND_REMOVAL if (m_multisample_allowed) glsafe(::glEnable(GL_MULTISAMPLE)); @@ -5868,11 +5870,11 @@ void GLCanvas3D::_render_sla_slices() if (!bottom_obj_triangles.empty() || !top_obj_triangles.empty() || !bottom_sup_triangles.empty() || !top_sup_triangles.empty()) { for (const SLAPrintObject::Instance& inst : obj->instances()) { glsafe(::glPushMatrix()); - glsafe(::glTranslated(unscale(inst.shift.x()), unscale(inst.shift.y()), 0)); - glsafe(::glRotatef(Geometry::rad2deg(inst.rotation), 0.0, 0.0, 1.0)); + glsafe(::glTranslated(unscale(inst.shift.x()), unscale(inst.shift.y()), 0.0)); + glsafe(::glRotatef(Geometry::rad2deg(inst.rotation), 0.0f, 0.0f, 1.0f)); if (obj->is_left_handed()) // The polygons are mirrored by X. - glsafe(::glScalef(-1.0, 1.0, 1.0)); + glsafe(::glScalef(-1.0f, 1.0f, 1.0f)); glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glColor3f(1.0f, 0.37f, 0.0f)); if (!bottom_obj_triangles.empty()) { From c9ff260d13690eb32527900599487712a77e1657 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 11 Feb 2022 09:15:56 +0100 Subject: [PATCH 16/23] Tech ENABLE_GLBEGIN_GLEND_REMOVAL - SLA caps rendered using GLModel --- src/slic3r/GUI/GLCanvas3D.cpp | 100 ++++++++++++++++++++++++++++++++-- src/slic3r/GUI/GLCanvas3D.hpp | 13 ++++- 2 files changed, 108 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 398eef1de8..db15bdce5f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5802,32 +5802,81 @@ void GLCanvas3D::_render_sla_slices() if (!obj->is_step_done(slaposSliceSupports)) continue; +#if ENABLE_GLBEGIN_GLEND_REMOVAL + SlaCap::ObjectIdToModelsMap::iterator it_caps_bottom = m_sla_caps[0].triangles.find(i); + SlaCap::ObjectIdToModelsMap::iterator it_caps_top = m_sla_caps[1].triangles.find(i); +#else SlaCap::ObjectIdToTrianglesMap::iterator it_caps_bottom = m_sla_caps[0].triangles.find(i); SlaCap::ObjectIdToTrianglesMap::iterator it_caps_top = m_sla_caps[1].triangles.find(i); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL { if (it_caps_bottom == m_sla_caps[0].triangles.end()) it_caps_bottom = m_sla_caps[0].triangles.emplace(i, SlaCap::Triangles()).first; if (!m_sla_caps[0].matches(clip_min_z)) { m_sla_caps[0].z = clip_min_z; +#if ENABLE_GLBEGIN_GLEND_REMOVAL + it_caps_bottom->second.object.reset(); + it_caps_bottom->second.supports.reset(); +#else it_caps_bottom->second.object.clear(); it_caps_bottom->second.supports.clear(); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL } if (it_caps_top == m_sla_caps[1].triangles.end()) it_caps_top = m_sla_caps[1].triangles.emplace(i, SlaCap::Triangles()).first; if (!m_sla_caps[1].matches(clip_max_z)) { m_sla_caps[1].z = clip_max_z; +#if ENABLE_GLBEGIN_GLEND_REMOVAL + it_caps_top->second.object.reset(); + it_caps_top->second.supports.reset(); +#else it_caps_top->second.object.clear(); it_caps_top->second.supports.clear(); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL } } +#if ENABLE_GLBEGIN_GLEND_REMOVAL + GLModel& bottom_obj_triangles = it_caps_bottom->second.object; + GLModel& bottom_sup_triangles = it_caps_bottom->second.supports; + GLModel& top_obj_triangles = it_caps_top->second.object; + GLModel& top_sup_triangles = it_caps_top->second.supports; +#else Pointf3s &bottom_obj_triangles = it_caps_bottom->second.object; Pointf3s &bottom_sup_triangles = it_caps_bottom->second.supports; Pointf3s &top_obj_triangles = it_caps_top->second.object; Pointf3s &top_sup_triangles = it_caps_top->second.supports; +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL +#if ENABLE_GLBEGIN_GLEND_REMOVAL + auto init_model = [](GLModel& model, const Pointf3s& triangles, const ColorRGBA& color) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::index_type(triangles.size()) }; + init_data.reserve_vertices(triangles.size()); + init_data.reserve_indices(triangles.size() / 3); + init_data.color = color; + + unsigned int vertices_count = 0; + for (const Vec3d& v : triangles) { + init_data.add_vertex((Vec3f)v.cast()); + ++vertices_count; + if (vertices_count % 3 == 0) { + if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT) + init_data.add_ushort_triangle((unsigned short)vertices_count - 3, (unsigned short)vertices_count - 2, (unsigned short)vertices_count - 1); + else + init_data.add_uint_triangle(vertices_count - 3, vertices_count - 2, vertices_count - 1); + } + } + + if (!init_data.is_empty()) + model.init_from(std::move(init_data)); + }; + + if ((!bottom_obj_triangles.is_initialized() || !bottom_sup_triangles.is_initialized() || + !top_obj_triangles.is_initialized() || !top_sup_triangles.is_initialized()) && !obj->get_slice_index().empty()) { +#else if ((bottom_obj_triangles.empty() || bottom_sup_triangles.empty() || top_obj_triangles.empty() || top_sup_triangles.empty()) && - !obj->get_slice_index().empty()) - { + !obj->get_slice_index().empty()) { +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL double layer_height = print->default_object_config().layer_height.value; double initial_layer_height = print->material_config().initial_layer_height.value; bool left_handed = obj->is_left_handed(); @@ -5842,31 +5891,73 @@ void GLCanvas3D::_render_sla_slices() const SliceRecord& slice_high = obj->closest_slice_to_print_level(key_high, coord_t(SCALED_EPSILON)); // Offset to avoid OpenGL Z fighting between the object's horizontal surfaces and the triangluated surfaces of the cuts. - double plane_shift_z = 0.002; + const double plane_shift_z = 0.002; if (slice_low.is_valid()) { const ExPolygons& obj_bottom = slice_low.get_slice(soModel); const ExPolygons& sup_bottom = slice_low.get_slice(soSupport); +#if ENABLE_GLBEGIN_GLEND_REMOVAL + // calculate model bottom cap + if (!bottom_obj_triangles.is_initialized() && !obj_bottom.empty()) + init_model(bottom_obj_triangles, triangulate_expolygons_3d(obj_bottom, clip_min_z - plane_shift_z, !left_handed), { 1.0f, 0.37f, 0.0f, 1.0f }); + // calculate support bottom cap + if (!bottom_sup_triangles.is_initialized() && !sup_bottom.empty()) + init_model(bottom_sup_triangles, triangulate_expolygons_3d(sup_bottom, clip_min_z - plane_shift_z, !left_handed), { 1.0f, 0.0f, 0.37f, 1.0f }); +#else // calculate model bottom cap if (bottom_obj_triangles.empty() && !obj_bottom.empty()) bottom_obj_triangles = triangulate_expolygons_3d(obj_bottom, clip_min_z - plane_shift_z, ! left_handed); // calculate support bottom cap if (bottom_sup_triangles.empty() && !sup_bottom.empty()) - bottom_sup_triangles = triangulate_expolygons_3d(sup_bottom, clip_min_z - plane_shift_z, ! left_handed); + bottom_sup_triangles = triangulate_expolygons_3d(sup_bottom, clip_min_z - plane_shift_z, !left_handed); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL } if (slice_high.is_valid()) { const ExPolygons& obj_top = slice_high.get_slice(soModel); const ExPolygons& sup_top = slice_high.get_slice(soSupport); +#if ENABLE_GLBEGIN_GLEND_REMOVAL + // calculate model top cap + if (!top_obj_triangles.is_initialized() && !obj_top.empty()) + init_model(top_obj_triangles, triangulate_expolygons_3d(obj_top, clip_max_z + plane_shift_z, left_handed), { 1.0f, 0.37f, 0.0f, 1.0f }); + // calculate support top cap + if (!top_sup_triangles.is_initialized() && !sup_top.empty()) + init_model(top_sup_triangles, triangulate_expolygons_3d(sup_top, clip_max_z + plane_shift_z, left_handed), { 1.0f, 0.0f, 0.37f, 1.0f }); +#else // calculate model top cap if (top_obj_triangles.empty() && !obj_top.empty()) top_obj_triangles = triangulate_expolygons_3d(obj_top, clip_max_z + plane_shift_z, left_handed); // calculate support top cap if (top_sup_triangles.empty() && !sup_top.empty()) top_sup_triangles = triangulate_expolygons_3d(sup_top, clip_max_z + plane_shift_z, left_handed); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL } } +#if ENABLE_GLBEGIN_GLEND_REMOVAL + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + + for (const SLAPrintObject::Instance& inst : obj->instances()) { + glsafe(::glPushMatrix()); + glsafe(::glTranslated(unscale(inst.shift.x()), unscale(inst.shift.y()), 0.0)); + glsafe(::glRotatef(Geometry::rad2deg(inst.rotation), 0.0f, 0.0f, 1.0f)); + if (obj->is_left_handed()) + // The polygons are mirrored by X. + glsafe(::glScalef(-1.0f, 1.0f, 1.0f)); + + bottom_obj_triangles.render(); + top_obj_triangles.render(); + bottom_sup_triangles.render(); + top_sup_triangles.render(); + + glsafe(::glPopMatrix()); + } + + shader->stop_using(); + } +#else if (!bottom_obj_triangles.empty() || !top_obj_triangles.empty() || !bottom_sup_triangles.empty() || !top_sup_triangles.empty()) { for (const SLAPrintObject::Instance& inst : obj->instances()) { glsafe(::glPushMatrix()); @@ -5898,6 +5989,7 @@ void GLCanvas3D::_render_sla_slices() glsafe(::glPopMatrix()); } } +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 33e17d5dd2..57f173c7b2 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -335,6 +335,16 @@ class GLCanvas3D struct SlaCap { +#if ENABLE_GLBEGIN_GLEND_REMOVAL + struct Triangles + { + GLModel object; + GLModel supports; + }; + typedef std::map ObjectIdToModelsMap; + double z; + ObjectIdToModelsMap triangles; +#else struct Triangles { Pointf3s object; @@ -343,6 +353,7 @@ class GLCanvas3D typedef std::map ObjectIdToTrianglesMap; double z; ObjectIdToTrianglesMap triangles; +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL SlaCap() { reset(); } void reset() { z = DBL_MAX; triangles.clear(); } @@ -473,7 +484,7 @@ private: std::array m_clipping_planes; ClippingPlane m_camera_clipping_plane; bool m_use_clipping_planes; - SlaCap m_sla_caps[2]; + std::array m_sla_caps; std::string m_sidebar_field; // when true renders an extra frame by not resetting m_dirty to false // see request_extra_frame() From 0c74081f7dd33ebeb4e6f2c179e728520b19febb Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 11 Feb 2022 11:29:00 +0100 Subject: [PATCH 17/23] Follow-up of afcac6e2ea306fbbb12428e360c0fe70b2a3f12d - Fixed GLGizmoSlaSupports::render_points() --- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 3406c58a29..fa0b27269a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -127,6 +127,14 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) if (! has_points && ! has_holes) return; +#if ENABLE_GLBEGIN_GLEND_REMOVAL + GLShaderProgram* shader = wxGetApp().get_shader(picking ? "flat" : "gouraud_light"); + if (shader == nullptr) + return; + + shader->start_using(); + ScopeGuard guard([shader]() { shader->stop_using(); }); +#else GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light"); if (shader != nullptr) shader->start_using(); @@ -134,6 +142,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) if (shader != nullptr) shader->stop_using(); }); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); @@ -177,11 +186,12 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) #if ENABLE_GLBEGIN_GLEND_REMOVAL m_cone.set_color(render_color); m_sphere.set_color(render_color); + if (!picking) #else m_cone.set_color(-1, render_color); m_sphere.set_color(-1, render_color); -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL if (shader && !picking) +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL shader->set_uniform("emission_factor", 0.5f); // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. @@ -236,9 +246,9 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) m_cylinder.set_color(render_color); #else m_cylinder.set_color(-1, render_color); + if (shader != nu) #endif // ENABLE_GLBEGIN_GLEND_REMOVAL - if (shader) - shader->set_uniform("emission_factor", 0.5f); + shader->set_uniform("emission_factor", 0.5f); for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) { if (is_mesh_point_clipped(drain_hole.pos.cast())) continue; From a1ff2bb7d7a3c26d3f4341e9fe1f4d47fad50e67 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 11 Feb 2022 11:38:36 +0100 Subject: [PATCH 18/23] Fix for #6364 - MacOS / Minimize window / Shortcut CMD + M There was a bug in wxWidgets 3.1.4. (wxWidgets/wxWidgets#18998) So, we cherry-picked fixing commit from the upstream wxWidgets to our "v3.1.4-patched" Note: It's update of the Sha256 for wxWidgets in master branch --- deps/wxWidgets/wxWidgets.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index 2730854ebc..1ca6735cc3 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -12,8 +12,8 @@ endif() prusaslicer_add_cmake_project(wxWidgets # GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" # GIT_TAG tm_cross_compile #${_wx_git_tag} - URL https://github.com/prusa3d/wxWidgets/archive/73f029adfcc82fb3aa4b01220a013f716e57d110.zip - URL_HASH SHA256=c35fe0187db497b6a3f477e24ed5e307028657ff0c2554385810b6e7961ad2e4 + URL https://github.com/prusa3d/wxWidgets/archive/489f6118256853cf5b299d595868641938566cdb.zip + URL_HASH SHA256=5b22d465377cedd8044bba69bea958b248953fd3628c1de4913a84d4e6f6175b DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG CMAKE_ARGS -DwxBUILD_PRECOMP=ON From 5089d1460d172e29164e313e806457181a67daef Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 11 Feb 2022 11:47:29 +0100 Subject: [PATCH 19/23] Removed obsolete member variable GLCanvas3D::m_color_by --- src/slic3r/GUI/3DScene.cpp | 8 ++------ src/slic3r/GUI/3DScene.hpp | 2 -- src/slic3r/GUI/GCodeViewer.cpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 10 ++-------- src/slic3r/GUI/GLCanvas3D.hpp | 4 ---- 5 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index c9b647b07c..23ca974dd7 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -684,13 +684,12 @@ std::vector GLVolumeCollection::load_object( const ModelObject *model_object, int obj_idx, const std::vector &instance_idxs, - const std::string &color_by, bool opengl_initialized) { std::vector volumes_idx; for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++volume_idx) for (int instance_idx : instance_idxs) - volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by, opengl_initialized)); + volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, opengl_initialized)); return volumes_idx; } @@ -699,16 +698,13 @@ int GLVolumeCollection::load_object_volume( int obj_idx, int volume_idx, int instance_idx, - const std::string &color_by, bool opengl_initialized) { const ModelVolume *model_volume = model_object->volumes[volume_idx]; const int extruder_id = model_volume->extruder_id(); const ModelInstance *instance = model_object->instances[instance_idx]; const TriangleMesh &mesh = model_volume->mesh(); - ColorRGBA color = GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4]; - color.a(model_volume->is_model_part() ? 1.0f : 0.5f); - this->volumes.emplace_back(new GLVolume(color)); + this->volumes.emplace_back(new GLVolume()); GLVolume& v = *this->volumes.back(); v.set_color(color_from_model_volume(*model_volume)); #if ENABLE_SMOOTH_NORMALS diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 5aaa759f2f..c2e4e587ce 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -593,7 +593,6 @@ public: const ModelObject *model_object, int obj_idx, const std::vector &instance_idxs, - const std::string &color_by, bool opengl_initialized); int load_object_volume( @@ -601,7 +600,6 @@ public: int obj_idx, int volume_idx, int instance_idx, - const std::string &color_by, bool opengl_initialized); // Load SLA auxiliary GLVolumes (for support trees or pad). diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index d54c28293f..c86d16a1f0 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -2306,7 +2306,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) } size_t current_volumes_count = m_shells.volumes.volumes.size(); - m_shells.volumes.load_object(model_obj, object_id, instance_ids, "object", initialized); + m_shells.volumes.load_object(model_obj, object_id, instance_ids, initialized); // adjust shells' z if raft is present const SlicingParameters& slicing_parameters = obj->slicing_parameters(); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index db15bdce5f..732c75589e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1106,7 +1106,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed) , m_moving(false) , m_tab_down(false) , m_cursor_type(Standard) - , m_color_by("volume") , m_reload_delayed(false) #if ENABLE_RENDER_PICKING_PASS , m_show_picking_texture(false) @@ -1358,11 +1357,6 @@ void GLCanvas3D::bed_shape_changed() m_dirty = true; } -void GLCanvas3D::set_color_by(const std::string& value) -{ - m_color_by = value; -} - void GLCanvas3D::refresh_camera_scene_box() { wxGetApp().plater()->get_camera().set_scene_box(scene_bounding_box()); @@ -1807,7 +1801,7 @@ std::vector GLCanvas3D::load_object(const ModelObject& model_object, int ob instance_idxs.emplace_back(i); } } - return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_initialized); + return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_initialized); } std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) @@ -2032,7 +2026,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // Note the index of the loaded volume, so that we can reload the main model GLVolume with the hollowed mesh // later in this function. it->volume_idx = m_volumes.volumes.size(); - m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_initialized); + m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_initialized); m_volumes.volumes.back()->geometry_id = key.geometry_id; update_object_list = true; } else { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 57f173c7b2..a3c302baef 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -522,8 +522,6 @@ private: // I just don't want to do it now before a release (Lukas Matena 24.3.2019) bool m_render_sla_auxiliaries; - std::string m_color_by; - bool m_reload_delayed; #if ENABLE_RENDER_PICKING_PASS @@ -692,8 +690,6 @@ public: bool get_use_clipping_planes() const { return m_use_clipping_planes; } const std::array &get_clipping_planes() const { return m_clipping_planes; }; - void set_color_by(const std::string& value); - void refresh_camera_scene_box(); BoundingBoxf3 volumes_bounding_box() const; From 0835e117d588f98da14c0636ad514882ee9313f3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 11 Feb 2022 12:06:48 +0100 Subject: [PATCH 20/23] Refactoring in GLCanvas3D::LayersEditing::render_volumes() --- src/slic3r/GUI/GLCanvas3D.cpp | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 732c75589e..edd62ce0ab 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -495,17 +495,17 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G { assert(this->is_allowed()); assert(this->last_object_id != -1); + + GLShaderProgram* current_shader = wxGetApp().get_current_shader(); + ScopeGuard guard([current_shader]() { if (current_shader != nullptr) current_shader->start_using(); }); + if (current_shader != nullptr) + current_shader->stop_using(); + GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height"); if (shader == nullptr) return; - GLShaderProgram* current_shader = wxGetApp().get_current_shader(); - if (shader->get_id() != current_shader->get_id()) - // The layer editing shader is not yet active. Activate it. - shader->start_using(); - else - // The layer editing shader was already active. - current_shader = nullptr; + shader->start_using(); generate_layer_height_texture(); @@ -516,10 +516,10 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G shader->set_uniform("z_cursor_band_width", float(this->band_width)); // Initialize the layer height texture mapping. - GLsizei w = (GLsizei)m_layers_texture.width; - GLsizei h = (GLsizei)m_layers_texture.height; - GLsizei half_w = w / 2; - GLsizei half_h = h / 2; + const GLsizei w = (GLsizei)m_layers_texture.width; + const GLsizei h = (GLsizei)m_layers_texture.height; + const GLsizei half_w = w / 2; + const GLsizei half_h = h / 2; glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0)); @@ -528,17 +528,15 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4)); for (const GLVolume* glvolume : volumes.volumes) { // Render the object using the layer editing shader and texture. - if (! glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier) + if (!glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier) continue; shader->set_uniform("volume_world_matrix", glvolume->world_matrix()); - shader->set_uniform("object_max_z", GLfloat(0)); + shader->set_uniform("object_max_z", 0.0f); glvolume->render(); } // Revert back to the previous shader. glBindTexture(GL_TEXTURE_2D, 0); - if (current_shader != nullptr) - current_shader->start_using(); } void GLCanvas3D::LayersEditing::adjust_layer_height_profile() From 7685e707434613e1a8ba50a189da61061e1a26d0 Mon Sep 17 00:00:00 2001 From: ole00 Date: Fri, 11 Feb 2022 22:06:47 +0000 Subject: [PATCH 21/23] SLA: added pwmx format exporter Pwmx format is used by Anycubic Photon Mono X printers. File format structure and rle encoding implemented according to: https://github.com/sn4k3/UVtools https://github.com/ole00/pwmx_info --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Format/pwmx.cpp | 509 ++++++++++++++++++++ src/libslic3r/Format/pwmx.hpp | 36 ++ src/libslic3r/SLA/RasterBase.cpp | 48 ++ src/libslic3r/SLA/RasterBase.hpp | 4 + src/libslic3r/SLAPrint.cpp | 18 + src/libslic3r/SLAPrint.hpp | 38 +- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 26 +- src/slic3r/GUI/GUI_App.cpp | 2 +- 9 files changed, 647 insertions(+), 36 deletions(-) create mode 100644 src/libslic3r/Format/pwmx.cpp create mode 100644 src/libslic3r/Format/pwmx.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index d4c8d7edc1..ea663f2e1f 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -98,6 +98,8 @@ set(SLIC3R_SOURCES Format/SL1.cpp Format/SL1_SVG.hpp Format/SL1_SVG.cpp + Format/pwmx.hpp + Format/pwmx.cpp GCode/ThumbnailData.cpp GCode/ThumbnailData.hpp GCode/Thumbnails.cpp diff --git a/src/libslic3r/Format/pwmx.cpp b/src/libslic3r/Format/pwmx.cpp new file mode 100644 index 0000000000..99785b8d08 --- /dev/null +++ b/src/libslic3r/Format/pwmx.cpp @@ -0,0 +1,509 @@ +#include "pwmx.hpp" +#include "GCode/ThumbnailData.hpp" + +#include +#include +#include + + +#define TAG_INTRO "ANYCUBIC\0\0\0\0" +#define TAG_HEADER "HEADER\0\0\0\0\0\0" +#define TAG_PREVIEW "PREVIEW\0\0\0\0\0" +#define TAG_LAYERS "LAYERDEF\0\0\0\0" + +#define CFG_LIFT_DISTANCE "LIFT_DISTANCE" +#define CFG_LIFT_SPEED "LIFT_SPEED" +#define CFG_RETRACT_SPEED "RETRACT_SPEED" +#define CFG_DELAY_BEFORE_EXPOSURE "DELAY_BEFORE_EXPOSURE" +#define CFG_BOTTOM_LIFT_SPEED "BOTTOM_LIFT_SPEED" +#define CFG_BOTTOM_LIFT_DISTANCE "BOTTOM_LIFT_DISTANCE" + +#define PREV_W 224 +#define PREV_H 168 +#define PREV_DPI 42 + +#define LAYER_SIZE_ESTIMATE (32 * 1024) + + +namespace Slic3r { + +using ConfMap = std::map; + +typedef struct pwmx_format_intro +{ + char tag[12]; + std::uint32_t version; // value 1 + std::uint32_t area_num; // unknown - usually 4 + std::uint32_t header_data_offset; + std::float_t intro24; // unknown - usually 0 + std::uint32_t preview_data_offset; + std::float_t intro32; // unknown + std::uint32_t layer_data_offset; + std::float_t intro40; // unknown + std::uint32_t image_data_offset; +} pwmx_format_intro; + +typedef struct pwmx_format_header +{ + char tag[12]; + std::uint32_t payload_size; + std::float_t pixel_size_um; + std::float_t layer_height_mm; + std::float_t exposure_time_s; + std::float_t delay_before_exposure_s; + std::float_t bottom_exposure_time_s; + std::float_t bottom_layer_count; + std::float_t lift_distance_mm; + std::float_t lift_speed_mms; + std::float_t retract_speed_mms; + std::float_t volume_ml; + std::uint32_t antialiasing; + std::uint32_t res_x; + std::uint32_t res_y; + std::float_t weight_g; + std::float_t price; + std::uint32_t price_currency; + std::uint32_t per_layer_override; // ? unknown meaning ? + std::uint32_t print_time_s; + std::uint32_t transition_layer_count; + std::uint32_t unknown; // ? usually 0 ? + +} pwmx_format_header; + +typedef struct pwmx_format_preview +{ + char tag[12]; + std::uint32_t payload_size; + std::uint32_t preview_w; + std::uint32_t preview_dpi; + std::uint32_t preview_h; + // raw image data in BGR565 format + std::uint8_t pixels[PREV_W * PREV_H * 2]; +} pwmx_format_preview; + +typedef struct pwmx_format_layers_header +{ + char tag[12]; + std::uint32_t payload_size; + std::uint32_t layer_count; +} pwmx_format_layers_header; + +typedef struct pwmx_format_layer +{ + std::uint32_t image_offset; + std::uint32_t image_size; + std::float_t lift_distance_mm; + std::float_t lift_speed_mms; + std::float_t exposure_time_s; + std::float_t layer_height_mm; + std::float_t layer44; // unkown - usually 0 + std::float_t layer48; // unkown - usually 0 +} pwmx_format_layer; + +typedef struct pwmx_format_misc +{ + std::float_t bottom_layer_height_mm; + std::float_t bottom_lift_distance_mm; + std::float_t bottom_lift_speed_mms; + +} pwmx_format_misc; + +class PwmxFormatConfigDef : public ConfigDef +{ +public: + PwmxFormatConfigDef() + { + add(CFG_LIFT_DISTANCE, coFloat); + add(CFG_LIFT_SPEED, coFloat); + add(CFG_RETRACT_SPEED, coFloat); + add(CFG_DELAY_BEFORE_EXPOSURE, coFloat); + add(CFG_BOTTOM_LIFT_DISTANCE, coFloat); + add(CFG_BOTTOM_LIFT_SPEED, coFloat); + } +}; + +class PwmxFormatDynamicConfig : public DynamicConfig +{ +public: + PwmxFormatDynamicConfig(){}; + const ConfigDef *def() const override { return &config_def; } + +private: + PwmxFormatConfigDef config_def; +}; + +namespace { + +const char *get_cfg_value(const DynamicConfig &cfg, + const std::string & key, + const std::string & def = "0") +{ + std::string ret; + + if (cfg.has(key)) { + auto opt = cfg.option(key); + if (opt) { + ret = opt->serialize(); + } else { + return def.c_str(); + } + } else { + return def.c_str(); + } + + return ret.c_str(); +} + +template void crop_value(T &val, T val_min, T val_max) +{ + if (val < val_min) { + val = val_min; + } else if (val > val_max) { + val = val_max; + } +} + +void fill_preview(pwmx_format_preview &p, + pwmx_format_misc &m, + ThumbnailsList &thumbnails) +{ + + p.preview_w = PREV_W; + p.preview_h = PREV_H; + p.preview_dpi = PREV_DPI; + p.payload_size = sizeof(p) - sizeof(p.tag) - sizeof(p.payload_size); + + std::memset(p.pixels, 0 , sizeof(p.pixels)); + if (!thumbnails.empty()) { + std::uint32_t dst_index; + std::uint32_t i = 0; + size_t len; + size_t pixel_x = 0; + auto t = thumbnails[0]; //use the first thumbnail + len = t.pixels.size(); + //sanity check + if (len != PREV_W * PREV_H * 4) { + printf("incorrect thumbnail size. expected %ix%i\n", PREV_W, PREV_H); + return; + } + // rearange pixels: they seem to be stored from bottom to top. + dst_index = (PREV_W * (PREV_H - 1) * 2); + while (i < len) { + std::uint32_t pixel; + std::uint32_t r = t.pixels[i++]; + std::uint32_t g = t.pixels[i++]; + std::uint32_t b = t.pixels[i++]; + i++; // Alpha + // convert to BGRA565 + pixel = ((b >> 3) << 11) | ((g >>2) << 5) | (r >> 3); + p.pixels[dst_index++] = pixel & 0xFF; + p.pixels[dst_index++] = (pixel >> 8) & 0xFF; + pixel_x++; + if (pixel_x == PREV_W) { + pixel_x = 0; + dst_index -= (PREV_W * 4); + } + } + } +} + + +void fill_header(pwmx_format_header &h, + pwmx_format_misc &m, + const SLAPrint &print, + std::uint32_t layer_count) +{ + std::float_t bottle_weight_g; + std::float_t bottle_volume_ml; + std::float_t bottle_cost; + std::float_t material_density; + auto & cfg = print.full_print_config(); + std::string mnotes = cfg.option("material_notes")->serialize(); + // create a config parser from the material notes + Slic3r::PwmxFormatDynamicConfig mat_cfg; + SLAPrintStatistics stats = print.print_statistics(); + + // sanitize the string config + boost::replace_all(mnotes, "\\n", "\n"); + boost::replace_all(mnotes, "\\r", "\r"); + mat_cfg.load_from_ini_string(mnotes, + ForwardCompatibilitySubstitutionRule::Enable); + + h.layer_height_mm = std::atof(get_cfg_value(cfg, "layer_height")); + m.bottom_layer_height_mm = std::atof( + get_cfg_value(cfg, "initial_layer_height")); + h.exposure_time_s = std::atof(get_cfg_value(cfg, "exposure_time")); + h.bottom_exposure_time_s = std::atof( + get_cfg_value(cfg, "initial_exposure_time")); + h.bottom_layer_count = std::atof(get_cfg_value(cfg, "faded_layers")); + if (layer_count < h.bottom_layer_count) { + h.bottom_layer_count = layer_count; + } + h.res_x = std::atol(get_cfg_value(cfg, "display_pixels_x")); + h.res_y = std::atol(get_cfg_value(cfg, "display_pixels_y")); + bottle_weight_g = std::atof(get_cfg_value(cfg, "bottle_weight")) * 1000.0f; + bottle_volume_ml = std::atof(get_cfg_value(cfg, "bottle_volume")); + bottle_cost = std::atof(get_cfg_value(cfg, "bottle_cost")); + material_density = bottle_weight_g / bottle_volume_ml; + + h.volume_ml = (stats.objects_used_material + stats.support_used_material) / 1000; + h.weight_g = h.volume_ml * material_density; + h.price = (h.volume_ml * bottle_cost) / bottle_volume_ml; + h.price_currency = '$'; + h.antialiasing = 1; + h.per_layer_override = 0; + + // TODO - expose these variables to the UI rather than using material notes + h.delay_before_exposure_s = std::atof( + get_cfg_value(mat_cfg, CFG_DELAY_BEFORE_EXPOSURE, "0.5")); + crop_value(h.delay_before_exposure_s, 0.0f, 1000.0f); + + h.lift_distance_mm = std::atof( + get_cfg_value(mat_cfg, CFG_LIFT_DISTANCE, "8.0")); + crop_value(h.lift_distance_mm, 0.0f, 100.0f); + + if (mat_cfg.has(CFG_BOTTOM_LIFT_DISTANCE)) { + m.bottom_lift_distance_mm = std::atof( + get_cfg_value(mat_cfg, CFG_BOTTOM_LIFT_DISTANCE, "8.0")); + crop_value(h.lift_distance_mm, 0.0f, 100.0f); + } else { + m.bottom_lift_distance_mm = h.lift_distance_mm; + } + + + h.lift_speed_mms = std::atof( + get_cfg_value(mat_cfg, CFG_LIFT_SPEED, "2.0")); + crop_value(m.bottom_lift_speed_mms, 0.1f, 20.0f); + + if (mat_cfg.has(CFG_BOTTOM_LIFT_SPEED)) { + m.bottom_lift_speed_mms = std::atof( + get_cfg_value(mat_cfg, CFG_BOTTOM_LIFT_SPEED, "2.0")); + crop_value(m.bottom_lift_speed_mms, 0.1f, 20.0f); + } else { + m.bottom_lift_speed_mms = h.lift_speed_mms; + } + + h.retract_speed_mms = std::atof( + get_cfg_value(mat_cfg, CFG_RETRACT_SPEED, "3.0")); + crop_value(h.lift_speed_mms, 0.1f, 20.0f); + + h.print_time_s = (h.bottom_layer_count * h.bottom_exposure_time_s) + + ((layer_count - h.bottom_layer_count) * + h.exposure_time_s) + + (layer_count * h.lift_distance_mm / h.retract_speed_mms) + + (layer_count * h.lift_distance_mm / h.lift_speed_mms) + + (layer_count * h.delay_before_exposure_s); + + + h.payload_size = sizeof(h) - sizeof(h.tag) - sizeof(h.payload_size); + h.pixel_size_um = 50; +} + +} // namespace + +std::unique_ptr PwmxArchive::create_raster() const +{ + sla::Resolution res; + sla::PixelDim pxdim; + std::array mirror; + + double w = m_cfg.display_width.getFloat(); + double h = m_cfg.display_height.getFloat(); + auto pw = size_t(m_cfg.display_pixels_x.getInt()); + auto ph = size_t(m_cfg.display_pixels_y.getInt()); + + mirror[X] = m_cfg.display_mirror_x.getBool(); + mirror[Y] = m_cfg.display_mirror_y.getBool(); + + auto ro = m_cfg.display_orientation.getInt(); + sla::RasterBase::Orientation orientation = + ro == sla::RasterBase::roPortrait ? sla::RasterBase::roPortrait : + sla::RasterBase::roLandscape; + + if (orientation == sla::RasterBase::roPortrait) { + std::swap(w, h); + std::swap(pw, ph); + } + + res = sla::Resolution{pw, ph}; + pxdim = sla::PixelDim{w / pw, h / ph}; + sla::RasterBase::Trafo tr{orientation, mirror}; + + double gamma = m_cfg.gamma_correction.getFloat(); + + return sla::create_raster_grayscale_aa(res, pxdim, gamma, tr); +} + +sla::RasterEncoder PwmxArchive::get_encoder() const +{ + return sla::PWXRasterEncoder{}; +} + +// Endian safe write of little endian 32bit ints +static void pwmx_write_int32(std::ofstream &out, std::uint32_t val) +{ + const char i1 = (val & 0xFF); + const char i2 = (val >> 8) & 0xFF; + const char i3 = (val >> 16) & 0xFF; + const char i4 = (val >> 24) & 0xFF; + + out.write((const char *) &i1, 1); + out.write((const char *) &i2, 1); + out.write((const char *) &i3, 1); + out.write((const char *) &i4, 1); +} +static void pwmx_write_float(std::ofstream &out, std::float_t val) +{ + std::uint32_t *f = (std::uint32_t *) &val; + pwmx_write_int32(out, *f); +} + +static void pwmx_write_intro(std::ofstream &out, pwmx_format_intro &i) +{ + out.write(TAG_INTRO, sizeof(i.tag)); + pwmx_write_int32(out, i.version); + pwmx_write_int32(out, i.area_num); + pwmx_write_int32(out, i.header_data_offset); + pwmx_write_int32(out, i.intro24); + pwmx_write_int32(out, i.preview_data_offset); + pwmx_write_int32(out, i.intro32); + pwmx_write_int32(out, i.layer_data_offset); + pwmx_write_int32(out, i.intro40); + pwmx_write_int32(out, i.image_data_offset); +} + +static void pwmx_write_header(std::ofstream &out, pwmx_format_header &h) +{ + out.write(TAG_HEADER, sizeof(h.tag)); + pwmx_write_int32(out, h.payload_size); + pwmx_write_float(out, h.pixel_size_um); + pwmx_write_float(out, h.layer_height_mm); + pwmx_write_float(out, h.exposure_time_s); + pwmx_write_float(out, h.delay_before_exposure_s); + pwmx_write_float(out, h.bottom_exposure_time_s); + pwmx_write_float(out, h.bottom_layer_count); + pwmx_write_float(out, h.lift_distance_mm); + pwmx_write_float(out, h.lift_speed_mms); + pwmx_write_float(out, h.retract_speed_mms); + pwmx_write_float(out, h.volume_ml); + pwmx_write_int32(out, h.antialiasing); + pwmx_write_int32(out, h.res_x); + pwmx_write_int32(out, h.res_y); + pwmx_write_float(out, h.weight_g); + pwmx_write_float(out, h.price); + pwmx_write_int32(out, h.price_currency); + pwmx_write_int32(out, h.per_layer_override); + pwmx_write_int32(out, h.print_time_s); + pwmx_write_int32(out, h.transition_layer_count); + pwmx_write_int32(out, h.unknown); +} + +static void pwmx_write_preview(std::ofstream &out, pwmx_format_preview &p) +{ + out.write(TAG_PREVIEW, sizeof(p.tag)); + pwmx_write_int32(out, p.payload_size); + pwmx_write_int32(out, p.preview_w); + pwmx_write_int32(out, p.preview_dpi); + pwmx_write_int32(out, p.preview_h); + out.write((const char*) p.pixels, sizeof(p.pixels)); +} + +static void pwmx_write_layers_header(std::ofstream &out, pwmx_format_layers_header &h) +{ + out.write(TAG_LAYERS, sizeof(h.tag)); + pwmx_write_int32(out, h.payload_size); + pwmx_write_int32(out, h.layer_count); +} + +static void pwmx_write_layer(std::ofstream &out, pwmx_format_layer &l) +{ + pwmx_write_int32(out, l.image_offset); + pwmx_write_int32(out, l.image_size); + pwmx_write_float(out, l.lift_distance_mm); + pwmx_write_float(out, l.lift_speed_mms); + pwmx_write_float(out, l.exposure_time_s); + pwmx_write_float(out, l.layer_height_mm); + pwmx_write_float(out, l.layer44); + pwmx_write_float(out, l.layer48); +} + +void PwmxArchive::export_print(const std::string fname, + const SLAPrint & print, + ThumbnailsList & thumbnails, + const std::string &prjname) +{ + std::uint32_t layer_count = m_layers.size(); + + pwmx_format_intro intro = {0}; + pwmx_format_header header = {0}; + pwmx_format_preview preview = {0}; + pwmx_format_layers_header layers_header = {0}; + pwmx_format_misc misc = {0}; + std::vector layer_images; + std::uint32_t image_offset; + + intro.version = 1; + intro.area_num = 4; + intro.header_data_offset = sizeof(intro); + intro.preview_data_offset = sizeof(intro) + sizeof(header); + intro.layer_data_offset = intro.preview_data_offset + sizeof(preview); + intro.image_data_offset = intro.layer_data_offset + + sizeof(layers_header) + + (sizeof(pwmx_format_layer) * layer_count); + + fill_header(header, misc, print, layer_count); + fill_preview(preview, misc, thumbnails); + + try { + // open the file and write the contents + std::ofstream out; + out.open(fname, std::ios::binary | std::ios::out | std::ios::trunc); + pwmx_write_intro(out, intro); + pwmx_write_header(out, header); + pwmx_write_preview(out, preview); + + layers_header.payload_size = intro.image_data_offset - intro.layer_data_offset - + sizeof(layers_header.tag) - sizeof(layers_header.payload_size); + layers_header.layer_count = layer_count; + pwmx_write_layers_header(out, layers_header); + + //layers + layer_images.reserve(layer_count * LAYER_SIZE_ESTIMATE); + image_offset = intro.image_data_offset; + size_t i = 0; + for (const sla::EncodedRaster &rst : m_layers) { + pwmx_format_layer l; + std::memset(&l, 0, sizeof(l)); + l.image_offset = image_offset; + l.image_size = rst.size(); + if (i < header.bottom_layer_count) { + l.exposure_time_s = header.bottom_exposure_time_s; + l.layer_height_mm = misc.bottom_layer_height_mm; + l.lift_distance_mm = misc.bottom_lift_distance_mm; + l.lift_speed_mms = misc.bottom_lift_speed_mms; + } else { + l.exposure_time_s = header.exposure_time_s; + l.layer_height_mm = header.layer_height_mm; + l.lift_distance_mm = header.lift_distance_mm; + l.lift_speed_mms = header.lift_speed_mms; + } + image_offset += l.image_size; + pwmx_write_layer(out, l); + // add the rle encoded layer image into the buffer + const char* img_start = reinterpret_cast(rst.data()); + const char* img_end = img_start + rst.size(); + std::copy(img_start, img_end, std::back_inserter(layer_images)); + i++; + } + const char* img_buffer = reinterpret_cast(layer_images.data()); + out.write(img_buffer, layer_images.size()); + out.close(); + } catch(std::exception& e) { + BOOST_LOG_TRIVIAL(error) << e.what(); + // Rethrow the exception + throw; + } + +} + +} // namespace Slic3r diff --git a/src/libslic3r/Format/pwmx.hpp b/src/libslic3r/Format/pwmx.hpp new file mode 100644 index 0000000000..69cec100c3 --- /dev/null +++ b/src/libslic3r/Format/pwmx.hpp @@ -0,0 +1,36 @@ +#ifndef _SLIC3R_FORMAT_PWMX_HPP_ +#define _SLIC3R_FORMAT_PWMX_HPP_ + +#include + +#include "libslic3r/SLAPrint.hpp" + +namespace Slic3r { + +class PwmxArchive: public SLAArchive { + SLAPrinterConfig m_cfg; + +protected: + std::unique_ptr create_raster() const override; + sla::RasterEncoder get_encoder() const override; + + SLAPrinterConfig & cfg() { return m_cfg; } + const SLAPrinterConfig & cfg() const { return m_cfg; } + +public: + + PwmxArchive() = default; + explicit PwmxArchive(const SLAPrinterConfig &cfg): m_cfg(cfg) {} + explicit PwmxArchive(SLAPrinterConfig &&cfg): m_cfg(std::move(cfg)) {} + + void export_print(const std::string fname, + const SLAPrint &print, + ThumbnailsList &thumbnails, + const std::string &projectname = "") override; + bool uses_zipper_export() override {return false;} +}; + + +} // namespace Slic3r::sla + +#endif // _SLIC3R_FORMAT_PWMX_HPP_ diff --git a/src/libslic3r/SLA/RasterBase.cpp b/src/libslic3r/SLA/RasterBase.cpp index 0b6c45eff3..fd3f3e062d 100644 --- a/src/libslic3r/SLA/RasterBase.cpp +++ b/src/libslic3r/SLA/RasterBase.cpp @@ -16,6 +16,54 @@ const RasterBase::TMirroring RasterBase::MirrorX = {true, false}; const RasterBase::TMirroring RasterBase::MirrorY = {false, true}; const RasterBase::TMirroring RasterBase::MirrorXY = {true, true}; + + +static void pwx_get_pixel_span(const std::uint8_t* ptr, const std::uint8_t* end, + std::uint8_t& pixel, size_t& span_len) +{ + size_t max_len; + + span_len = 0; + pixel = (*ptr) & 0xF0; + // the maximum length of the span depends on the pixel color + max_len = (pixel == 0 || pixel == 0xF0) ? 0xFFF : 0xF; + while (((*ptr) & 0xF0) == pixel && ptr < end && span_len < max_len) { + span_len++; + ptr++; + } +} + +EncodedRaster PWXRasterEncoder::operator()(const void *ptr, size_t w, size_t h, + size_t num_components) +{ + std::vector dst; + size_t span_len; + std::uint8_t pixel; + auto size = w * h * num_components; + dst.reserve(size); + + const std::uint8_t* src = reinterpret_cast(ptr); + const std::uint8_t* src_end = src + size; + while (src < src_end) { + pwx_get_pixel_span(src, src_end, pixel, span_len); + src += span_len; + // fully transparent of fully opaque pixel + if (pixel == 0 || pixel == 0xF0) { + pixel = pixel | (span_len >> 8); + std::copy(&pixel, (&pixel) + 1, std::back_inserter(dst)); + pixel = span_len & 0xFF; + std::copy(&pixel, (&pixel) + 1, std::back_inserter(dst)); + } + // antialiased pixel + else { + pixel = pixel | span_len; + std::copy(&pixel, (&pixel) + 1, std::back_inserter(dst)); + } + } + + return EncodedRaster(std::move(dst), "pwx"); +} + EncodedRaster PNGRasterEncoder::operator()(const void *ptr, size_t w, size_t h, size_t num_components) { diff --git a/src/libslic3r/SLA/RasterBase.hpp b/src/libslic3r/SLA/RasterBase.hpp index 657fc865c2..9d22591cc7 100644 --- a/src/libslic3r/SLA/RasterBase.hpp +++ b/src/libslic3r/SLA/RasterBase.hpp @@ -97,6 +97,10 @@ public: virtual EncodedRaster encode(RasterEncoder encoder) const = 0; }; +struct PWXRasterEncoder { + EncodedRaster operator()(const void *ptr, size_t w, size_t h, size_t num_components); +}; + struct PNGRasterEncoder { EncodedRaster operator()(const void *ptr, size_t w, size_t h, size_t num_components); }; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 7b78dfea2f..23502c44e3 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -3,6 +3,7 @@ #include "Format/SL1.hpp" #include "Format/SL1_SVG.hpp" +#include "Format/pwmx.hpp" #include "ClipperUtils.hpp" #include "Geometry.hpp" @@ -16,6 +17,8 @@ #include #include +#include + // #define SLAPRINT_DO_BENCHMARK #ifdef SLAPRINT_DO_BENCHMARK @@ -249,6 +252,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con m_archiver = std::make_unique(m_printer_config); else if (m_printer_config.sla_archive_format.value == "SL2") m_archiver = std::make_unique(m_printer_config); + else if (m_printer_config.sla_archive_format.value == "pwmx") { + m_archiver = std::make_unique(m_printer_config); + } } struct ModelObjectStatus { @@ -1265,4 +1271,16 @@ void SLAPrint::StatusReporter::operator()(SLAPrint & p, p.set_status(int(std::round(st)), msg, flags); } + +void SLAPrint::write_thumbnail(Zipper& zipper, const ThumbnailData& data) +{ + size_t png_size = 0; + void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)data.pixels.data(), data.width, data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1); + if (png_data != nullptr) + { + zipper.add_entry("thumbnail/thumbnail" + std::to_string(data.width) + "x" + std::to_string(data.height) + ".png", (const std::uint8_t*)png_data, png_size); + mz_free(png_data); + } +} + } // namespace Slic3r diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index b2df0c4e92..8d5b54376a 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -9,6 +9,7 @@ #include "Point.hpp" #include "MTUtils.hpp" #include "Zipper.hpp" +#include "GCode/ThumbnailData.hpp" #include "libslic3r/Execution/ExecutionTBB.hpp" @@ -422,12 +423,16 @@ public: } // Export the print into an archive using the provided zipper. - // TODO: Use an archive writer interface instead of Zipper. - // This is quite limiting as the Zipper is a complete class, not an interface. - // The output can only be a zip archive. virtual void export_print(Zipper &zipper, const SLAPrint &print, - const std::string &projectname = "") = 0; + const std::string &projectname = "") {}; + // Export the print into an archive using the provided filename. + virtual void export_print(const std::string fname, + const SLAPrint &print, + ThumbnailsList &thumbnails, + const std::string &projectname = "") {}; + // By default the exporters use zipper export. Return false to use file export. + virtual bool uses_zipper_export() { return true; } }; /** @@ -533,16 +538,27 @@ public: // The aggregated and leveled print records from various objects. // TODO: use this structure for the preview in the future. const std::vector& print_layers() const { return m_printer_input; } - - void export_print(Zipper &zipper, const std::string &projectname = "") - { - m_archiver->export_print(zipper, *this, projectname); - } + + void write_thumbnail(Zipper& zipper, const ThumbnailData& data); void export_print(const std::string &fname, const std::string &projectname = "") { - Zipper zipper(fname); - export_print(zipper, projectname); + Slic3r::ThumbnailsList thumbnails; //empty thumbnail list + export_print(fname, thumbnails, projectname); + } + + void export_print(const std::string &fname, Slic3r::ThumbnailsList &thumbnails, const std::string &projectname = "") + { + if (m_archiver->uses_zipper_export()) { + Zipper zipper(fname); + m_archiver->export_print(zipper, *this, projectname); + for (const ThumbnailData& data : thumbnails) + if (data.is_valid()) + write_thumbnail(zipper, data); + zipper.finalize(); + } else { + m_archiver->export_print(fname, *this, thumbnails, projectname); + } } private: diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 5d3d47c202..37e527d649 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -165,17 +165,6 @@ void BackgroundSlicingProcess::process_fff() } } -static void write_thumbnail(Zipper& zipper, const ThumbnailData& data) -{ - size_t png_size = 0; - void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)data.pixels.data(), data.width, data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1); - if (png_data != nullptr) - { - zipper.add_entry("thumbnail/thumbnail" + std::to_string(data.width) + "x" + std::to_string(data.height) + ".png", (const std::uint8_t*)png_data, png_size); - mz_free(png_data); - } -} - void BackgroundSlicingProcess::process_sla() { assert(m_print == m_sla_print); @@ -189,12 +178,7 @@ void BackgroundSlicingProcess::process_sla() ThumbnailsList thumbnails = this->render_thumbnails( ThumbnailsParams{current_print()->full_print_config().option("thumbnails")->values, true, true, true, true}); - Zipper zipper(export_path); - m_sla_print->export_print(zipper); - for (const ThumbnailData& data : thumbnails) - if (data.is_valid()) - write_thumbnail(zipper, data); - zipper.finalize(); + m_sla_print->export_print(export_path, thumbnails); m_print->set_status(100, (boost::format(_utf8(L("Masked SLA file exported to %1%"))) % export_path).str()); } else if (! m_upload_job.empty()) { @@ -739,13 +723,7 @@ void BackgroundSlicingProcess::prepare_upload() ThumbnailsList thumbnails = this->render_thumbnails( ThumbnailsParams{current_print()->full_print_config().option("thumbnails")->values, true, true, true, true}); - // true, false, true, true); // renders also supports and pad - Zipper zipper{source_path.string()}; - m_sla_print->export_print(zipper, m_upload_job.upload_data.upload_path.string()); - for (const ThumbnailData& data : thumbnails) - if (data.is_valid()) - write_thumbnail(zipper, data); - zipper.finalize(); + m_sla_print->export_print(source_path.string(),thumbnails, m_upload_job.upload_data.upload_path.string()); } m_print->set_status(100, (boost::format(_utf8(L("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue"))) % m_upload_job.printhost->get_host()).str()); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b55ba0d75e..b914d8db47 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -496,7 +496,7 @@ static const FileWildcards file_wildcards_by_type[FT_SIZE] = { /* FT_TEX */ { "Texture"sv, { ".png"sv, ".svg"sv } }, - /* FT_SL1 */ { "Masked SLA files"sv, { ".sl1"sv, ".sl1s"sv } }, + /* FT_SL1 */ { "Masked SLA files"sv, { ".sl1"sv, ".sl1s"sv, ".pwmx"sv } }, }; // This function produces a Win32 file dialog file template mask to be consumed by wxWidgets on all platforms. From df0751716c2cc031cc8b3ffabd272ac55c1b0b6f Mon Sep 17 00:00:00 2001 From: ole00 Date: Sat, 12 Feb 2022 01:13:58 +0000 Subject: [PATCH 22/23] Added resources and settings for Anycubic Photon Mono X SLA printer --- resources/profiles/Anycubic.ini | 100 ++++++++++++++++++ .../Anycubic/PHOTON MONO X_thumbnail.png | Bin 0 -> 45953 bytes 2 files changed, 100 insertions(+) create mode 100644 resources/profiles/Anycubic/PHOTON MONO X_thumbnail.png diff --git a/resources/profiles/Anycubic.ini b/resources/profiles/Anycubic.ini index 053aecbd59..a67ba0962f 100644 --- a/resources/profiles/Anycubic.ini +++ b/resources/profiles/Anycubic.ini @@ -64,6 +64,13 @@ technology = FFF family = PREDATOR default_materials = Generic PLA @PREDATOR; Generic PETG @PREDATOR; Generic ABS @PREDATOR +[printer_model:PHOTON MONO X] +name = Photon Mono X +variants = default +technology = SLA +family = PHOTON MONO +default_materials = Generic Blue Resin MONO @0.05 + # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. @@ -1898,3 +1905,96 @@ default_print_profile = 0.24mm 0.8 nozzle DETAILED QUALITY @PREDATOR ######################################### ########## end printer presets ########## ######################################### + +######################################### +########## SLA printer presets ########## +######################################### + + +[sla_print:*common print ANYCUBIC SLA*] +compatible_printers_condition = family=="PHOTON MONO" +layer_height = 0.05 +output_filename_format = [input_filename_base].pwmx +pad_edge_radius = 0.5 +pad_enable = 0 +pad_max_merge_distance = 50 +pad_wall_height = 0 +pad_wall_thickness = 1 +pad_wall_slope = 45 +faded_layers = 8 +slice_closing_radius = 0.005 +support_base_diameter = 3 +support_base_height = 1 +support_critical_angle = 45 +support_density_at_45 = 250 +support_density_at_horizontal = 500 +support_head_front_diameter = 0.4 +support_head_penetration = 0.4 +support_head_width = 3 +support_max_bridge_length = 10 +support_minimal_z = 0 +support_object_elevation = 5 +support_pillar_diameter = 1 +support_pillar_connection_mode = zigzag +support_pillar_widening_factor = 0 +supports_enable = 1 +support_small_pillar_diameter_percent = 60% + +[sla_print:0.05 ANYCUBIC SLA] +inherits = *common print ANYCUBIC SLA* +layer_height = 0.05 + + +########### Materials + +[sla_material:*common ANYCUBIC SLA*] +compatible_printers_condition = family=="PHOTON MONO" +compatible_prints_condition = layer_height == 0.05 +exposure_time = 7 +initial_exposure_time = 40 +initial_layer_height = 0.05 +material_correction = 1,1,1 +material_notes = LIFT_DISTANCE=8.0\nLIFT_SPEED=2.5\nRETRACT_SPEED=3.0\nBOTTOM_LIFT_SPEED=2.0\nBOTTOM_LIFT_DISTANCE=9.0\nDELAY_BEFORE_EXPOSURE=0.5 + +[sla_material:*common 0.05 ANYCUBIC SLA*] +inherits = *common ANYCUBIC SLA* + +[sla_material:Generic Blue Resin MONO @0.05] +inherits = *common 0.05 ANYCUBIC SLA* +exposure_time = 2.5 +initial_exposure_time = 40 +material_type = Tough +material_vendor = Generic +material_colour = #6080EC + +########## Printers + +[printer:Anycubic Photon Mono X] +printer_technology = SLA +printer_model = PHOTON MONO X +printer_variant = default +default_sla_material_profile = Generic Blue Resin MONO @0.05 +default_sla_print_profile = 0.05 ANYCUBIC SLA +thumbnails = 224x168 +sla_archive_format = pwmx +bed_shape = 1.48x1.02,193.48x1.02,193.48x121.02,1.48x121.02 +display_height = 120 +display_orientation = landscape +display_mirror_x = 1 +display_mirror_y = 0 +display_pixels_x = 3840 +display_pixels_y = 2400 +display_width = 192 +max_print_height = 245 +elefant_foot_compensation = 0.2 +elefant_foot_min_width = 0.2 +min_exposure_time = 1 +max_exposure_time = 120 +min_initial_exposure_time = 1 +max_initial_exposure_time = 300 +printer_correction = 1,1,1 +gamma_correction = 1 +area_fill = 45 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.'\nPRINTER_VENDOR_ANYCUBIC\nPRINTER_MODEL_PHOTONMONOX\n + + diff --git a/resources/profiles/Anycubic/PHOTON MONO X_thumbnail.png b/resources/profiles/Anycubic/PHOTON MONO X_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..70ad47b63defdbd22c913d211d284a1bed729860 GIT binary patch literal 45953 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

?9hL)5+9SL}$(eIdYR*>(Nn1RXY00 z_y?`E?Af!*=GK;9okh|fAvy@noZ}L$pR6G6jMJbHt59MnO^?Eq7+qpai7pC^DKJI+ zDZ@{Xvg}iq;rYf;7KVOV(u+TZp^T3g1-%Afzw|IWdyZ(#3%gZ#&z|K~h>&lh;wH+(bC{MPT{ zS(>Rx;U=5>KfkshS%|mm;E!ehM#=#5AvE{{x>WwFY}3y{V6~G(wq6#Z~IRE z(R03w5B`t$bIZ@af=l-A;|rhrEdS)2zJa>(RvMrD>rb<=xWxP4`)+>bm9ONVJokHe z_IEs&|MBj3@Y-MeIhIyd(zb8K@pJ$B7XJO4-pG4@|6TmiAAW$Jc*QFj42Rjuq~^Cx z=XO(7#}BpRRT(vU99{p$zzS&L&(>cyWEw@>6(2tK@cCPz^Of3(OSzPYAfXsjS(`U* z*HM2X=AP~2A;-)gSN?ek0SS6@Y3oezf%5gFQk4A?0S+8IM1Of7Tk9tQp)3bHc;6j- z@ZG=3bXw8Wj%qUE!|!=J|LP65a_QAK@cl3QC9eP9{vm(#&i~B!{L>%h;rs97@dqE^ z|9JlQ@h9)Too9X9^ZB+Hyp$`h`#b#P>u=>l?|wU58z*`G5B~(c;gDC|{5_mFeuT2W z&=n_h!gM<2H-Gm%-2bJ!x%!Grx%;lWSkkb#xX7)){;T}6AN>(-`jMA%{S9Bot^e)a zyyJI%hf^mvc;#zf&(m(ak!StAujAo|9-ve(7!G*rfBX;p)KC8$FTCj{p7!Z^zlT-EW(Qr#|iJ{FmQ)8~^mCAL1=-~KfV!vWv_W50lH8mzUPI&qu_?z)5F z;ws`iS6aK8UVj+!N}<2Biq@LZ6sDtf8Z46uEG;d2PIOa&cp-ADG=sqcx4!w+yy)hi z=i`6+N4)XX|2NWK@;)lzD*;sfgU~MOrvJPytBA zb9XFEP$GpA=oRG<;Jz>2E9M*u!za2z$H-gPTK|X zryuPl1adJ*t&M-o7?d(-rO{|_Z6-C({MP64vRD5ad-h+3wGAlY#;1Q1|ME4z#^TCe z#5o3w%RJ}#H}Te6Ue4OlNBGb?-^S-Y`4Rr{cfSx@JF0q0caqm5M@ru{Jg%|$FPcR*iXP`YWrr^Yh6HKNRH~r|3 zBO;7}Q)_Gd%rD%+AO67~@VeK$iof~GkMjdB_&$E|mRmr>#`;O_yz7g+@jv}1-uQ+$ zP*-D4ojl2_|DV_Mw%cyy^}l=zfB6?5;YBySh+lm5EuA!QVPTQqzV+AnzVCf5Z+`1- zjMq=_(?9hueaexYV69sGU8~mHTg3-7Db-7c@G7OYfLH9>x9?fkTyxE!wC)tx`IXa$!$hH{OsT z;tMTwmUj`9ptJ;W!aS){?G~Ajf{_q56tJ+k!hu8AQcb6fH`i(E8dDTpas7=nP0i-1 zlkDApkb~EK9pkMHzV{_R$JajnncVrAPw<7m`8Y58rC;HSuX!4y%~M?a)TeXZ)1JX- zYlD3U4)W~hy@=6xlP}+QFW+<1kMq1A`Y}dZ8$9EizmqGj`Wn_w9Os8#`qTWJ;KStqR5z7~}J5 zi%{7HE0P)jKIjxmX>FY?aFRI9nFeigDu|7q*61o{2&xb(s+8QUNGl3sP)cJxaU$t8 zt7w-&+w%9%K%*$dxuEf^0=+fZImaR_Skj3SuaTND+WbhbXi%kJY(=!z!4BHBtEX0M zw|`FZj{bd|Q%dUslooN4Fm`$Hn8~aq+l#Ts$rw7mtg_#pB{}@wj+gJpO-k{C}%O`IChc3wZzl002ovPDHLk FV1nTQC-eXS literal 0 HcmV?d00001 From 69019ae4bb16473a33ad681c2a8cb57887e5c830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 14 Feb 2022 08:20:57 +0100 Subject: [PATCH 23/23] Added a missing include (GCC11.1 without PCH). --- src/slic3r/GUI/MeshUtils.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 11ebf93864..cc961ee8f1 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -3,6 +3,7 @@ #include "libslic3r/Point.hpp" #include "libslic3r/Geometry.hpp" +#include "libslic3r/TriangleMesh.hpp" #include "libslic3r/SLA/IndexedMesh.hpp" #include "admesh/stl.h"