diff --git a/CMakeLists.txt b/CMakeLists.txt index 72fd87d224..6c6afe6710 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -506,6 +506,12 @@ endif () # Find the Cereal serialization library find_package(cereal REQUIRED) +add_library(libcereal INTERFACE) +if (NOT TARGET cereal::cereal) + target_link_libraries(libcereal INTERFACE cereal) +else() + target_link_libraries(libcereal INTERFACE cereal::cereal) +endif() # l10n set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization") 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 diff --git a/resources/profiles/Anycubic.ini b/resources/profiles/Anycubic.ini index 053aecbd59..821d87462c 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,94 @@ 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 Normal @ANYCUBIC] +inherits = *common print ANYCUBIC SLA* +layer_height = 0.05 + +########### Materials + +[sla_material:*common ANYCUBIC SLA*] +compatible_printers_condition = printer_notes=~/.*PHOTONMONOX.*/ +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 +compatible_printers_condition = printer_notes=~/.*PHOTONMONOX.*/ + +########## 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 Normal @ANYCUBIC +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 0000000000..70ad47b63d Binary files /dev/null and b/resources/profiles/Anycubic/PHOTON MONO X_thumbnail.png differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ab1c7b9645..61a2a90d8b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -126,7 +126,7 @@ if (NOT WIN32 AND NOT APPLE) set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") endif () -target_link_libraries(PrusaSlicer libslic3r cereal) +target_link_libraries(PrusaSlicer libslic3r libcereal) if (APPLE) # add_compile_options(-stdlib=libc++) # add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE) diff --git a/src/libslic3r/BuildVolume.hpp b/src/libslic3r/BuildVolume.hpp index 6b928d48b0..be8d224c39 100644 --- a/src/libslic3r/BuildVolume.hpp +++ b/src/libslic3r/BuildVolume.hpp @@ -94,6 +94,12 @@ public: // Called on initial G-code preview on OpenGL vertex buffer interleaved normals and vertices. bool all_paths_inside_vertices_and_normals_interleaved(const std::vector& paths, const Eigen::AlignedBox& bbox, bool ignore_bottom = true) const; + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + const std::pair, std::vector>& top_bottom_convex_hull_decomposition_scene() const { return m_top_bottom_convex_hull_decomposition_scene; } + const std::pair, std::vector>& top_bottom_convex_hull_decomposition_bed() const { return m_top_bottom_convex_hull_decomposition_bed; } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + private: // Source definition of the print bed geometry (PrintConfig::bed_shape) std::vector m_bed_shape; diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index d4c8d7edc1..5e8d681f1a 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -94,10 +94,14 @@ set(SLIC3R_SOURCES Format/objparser.hpp Format/STL.cpp Format/STL.hpp + Format/SLAArchive.hpp + Format/SLAArchive.cpp Format/SL1.hpp 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 @@ -354,7 +358,7 @@ find_package(JPEG REQUIRED) target_link_libraries(libslic3r libnest2d admesh - cereal + libcereal libigl miniz boost_libs diff --git a/src/libslic3r/Execution/Execution.hpp b/src/libslic3r/Execution/Execution.hpp index 62e49cfeb3..dcfd86bde8 100644 --- a/src/libslic3r/Execution/Execution.hpp +++ b/src/libslic3r/Execution/Execution.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "libslic3r/libslic3r.h" @@ -44,7 +45,8 @@ size_t max_concurrency(const EP &ep) template> void for_each(const EP &ep, It from, It to, Fn &&fn, size_t granularity = 1) { - AsTraits::for_each(ep, from, to, std::forward(fn), granularity); + AsTraits::for_each(ep, from, to, std::forward(fn), + std::max(granularity, size_t(1))); } // A reduce operation with the execution policy passed as argument. @@ -68,7 +70,7 @@ T reduce(const EP & ep, return AsTraits::reduce(ep, from, to, init, std::forward(mergefn), std::forward(accessfn), - granularity); + std::max(granularity, size_t(1))); } // An overload of reduce method to be used with iterators as 'from' and 'to' @@ -87,7 +89,7 @@ T reduce(const EP &ep, { return reduce( ep, from, to, init, std::forward(mergefn), - [](const auto &i) { return i; }, granularity); + [](const auto &i) { return i; }, std::max(granularity, size_t(1))); } template{}, - std::forward(accessfn), granularity); + std::forward(accessfn), + std::max(granularity, size_t(1))); } @@ -119,7 +122,7 @@ T accumulate(const EP &ep, { return reduce( ep, from, to, init, std::plus{}, [](const auto &i) { return i; }, - granularity); + std::max(granularity, size_t(1))); } } // namespace execution_policy diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp index 6f1e95528a..df10ce0465 100644 --- a/src/libslic3r/Format/SL1.cpp +++ b/src/libslic3r/Format/SL1.cpp @@ -20,11 +20,14 @@ #include "libslic3r/miniz_extension.hpp" #include "libslic3r/PNGReadWrite.hpp" #include "libslic3r/LocalesUtils.hpp" +#include "libslic3r/GCode/ThumbnailData.hpp" #include #include #include +#include + namespace marchsq { template<> struct _RasterTraits { @@ -482,10 +485,31 @@ sla::RasterEncoder SL1Archive::get_encoder() const return sla::PNGRasterEncoder{}; } -void SL1Archive::export_print(Zipper& zipper, - const SLAPrint &print, - const std::string &prjname) +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", + static_cast(png_data), + png_size); + + mz_free(png_data); + } +} + +void SL1Archive::export_print(const std::string fname, + const SLAPrint &print, + const ThumbnailsList &thumbnails, + const std::string &prjname) +{ + Zipper zipper{fname}; + std::string project = prjname.empty() ? boost::filesystem::path(zipper.get_filename()).stem().string() : @@ -512,6 +536,12 @@ void SL1Archive::export_print(Zipper& zipper, zipper.add_entry(imgname.c_str(), rst.data(), rst.size()); } + + for (const ThumbnailData& data : thumbnails) + if (data.is_valid()) + write_thumbnail(zipper, data); + + zipper.finalize(); } catch(std::exception& e) { BOOST_LOG_TRIVIAL(error) << e.what(); // Rethrow the exception diff --git a/src/libslic3r/Format/SL1.hpp b/src/libslic3r/Format/SL1.hpp index e6c0ff0896..493550db4b 100644 --- a/src/libslic3r/Format/SL1.hpp +++ b/src/libslic3r/Format/SL1.hpp @@ -3,8 +3,12 @@ #include +#include "SLAArchive.hpp" + #include "libslic3r/Zipper.hpp" -#include "libslic3r/SLAPrint.hpp" +#include "libslic3r/PrintConfig.hpp" + +struct indexed_triangle_set; namespace Slic3r { @@ -23,8 +27,11 @@ public: SL1Archive() = default; explicit SL1Archive(const SLAPrinterConfig &cfg): m_cfg(cfg) {} explicit SL1Archive(SLAPrinterConfig &&cfg): m_cfg(std::move(cfg)) {} - - void export_print(Zipper &zipper, const SLAPrint &print, const std::string &projectname = "") override; + + void export_print(const std::string fname, + const SLAPrint &print, + const ThumbnailsList &thumbnails, + const std::string &projectname = "") override; }; ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out); diff --git a/src/libslic3r/Format/SL1_SVG.cpp b/src/libslic3r/Format/SL1_SVG.cpp index 0ea2306119..372348283a 100644 --- a/src/libslic3r/Format/SL1_SVG.cpp +++ b/src/libslic3r/Format/SL1_SVG.cpp @@ -2,10 +2,13 @@ #include "SLA/RasterBase.hpp" #include "libslic3r/LocalesUtils.hpp" #include "libslic3r/ClipperUtils.hpp" +#include "libslic3r/BoundingBox.hpp" #include #include #include +#include +using namespace std::literals; namespace Slic3r { @@ -77,20 +80,23 @@ void append_svg(std::string &buf, const Polygon &poly) char intbuf[coord_t_bufsize]; - buf += std::string(" data; - constexpr const char finish[] = "\n"; + constexpr auto finish = "\n"sv; data.reserve(m_svg.size() + std::size(finish)); std::copy(m_svg.begin(), m_svg.end(), std::back_inserter(data)); - std::copy(finish, finish + std::size(finish) - 1, std::back_inserter(data)); + std::copy(finish.begin(), finish.end() - 1, std::back_inserter(data)); return sla::EncodedRaster{std::move(data), "svg"}; } diff --git a/src/libslic3r/Format/SLAArchive.cpp b/src/libslic3r/Format/SLAArchive.cpp new file mode 100644 index 0000000000..8e2bf2824f --- /dev/null +++ b/src/libslic3r/Format/SLAArchive.cpp @@ -0,0 +1,74 @@ +#include "SLAArchive.hpp" + +#include "SL1.hpp" +#include "SL1_SVG.hpp" +#include "pwmx.hpp" + +#include "libslic3r/libslic3r.h" + +#include +#include +#include +#include + +namespace Slic3r { + +using ArchiveFactory = std::function(const SLAPrinterConfig&)>; + +struct ArchiveEntry { + const char *ext; + ArchiveFactory factoryfn; +}; + +static const std::map REGISTERED_ARCHIVES { + { + "SL1", + { "sl1", [] (const auto &cfg) { return std::make_unique(cfg); } } + }, + { + "SL2", + { "sl2", [] (const auto &cfg) { return std::make_unique(cfg); } } + }, + { + "pwmx", + { "pwmx", [] (const auto &cfg) { return std::make_unique(cfg); } } + } +}; + +std::unique_ptr +SLAArchive::create(const std::string &archtype, const SLAPrinterConfig &cfg) +{ + auto entry = REGISTERED_ARCHIVES.find(archtype); + + if (entry != REGISTERED_ARCHIVES.end()) + return entry->second.factoryfn(cfg); + + return nullptr; +} + +const std::vector& SLAArchive::registered_archives() +{ + static std::vector archnames; + + if (archnames.empty()) { + archnames.reserve(REGISTERED_ARCHIVES.size()); + + for (auto &[name, _] : REGISTERED_ARCHIVES) + archnames.emplace_back(name.c_str()); + } + + return archnames; +} + +const char *SLAArchive::get_extension(const char *archtype) +{ + static const char* DEFAULT_EXT = "zip"; + + auto entry = REGISTERED_ARCHIVES.find(archtype); + if (entry != REGISTERED_ARCHIVES.end()) + return entry->second.ext; + + return DEFAULT_EXT; +} + +} // namespace Slic3r diff --git a/src/libslic3r/Format/SLAArchive.hpp b/src/libslic3r/Format/SLAArchive.hpp new file mode 100644 index 0000000000..971d7ba7b8 --- /dev/null +++ b/src/libslic3r/Format/SLAArchive.hpp @@ -0,0 +1,64 @@ +#ifndef SLAARCHIVE_HPP +#define SLAARCHIVE_HPP + +#include + +#include "libslic3r/SLA/RasterBase.hpp" +#include "libslic3r/Execution/ExecutionTBB.hpp" +#include "libslic3r/GCode/ThumbnailData.hpp" + +namespace Slic3r { + +class SLAPrint; +class SLAPrinterConfig; + +class SLAArchive { +protected: + std::vector m_layers; + + virtual std::unique_ptr create_raster() const = 0; + virtual sla::RasterEncoder get_encoder() const = 0; + +public: + virtual ~SLAArchive() = default; + + // Fn have to be thread safe: void(sla::RasterBase& raster, size_t lyrid); + template + void draw_layers( + size_t layer_num, + Fn && drawfn, + CancelFn cancelfn = []() { return false; }, + const EP & ep = {}) + { + m_layers.resize(layer_num); + execution::for_each( + ep, size_t(0), m_layers.size(), + [this, &drawfn, &cancelfn](size_t idx) { + if (cancelfn()) return; + + sla::EncodedRaster &enc = m_layers[idx]; + auto rst = create_raster(); + drawfn(*rst, idx); + enc = rst->encode(get_encoder()); + }, + execution::max_concurrency(ep)); + } + + // Export the print into an archive using the provided filename. + virtual void export_print(const std::string fname, + const SLAPrint &print, + const ThumbnailsList &thumbnails, + const std::string &projectname = "") = 0; + + // Factory method to create an archiver instance + static std::unique_ptr create(const std::string &archtype, const SLAPrinterConfig&); + + // Get the names of currently known archiver implementations + static const std::vector & registered_archives(); + + // Get the default file extension belonging to an archive format + static const char *get_extension(const char *archtype); +}; + +} // namespace Slic3r +#endif // SLAARCHIVE_HPP diff --git a/src/libslic3r/Format/pwmx.cpp b/src/libslic3r/Format/pwmx.cpp new file mode 100644 index 0000000000..f3f0f6802a --- /dev/null +++ b/src/libslic3r/Format/pwmx.cpp @@ -0,0 +1,564 @@ +#include "pwmx.hpp" +#include "GCode/ThumbnailData.hpp" +#include "SLA/RasterBase.hpp" +#include "libslic3r/SLAPrint.hpp" + +#include +#include +#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 { + +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 < end && span_len < max_len && ((*ptr) & 0xF0) == pixel) { + span_len++; + ptr++; + } +} + +struct PWXRasterEncoder +{ + sla::EncodedRaster 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 sla::EncodedRaster(std::move(dst), "pwx"); + } +}; + +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 { + +std::float_t get_cfg_value_f(const DynamicConfig &cfg, + const std::string &key, + const std::float_t &def = 0.f) +{ + if (cfg.has(key)) { + if (auto opt = cfg.option(key)) + return opt->getFloat(); + } + + return def; +} + +int get_cfg_value_i(const DynamicConfig &cfg, + const std::string &key, + const int &def = 0) +{ + if (cfg.has(key)) { + if (auto opt = cfg.option(key)) + return opt->getInt(); + } + + return def; +} + +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*/, + const 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) +{ + CNumericLocalesSetter locales_setter; + + 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(); + auto mat_opt = cfg.option("material_notes"); + std::string mnotes = mat_opt? 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 = get_cfg_value_f(cfg, "layer_height"); + m.bottom_layer_height_mm = get_cfg_value_f(cfg, "initial_layer_height"); + h.exposure_time_s = get_cfg_value_f(cfg, "exposure_time"); + h.bottom_exposure_time_s = get_cfg_value_f(cfg, "initial_exposure_time"); + h.bottom_layer_count = get_cfg_value_i(cfg, "faded_layers"); + if (layer_count < h.bottom_layer_count) { + h.bottom_layer_count = layer_count; + } + h.res_x = get_cfg_value_i(cfg, "display_pixels_x"); + h.res_y = get_cfg_value_i(cfg, "display_pixels_y"); + bottle_weight_g = get_cfg_value_f(cfg, "bottle_weight") * 1000.0f; + bottle_volume_ml = get_cfg_value_f(cfg, "bottle_volume"); + bottle_cost = get_cfg_value_f(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 = get_cfg_value_f(mat_cfg, CFG_DELAY_BEFORE_EXPOSURE, 0.5f); + crop_value(h.delay_before_exposure_s, 0.0f, 1000.0f); + + h.lift_distance_mm = get_cfg_value_f(mat_cfg, CFG_LIFT_DISTANCE, 8.0f); + crop_value(h.lift_distance_mm, 0.0f, 100.0f); + + if (mat_cfg.has(CFG_BOTTOM_LIFT_DISTANCE)) { + m.bottom_lift_distance_mm = get_cfg_value_f(mat_cfg, + CFG_BOTTOM_LIFT_DISTANCE, + 8.0f); + crop_value(h.lift_distance_mm, 0.0f, 100.0f); + } else { + m.bottom_lift_distance_mm = h.lift_distance_mm; + } + + h.lift_speed_mms = get_cfg_value_f(mat_cfg, CFG_LIFT_SPEED, 2.0f); + crop_value(m.bottom_lift_speed_mms, 0.1f, 20.0f); + + if (mat_cfg.has(CFG_BOTTOM_LIFT_SPEED)) { + m.bottom_lift_speed_mms = get_cfg_value_f(mat_cfg, CFG_BOTTOM_LIFT_SPEED, 2.0f); + 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 = get_cfg_value_f(mat_cfg, CFG_RETRACT_SPEED, 3.0f); + 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 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, + const ThumbnailsList &thumbnails, + const std::string &/*projectname*/) +{ + std::uint32_t layer_count = m_layers.size(); + + pwmx_format_intro intro = {}; + pwmx_format_header header = {}; + pwmx_format_preview preview = {}; + pwmx_format_layers_header layers_header = {}; + pwmx_format_misc misc = {}; + 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..65fb199102 --- /dev/null +++ b/src/libslic3r/Format/pwmx.hpp @@ -0,0 +1,37 @@ +#ifndef _SLIC3R_FORMAT_PWMX_HPP_ +#define _SLIC3R_FORMAT_PWMX_HPP_ + +#include + +#include "SLAArchive.hpp" + +#include "libslic3r/PrintConfig.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, + const ThumbnailsList &thumbnails, + const std::string &projectname = "") override; +}; + + +} // namespace Slic3r::sla + +#endif // _SLIC3R_FORMAT_PWMX_HPP_ diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index fb6dee37ba..9ce55d0bb3 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -274,7 +274,6 @@ namespace Slic3r { // Otherwise, leave control to the user completely. std::string toolchange_gcode_str; const std::string& toolchange_gcode = gcodegen.config().toolchange_gcode.value; -// m_max_layer_z = std::max(m_max_layer_z, tcr.print_z); if (! toolchange_gcode.empty()) { DynamicConfig config; int previous_extruder_id = gcodegen.writer().extruder() ? (int)gcodegen.writer().extruder()->id() : -1; @@ -283,7 +282,7 @@ namespace Slic3r { config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z)); config.set_key_value("toolchange_z", new ConfigOptionFloat(z)); -// config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); + config.set_key_value("max_layer_z", new ConfigOptionFloat(gcodegen.m_max_layer_z)); toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config); check_add_eol(toolchange_gcode_str); } @@ -305,6 +304,9 @@ namespace Slic3r { if (!start_filament_gcode.empty()) { // Process the start_filament_gcode for the active filament only. DynamicConfig config; + config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(gcodegen.writer().get_position()(2) - gcodegen.m_config.z_offset.value)); + config.set_key_value("max_layer_z", new ConfigOptionFloat(gcodegen.m_max_layer_z)); config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id)); start_filament_gcode_str = gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config); check_add_eol(start_filament_gcode_str); @@ -1274,15 +1276,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Write the custom start G-code file.writeln(start_gcode); - // Process filament-specific gcode. - /* if (has_wipe_tower) { - // Wipe tower will control the extruder switching, it will call the start_filament_gcode. - } else { - DynamicConfig config; - config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id))); - file.writeln(this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config)); - } -*/ this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true); print.throw_if_canceled(); @@ -1899,6 +1892,8 @@ namespace ProcessLayer // && !MMU1 ) { //! FIXME_in_fw show message during print pause + // FIXME: Why is pause_print_gcode here? Why is it supplied "color_change_extruder"? Why is that not + // passed to color_change_gcode below? DynamicConfig cfg; cfg.set_key_value("color_change_extruder", new ConfigOptionInt(m600_extruder_before_layer)); gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id, &cfg); @@ -2109,10 +2104,10 @@ GCode::LayerResult GCode::process_layer( DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); gcode += this->placeholder_parser_process("layer_gcode", print.config().layer_gcode.value, m_writer.extruder()->id(), &config) + "\n"; - config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); } if (! first_layer && ! m_second_layer_things_done) { @@ -3146,7 +3141,9 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id); if (! start_filament_gcode.empty()) { // Process the start_filament_gcode for the filament. - gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id); + DynamicConfig config; + config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id))); + gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config); check_add_eol(gcode); } gcode += m_writer.toolchange(extruder_id); @@ -3215,7 +3212,9 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id); if (! start_filament_gcode.empty()) { // Process the start_filament_gcode for the new filament. - gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id); + DynamicConfig config; + config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id))); + gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config); check_add_eol(gcode); } // Set the new extruder to the operating temperature. 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); diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 6134e1f5ab..aa69fdc777 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -982,7 +982,7 @@ bool SupportTreeBuildsteps::connect_to_model_body(Head &head) double w = dist - 2 * head.r_pin_mm - head.r_back_mm; if (w < 0.) { - BOOST_LOG_TRIVIAL(error) << "Pinhead width is negative!"; + BOOST_LOG_TRIVIAL(warning) << "Pinhead width is negative!"; w = 0.; } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 7b78dfea2f..72cb96dd04 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" @@ -244,12 +245,8 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con // Handle changes to object config defaults m_default_object_config.apply_only(config, object_diff, true); - if (!m_archiver || !printer_diff.empty()) { - if (m_printer_config.sla_archive_format.value == "SL1") - 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); - } + if (!m_archiver || !printer_diff.empty()) + m_archiver = SLAArchive::create(m_printer_config.sla_archive_format.value.c_str(), m_printer_config); struct ModelObjectStatus { enum Status { diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index b2df0c4e92..0723382b39 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -9,6 +9,8 @@ #include "Point.hpp" #include "MTUtils.hpp" #include "Zipper.hpp" +#include "Format/SLAArchive.hpp" +#include "GCode/ThumbnailData.hpp" #include "libslic3r/Execution/ExecutionTBB.hpp" @@ -389,47 +391,6 @@ struct SLAPrintStatistics } }; -class SLAArchive { -protected: - std::vector m_layers; - - virtual std::unique_ptr create_raster() const = 0; - virtual sla::RasterEncoder get_encoder() const = 0; - -public: - virtual ~SLAArchive() = default; - - // Fn have to be thread safe: void(sla::RasterBase& raster, size_t lyrid); - template - void draw_layers( - size_t layer_num, - Fn && drawfn, - CancelFn cancelfn = []() { return false; }, - const EP & ep = {}) - { - m_layers.resize(layer_num); - execution::for_each( - ep, size_t(0), m_layers.size(), - [this, &drawfn, &cancelfn](size_t idx) { - if (cancelfn()) return; - - sla::EncodedRaster &enc = m_layers[idx]; - auto rst = create_raster(); - drawfn(*rst, idx); - enc = rst->encode(get_encoder()); - }, - execution::max_concurrency(ep)); - } - - // 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; -}; - /** * @brief This class is the high level FSM for the SLA printing process. * @@ -534,15 +495,17 @@ public: // 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 export_print(const std::string &fname, const std::string &projectname = "") { - Zipper zipper(fname); - export_print(zipper, projectname); + ThumbnailsList thumbnails; //empty thumbnail list + export_print(fname, thumbnails, projectname); + } + + void export_print(const std::string &fname, + const ThumbnailsList &thumbnails, + const std::string &projectname = "") + { + m_archiver->export_print(fname, *this, thumbnails, projectname); } private: 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/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 34c0efd014..022bba2a8a 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -270,7 +270,7 @@ endforeach() encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL hidapi libcurl ${wxWidgets_LIBRARIES}) +target_link_libraries(libslic3r_gui libslic3r avrdude libcereal imgui GLEW::GLEW OpenGL::GL hidapi libcurl ${wxWidgets_LIBRARIES}) if (MSVC) target_link_libraries(libslic3r_gui Setupapi.lib) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 8488c1148f..244a802dbc 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); @@ -335,10 +334,10 @@ 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 (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..da09a236f4 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1,10 +1,12 @@ #include +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #if ENABLE_SMOOTH_NORMALS #include #include #include #endif // ENABLE_SMOOTH_NORMALS +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #include "3DScene.hpp" #include "GLShader.hpp" @@ -69,6 +71,7 @@ void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char namespace Slic3r { +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #if ENABLE_SMOOTH_NORMALS static void smooth_normals_corner(TriangleMesh& mesh, std::vector& normals) { @@ -287,6 +290,7 @@ void GLIndexedVertexArray::render( glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL const float GLVolume::SinkingContours::HalfWidth = 0.25f; @@ -400,8 +404,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 +414,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); @@ -484,7 +487,9 @@ GLVolume::GLVolume(float r, float g, float b, float a) , force_neutral_color(false) , force_sinking_contours(false) , tverts_range(0, size_t(-1)) +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL , qverts_range(0, size_t(-1)) +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL { color = { r, g, b, a }; set_render_color(color); @@ -600,6 +605,36 @@ const BoundingBoxf3& GLVolume::transformed_non_sinking_bounding_box() const return *m_transformed_non_sinking_bounding_box; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void GLVolume::set_range(double min_z, double max_z) +{ + this->tverts_range.first = 0; + this->tverts_range.second = this->model.indices_count(); + + if (!this->print_zs.empty()) { + // The Z layer range is specified. + // First test whether the Z span of this object is not out of (min_z, max_z) completely. + if (this->print_zs.front() > max_z || this->print_zs.back() < min_z) + this->tverts_range.second = 0; + else { + // Then find the lowest layer to be displayed. + size_t i = 0; + for (; i < this->print_zs.size() && this->print_zs[i] < min_z; ++i); + if (i == this->print_zs.size()) + // This shall not happen. + this->tverts_range.second = 0; + else { + // Remember start of the layer. + this->tverts_range.first = this->offsets[i]; + // Some layers are above $min_z. Which? + for (; i < this->print_zs.size() && this->print_zs[i] <= max_z; ++i); + if (i < this->print_zs.size()) + this->tverts_range.second = this->offsets[i]; + } + } + } +} +#else void GLVolume::set_range(double min_z, double max_z) { this->qverts_range.first = 0; @@ -612,7 +647,8 @@ void GLVolume::set_range(double min_z, double max_z) if (this->print_zs.front() > max_z || this->print_zs.back() < min_z) { this->qverts_range.second = 0; this->tverts_range.second = 0; - } else { + } + else { // Then find the lowest layer to be displayed. size_t i = 0; for (; i < this->print_zs.size() && this->print_zs[i] < min_z; ++ i); @@ -620,7 +656,8 @@ void GLVolume::set_range(double min_z, double max_z) // This shall not happen. this->qverts_range.second = 0; this->tverts_range.second = 0; - } else { + } + else { // Remember start of the layer. this->qverts_range.first = this->offsets[i * 2]; this->tverts_range.first = this->offsets[i * 2 + 1]; @@ -634,8 +671,9 @@ void GLVolume::set_range(double min_z, double max_z) } } } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL -void GLVolume::render() const +void GLVolume::render() { if (!is_active) return; @@ -646,7 +684,14 @@ void GLVolume::render() const glsafe(::glPushMatrix()); glsafe(::glMultMatrixd(world_matrix().data())); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (tverts_range == std::make_pair(0, -1)) + model.render(); + else + model.render(this->tverts_range); +#else this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL glsafe(::glPopMatrix()); if (this->is_left_handed()) @@ -681,36 +726,59 @@ void GLVolume::render_non_manifold_edges() } #endif // ENABLE_SHOW_NON_MANIFOLD_EDGES +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +std::vector GLVolumeCollection::load_object( + const ModelObject* model_object, + int obj_idx, + const std::vector& instance_idxs) +#else 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) +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL { 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)); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx)); +#else + volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, opengl_initialized)); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL return volumes_idx; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +int GLVolumeCollection::load_object_volume( + const ModelObject* model_object, + int obj_idx, + int volume_idx, + int instance_idx) +#else int GLVolumeCollection::load_object_volume( const ModelObject *model_object, int obj_idx, int volume_idx, int instance_idx, - const std::string &color_by, bool opengl_initialized) +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL { 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(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_SMOOTH_NORMALS + v.model.init_from(mesh, true); +#else + v.model.init_from(mesh); +#endif // ENABLE_SMOOTH_NORMALS + v.model.set_color(color_from_model_volume(*model_volume)); +#else v.set_color(color_from_model_volume(*model_volume)); #if ENABLE_SMOOTH_NORMALS v.indexed_vertex_array.load_mesh(mesh, true); @@ -718,9 +786,9 @@ int GLVolumeCollection::load_object_volume( v.indexed_vertex_array.load_mesh(mesh); #endif // ENABLE_SMOOTH_NORMALS v.indexed_vertex_array.finalize_geometry(opengl_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx); - if (model_volume->is_model_part()) - { + if (model_volume->is_model_part()) { // GLVolume will reference a convex hull from model_volume! v.set_convex_hull(model_volume->get_convex_hull_shared_ptr()); if (extruder_id != -1) @@ -737,6 +805,16 @@ int GLVolumeCollection::load_object_volume( // Load SLA auxiliary GLVolumes (for support trees or pad). // This function produces volumes for multiple instances in a single shot, // as some object specific mesh conversions may be expensive. +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void GLVolumeCollection::load_object_auxiliary( + const SLAPrintObject* print_object, + int obj_idx, + // pairs of + const std::vector>& instances, + SLAPrintObjectStep milestone, + // Timestamp of the last change of the milestone + size_t timestamp) +#else void GLVolumeCollection::load_object_auxiliary( const SLAPrintObject *print_object, int obj_idx, @@ -746,6 +824,7 @@ void GLVolumeCollection::load_object_auxiliary( // Timestamp of the last change of the milestone size_t timestamp, bool opengl_initialized) +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL { assert(print_object->is_step_done(milestone)); Transform3d mesh_trafo_inv = print_object->trafo().inverse(); @@ -758,12 +837,21 @@ void GLVolumeCollection::load_object_auxiliary( const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR)); GLVolume& v = *this->volumes.back(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_SMOOTH_NORMALS + v.model.init_from(mesh, true); +#else + v.model.init_from(mesh); +#endif // ENABLE_SMOOTH_NORMALS + v.model.set_color((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR); +#else #if ENABLE_SMOOTH_NORMALS v.indexed_vertex_array.load_mesh(mesh, true); #else v.indexed_vertex_array.load_mesh(mesh); #endif // ENABLE_SMOOTH_NORMALS v.indexed_vertex_array.finalize_geometry(opengl_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first); v.geometry_id = std::pair(timestamp, model_instance.id().id); // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. @@ -779,6 +867,17 @@ void GLVolumeCollection::load_object_auxiliary( } } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +int GLVolumeCollection::load_wipe_tower_preview( + float pos_x, float pos_y, float width, float depth, float height, + float rotation_angle, bool size_unknown, float brim_width) +#else +int GLVolumeCollection::load_wipe_tower_preview( + int obj_idx, float pos_x, float pos_y, float width, float depth, float height, + float rotation_angle, bool size_unknown, float brim_width) +#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#else #if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL int GLVolumeCollection::load_wipe_tower_preview( float pos_x, float pos_y, float width, float depth, float height, @@ -788,6 +887,7 @@ int GLVolumeCollection::load_wipe_tower_preview( int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized) #endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL { if (depth < 0.01f) return int(this->volumes.size() - 1); @@ -844,9 +944,16 @@ int GLVolumeCollection::load_wipe_tower_preview( volumes.emplace_back(new GLVolume(color)); GLVolume& v = *volumes.back(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + v.model.init_from(mesh); + v.model.set_color(color); +#else v.indexed_vertex_array.load_mesh(mesh); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL v.set_convex_hull(mesh.convex_hull_3d()); +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL v.indexed_vertex_array.finalize_geometry(opengl_initialized); +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle)); #if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL @@ -861,6 +968,22 @@ int GLVolumeCollection::load_wipe_tower_preview( return int(volumes.size() - 1); } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +GLVolume* GLVolumeCollection::new_toolpath_volume(const ColorRGBA& rgba) +{ + GLVolume* out = new_nontoolpath_volume(rgba); + out->is_extrusion_path = true; + return out; +} + +GLVolume* GLVolumeCollection::new_nontoolpath_volume(const ColorRGBA& rgba) +{ + GLVolume* out = new GLVolume(rgba); + out->is_extrusion_path = false; + this->volumes.emplace_back(out); + return out; +} +#else GLVolume* GLVolumeCollection::new_toolpath_volume(const ColorRGBA& rgba, size_t reserve_vbo_floats) { GLVolume *out = new_nontoolpath_volume(rgba, reserve_vbo_floats); @@ -877,6 +1000,7 @@ GLVolume* GLVolumeCollection::new_nontoolpath_volume(const ColorRGBA& rgba, size this->volumes.emplace_back(out); return out; } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function filter_func) { @@ -965,7 +1089,10 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); - shader->set_uniform("uniform_color", volume.first->render_color); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (!volume.first->model.is_initialized()) +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + shader->set_uniform("uniform_color", volume.first->render_color); shader->set_uniform("z_range", m_z_range, 2); shader->set_uniform("clipping_plane", m_clipping_plane, 4); shader->set_uniform("print_volume.type", static_cast(m_print_volume.type)); @@ -985,6 +1112,10 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab #endif // ENABLE_ENVIRONMENT_MAP glcheck(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (volume.first->model.is_initialized()) + volume.first->model.set_color(volume.first->render_color); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL volume.first->render(); #if ENABLE_ENVIRONMENT_MAP @@ -1220,6 +1351,466 @@ std::string GLVolumeCollection::log_memory_info() const return " (GLVolumeCollection RAM: " + format_memsize_MB(this->cpu_memory_used()) + " GPU: " + format_memsize_MB(this->gpu_memory_used()) + " Both: " + format_memsize_MB(this->gpu_memory_used()) + ")"; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +static void thick_lines_to_geometry( + const Lines& lines, + const std::vector& widths, + const std::vector& heights, + bool closed, + double top_z, + GUI::GLModel::Geometry& geometry) +{ + assert(!lines.empty()); + if (lines.empty()) + return; + + enum Direction : unsigned char + { + Left, + Right, + Top, + Bottom + }; + + // right, left, top, bottom + std::array idx_prev = { -1, -1, -1, -1 }; + std::array idx_initial = { -1, -1, -1, -1 }; + + double bottom_z_prev = 0.0; + Vec2d b1_prev(Vec2d::Zero()); + Vec2d v_prev(Vec2d::Zero()); + double len_prev = 0.0; + double width_initial = 0.0; + double bottom_z_initial = 0.0; + + // loop once more in case of closed loops + const size_t lines_end = closed ? (lines.size() + 1) : lines.size(); + for (size_t ii = 0; ii < lines_end; ++ii) { + const size_t i = (ii == lines.size()) ? 0 : ii; + const Line& line = lines[i]; + const double bottom_z = top_z - heights[i]; + const double middle_z = 0.5 * (top_z + bottom_z); + const double width = widths[i]; + + const bool is_first = (ii == 0); + const bool is_last = (ii == lines_end - 1); + const bool is_closing = closed && is_last; + + const Vec2d v = unscale(line.vector()).normalized(); + const double len = unscale(line.length()); + + const Vec2d a = unscale(line.a); + const Vec2d b = unscale(line.b); + Vec2d a1 = a; + Vec2d a2 = a; + Vec2d b1 = b; + Vec2d b2 = b; + { + const double dist = 0.5 * width; // scaled + const double dx = dist * v.x(); + const double dy = dist * v.y(); + a1 += Vec2d(+dy, -dx); + a2 += Vec2d(-dy, +dx); + b1 += Vec2d(+dy, -dx); + b2 += Vec2d(-dy, +dx); + } + + // calculate new XY normals + const Vec2d xy_right_normal = unscale(line.normal()).normalized(); + + std::array idx_a = { 0, 0, 0, 0 }; + std::array idx_b = { 0, 0, 0, 0 }; + int idx_last = int(geometry.vertices_count()); + + const bool bottom_z_different = bottom_z_prev != bottom_z; + bottom_z_prev = bottom_z; + + if (!is_first && bottom_z_different) { + // Found a change of the layer thickness -> Add a cap at the end of the previous segment. + geometry.add_uint_triangle(idx_b[Bottom], idx_b[Left], idx_b[Top]); + geometry.add_uint_triangle(idx_b[Bottom], idx_b[Top], idx_b[Right]); + } + + // Share top / bottom vertices if possible. + if (is_first) { + idx_a[Top] = idx_last++; + geometry.add_vertex(Vec3f(a.x(), a.y(), top_z), Vec3f(0.0f, 0.0f, 1.0f)); + } + else + idx_a[Top] = idx_prev[Top]; + + if (is_first || bottom_z_different) { + // Start of the 1st line segment or a change of the layer thickness while maintaining the print_z. + idx_a[Bottom] = idx_last++; + geometry.add_vertex(Vec3f(a.x(), a.y(), bottom_z), Vec3f(0.0f, 0.0f, -1.0f)); + idx_a[Left] = idx_last++; + geometry.add_vertex(Vec3f(a2.x(), a2.y(), middle_z), Vec3f(-xy_right_normal.x(), -xy_right_normal.y(), 0.0f)); + idx_a[Right] = idx_last++; + geometry.add_vertex(Vec3f(a1.x(), a1.y(), middle_z), Vec3f(xy_right_normal.x(), xy_right_normal.y(), 0.0f)); + } + else + idx_a[Bottom] = idx_prev[Bottom]; + + if (is_first) { + // Start of the 1st line segment. + width_initial = width; + bottom_z_initial = bottom_z; + idx_initial = idx_a; + } + else { + // Continuing a previous segment. + // Share left / right vertices if possible. + const double v_dot = v_prev.dot(v); + // To reduce gpu memory usage, we try to reuse vertices + // To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges + // is longer than a fixed threshold. + // The following value is arbitrary, it comes from tests made on a bunch of models showing the visual artifacts + const double len_threshold = 2.5; + + // Generate new vertices if the angle between adjacent edges is greater than 45 degrees or thresholds conditions are met + const bool sharp = (v_dot < 0.707) || (len_prev > len_threshold) || (len > len_threshold); + if (sharp) { + if (!bottom_z_different) { + // Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn. + idx_a[Right] = idx_last++; + geometry.add_vertex(Vec3f(a1.x(), a1.y(), middle_z), Vec3f(xy_right_normal.x(), xy_right_normal.y(), 0.0f)); + idx_a[Left] = idx_last++; + geometry.add_vertex(Vec3f(a2.x(), a2.y(), middle_z), Vec3f(-xy_right_normal.x(), -xy_right_normal.y(), 0.0f)); + if (cross2(v_prev, v) > 0.0) { + // Right turn. Fill in the right turn wedge. + geometry.add_uint_triangle(idx_prev[Right], idx_a[Right], idx_prev[Top]); + geometry.add_uint_triangle(idx_prev[Right], idx_prev[Bottom], idx_a[Right]); + } + else { + // Left turn. Fill in the left turn wedge. + geometry.add_uint_triangle(idx_prev[Left], idx_prev[Top], idx_a[Left]); + geometry.add_uint_triangle(idx_prev[Left], idx_a[Left], idx_prev[Bottom]); + } + } + } + else { + if (!bottom_z_different) { + // The two successive segments are nearly collinear. + idx_a[Left] = idx_prev[Left]; + idx_a[Right] = idx_prev[Right]; + } + } + if (is_closing) { + if (!sharp) { + if (!bottom_z_different) { + // Closing a loop with smooth transition. Unify the closing left / right vertices. + geometry.set_vertex(idx_initial[Left], geometry.extract_position_3(idx_prev[Left]), geometry.extract_normal_3(idx_prev[Left])); + geometry.set_vertex(idx_initial[Right], geometry.extract_position_3(idx_prev[Right]), geometry.extract_normal_3(idx_prev[Right])); + geometry.remove_vertex(geometry.vertices_count() - 1); + geometry.remove_vertex(geometry.vertices_count() - 1); + // Replace the left / right vertex indices to point to the start of the loop. + const size_t indices_count = geometry.indices_count(); + for (size_t u = indices_count - 24; u < indices_count; ++u) { + const unsigned int id = geometry.extract_uint_index(u); + if (id == (unsigned int)idx_prev[Left]) + geometry.set_uint_index(u, (unsigned int)idx_initial[Left]); + else if (id == (unsigned int)idx_prev[Right]) + geometry.set_uint_index(u, (unsigned int)idx_initial[Right]); + } + } + } + // This is the last iteration, only required to solve the transition. + break; + } + } + + // Only new allocate top / bottom vertices, if not closing a loop. + if (is_closing) + idx_b[Top] = idx_initial[Top]; + else { + idx_b[Top] = idx_last++; + geometry.add_vertex(Vec3f(b.x(), b.y(), top_z), Vec3f(0.0f, 0.0f, 1.0f)); + } + + if (is_closing && width == width_initial && bottom_z == bottom_z_initial) + idx_b[Bottom] = idx_initial[Bottom]; + else { + idx_b[Bottom] = idx_last++; + geometry.add_vertex(Vec3f(b.x(), b.y(), bottom_z), Vec3f(0.0f, 0.0f, -1.0f)); + } + // Generate new vertices for the end of this line segment. + idx_b[Left] = idx_last++; + geometry.add_vertex(Vec3f(b2.x(), b2.y(), middle_z), Vec3f(-xy_right_normal.x(), -xy_right_normal.y(), 0.0f)); + idx_b[Right] = idx_last++; + geometry.add_vertex(Vec3f(b1.x(), b1.y(), middle_z), Vec3f(xy_right_normal.x(), xy_right_normal.y(), 0.0f)); + + idx_prev = idx_b; + bottom_z_prev = bottom_z; + b1_prev = b1; + v_prev = v; + len_prev = len; + + if (bottom_z_different && (closed || (!is_first && !is_last))) { + // Found a change of the layer thickness -> Add a cap at the beginning of this segment. + geometry.add_uint_triangle(idx_a[Bottom], idx_a[Right], idx_a[Top]); + geometry.add_uint_triangle(idx_a[Bottom], idx_a[Top], idx_a[Left]); + } + + if (!closed) { + // Terminate open paths with caps. + if (is_first) { + geometry.add_uint_triangle(idx_a[Bottom], idx_a[Right], idx_a[Top]); + geometry.add_uint_triangle(idx_a[Bottom], idx_a[Top], idx_a[Left]); + } + // We don't use 'else' because both cases are true if we have only one line. + if (is_last) { + geometry.add_uint_triangle(idx_b[Bottom], idx_b[Left], idx_b[Top]); + geometry.add_uint_triangle(idx_b[Bottom], idx_b[Top], idx_b[Right]); + } + } + + // Add quads for a straight hollow tube-like segment. + // bottom-right face + geometry.add_uint_triangle(idx_a[Bottom], idx_b[Bottom], idx_b[Right]); + geometry.add_uint_triangle(idx_a[Bottom], idx_b[Right], idx_a[Right]); + // top-right face + geometry.add_uint_triangle(idx_a[Right], idx_b[Right], idx_b[Top]); + geometry.add_uint_triangle(idx_a[Right], idx_b[Top], idx_a[Top]); + // top-left face + geometry.add_uint_triangle(idx_a[Top], idx_b[Top], idx_b[Left]); + geometry.add_uint_triangle(idx_a[Top], idx_b[Left], idx_a[Left]); + // bottom-left face + geometry.add_uint_triangle(idx_a[Left], idx_b[Left], idx_b[Bottom]); + geometry.add_uint_triangle(idx_a[Left], idx_b[Bottom], idx_a[Bottom]); + } +} + +// caller is responsible for supplying NO lines with zero length +static void thick_lines_to_geometry( + const Lines3& lines, + const std::vector& widths, + const std::vector& heights, + bool closed, + GUI::GLModel::Geometry& geometry) +{ + assert(!lines.empty()); + if (lines.empty()) + return; + + enum Direction : unsigned char + { + Left, + Right, + Top, + Bottom + }; + + // left, right, top, bottom + std::array idx_prev = { -1, -1, -1, -1 }; + std::array idx_initial = { -1, -1, -1, -1 }; + + double z_prev = 0.0; + double len_prev = 0.0; + Vec3d n_right_prev = Vec3d::Zero(); + Vec3d n_top_prev = Vec3d::Zero(); + Vec3d unit_v_prev = Vec3d::Zero(); + double width_initial = 0.0; + + // new vertices around the line endpoints + // left, right, top, bottom + std::array a = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; + std::array b = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; + + // loop once more in case of closed loops + const size_t lines_end = closed ? (lines.size() + 1) : lines.size(); + for (size_t ii = 0; ii < lines_end; ++ii) { + const size_t i = (ii == lines.size()) ? 0 : ii; + + const Line3& line = lines[i]; + const double height = heights[i]; + const double width = widths[i]; + + const Vec3d unit_v = unscale(line.vector()).normalized(); + const double len = unscale(line.length()); + + Vec3d n_top = Vec3d::Zero(); + Vec3d n_right = Vec3d::Zero(); + + if (line.a.x() == line.b.x() && line.a.y() == line.b.y()) { + // vertical segment + n_top = Vec3d::UnitY(); + n_right = Vec3d::UnitX(); + if (line.a.z() < line.b.z()) + n_right = -n_right; + } + else { + // horizontal segment + n_right = unit_v.cross(Vec3d::UnitZ()).normalized(); + n_top = n_right.cross(unit_v).normalized(); + } + + const Vec3d rl_displacement = 0.5 * width * n_right; + const Vec3d tb_displacement = 0.5 * height * n_top; + const Vec3d l_a = unscale(line.a); + const Vec3d l_b = unscale(line.b); + + a[Right] = l_a + rl_displacement; + a[Left] = l_a - rl_displacement; + a[Top] = l_a + tb_displacement; + a[Bottom] = l_a - tb_displacement; + b[Right] = l_b + rl_displacement; + b[Left] = l_b - rl_displacement; + b[Top] = l_b + tb_displacement; + b[Bottom] = l_b - tb_displacement; + + const Vec3d n_bottom = -n_top; + const Vec3d n_left = -n_right; + + std::array idx_a = { 0, 0, 0, 0}; + std::array idx_b = { 0, 0, 0, 0 }; + int idx_last = int(geometry.vertices_count()); + + const bool z_different = (z_prev != l_a.z()); + z_prev = l_b.z(); + + // Share top / bottom vertices if possible. + if (ii == 0) { + idx_a[Top] = idx_last++; + geometry.add_vertex((Vec3f)a[Top].cast(), (Vec3f)n_top.cast()); + } + else + idx_a[Top] = idx_prev[Top]; + + if (ii == 0 || z_different) { + // Start of the 1st line segment or a change of the layer thickness while maintaining the print_z. + idx_a[Bottom] = idx_last++; + geometry.add_vertex((Vec3f)a[Bottom].cast(), (Vec3f)n_bottom.cast()); + idx_a[Left] = idx_last++; + geometry.add_vertex((Vec3f)a[Left].cast(), (Vec3f)n_left.cast()); + idx_a[Right] = idx_last++; + geometry.add_vertex((Vec3f)a[Right].cast(), (Vec3f)n_right.cast()); + } + else + idx_a[Bottom] = idx_prev[Bottom]; + + if (ii == 0) { + // Start of the 1st line segment. + width_initial = width; + idx_initial = idx_a; + } + else { + // Continuing a previous segment. + // Share left / right vertices if possible. + const double v_dot = unit_v_prev.dot(unit_v); + const bool is_right_turn = n_top_prev.dot(unit_v_prev.cross(unit_v)) > 0.0; + + // To reduce gpu memory usage, we try to reuse vertices + // To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges + // is longer than a fixed threshold. + // The following value is arbitrary, it comes from tests made on a bunch of models showing the visual artifacts + const double len_threshold = 2.5; + + // Generate new vertices if the angle between adjacent edges is greater than 45 degrees or thresholds conditions are met + const bool is_sharp = v_dot < 0.707 || len_prev > len_threshold || len > len_threshold; + if (is_sharp) { + // Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn. + idx_a[Right] = idx_last++; + geometry.add_vertex((Vec3f)a[Right].cast(), (Vec3f)n_right.cast()); + idx_a[Left] = idx_last++; + geometry.add_vertex((Vec3f)a[Left].cast(), (Vec3f)n_left.cast()); + + if (is_right_turn) { + // Right turn. Fill in the right turn wedge. + geometry.add_uint_triangle(idx_prev[Right], idx_a[Right], idx_prev[Top]); + geometry.add_uint_triangle(idx_prev[Right], idx_prev[Bottom], idx_a[Right]); + } + else { + // Left turn. Fill in the left turn wedge. + geometry.add_uint_triangle(idx_prev[Left], idx_prev[Top], idx_a[Left]); + geometry.add_uint_triangle(idx_prev[Left], idx_a[Left], idx_prev[Bottom]); + } + } + else { + // The two successive segments are nearly collinear. + idx_a[Left] = idx_prev[Left]; + idx_a[Right] = idx_prev[Right]; + } + + if (ii == lines.size()) { + if (!is_sharp) { + // Closing a loop with smooth transition. Unify the closing left / right vertices. + geometry.set_vertex(idx_initial[Left], geometry.extract_position_3(idx_prev[Left]), geometry.extract_normal_3(idx_prev[Left])); + geometry.set_vertex(idx_initial[Right], geometry.extract_position_3(idx_prev[Right]), geometry.extract_normal_3(idx_prev[Right])); + geometry.remove_vertex(geometry.vertices_count() - 1); + geometry.remove_vertex(geometry.vertices_count() - 1); + // Replace the left / right vertex indices to point to the start of the loop. + const size_t indices_count = geometry.indices_count(); + for (size_t u = indices_count - 24; u < indices_count; ++u) { + const unsigned int id = geometry.extract_uint_index(u); + if (id == (unsigned int)idx_prev[Left]) + geometry.set_uint_index(u, (unsigned int)idx_initial[Left]); + else if (id == (unsigned int)idx_prev[Right]) + geometry.set_uint_index(u, (unsigned int)idx_initial[Right]); + } + } + + // This is the last iteration, only required to solve the transition. + break; + } + } + + // Only new allocate top / bottom vertices, if not closing a loop. + if (closed && ii + 1 == lines.size()) + idx_b[Top] = idx_initial[Top]; + else { + idx_b[Top] = idx_last++; + geometry.add_vertex((Vec3f)b[Top].cast(), (Vec3f)n_top.cast()); + } + + if (closed && ii + 1 == lines.size() && width == width_initial) + idx_b[Bottom] = idx_initial[Bottom]; + else { + idx_b[Bottom] = idx_last++; + geometry.add_vertex((Vec3f)b[Bottom].cast(), (Vec3f)n_bottom.cast()); + } + + // Generate new vertices for the end of this line segment. + idx_b[Left] = idx_last++; + geometry.add_vertex((Vec3f)b[Left].cast(), (Vec3f)n_left.cast()); + idx_b[Right] = idx_last++; + geometry.add_vertex((Vec3f)b[Right].cast(), (Vec3f)n_right.cast()); + + idx_prev = idx_b; + n_right_prev = n_right; + n_top_prev = n_top; + unit_v_prev = unit_v; + len_prev = len; + + if (!closed) { + // Terminate open paths with caps. + if (i == 0) { + geometry.add_uint_triangle(idx_a[Bottom], idx_a[Right], idx_a[Top]); + geometry.add_uint_triangle(idx_a[Bottom], idx_a[Top], idx_a[Left]); + } + + // We don't use 'else' because both cases are true if we have only one line. + if (i + 1 == lines.size()) { + geometry.add_uint_triangle(idx_b[Bottom], idx_b[Left], idx_b[Top]); + geometry.add_uint_triangle(idx_b[Bottom], idx_b[Top], idx_b[Right]); + } + } + + // Add quads for a straight hollow tube-like segment. + // bottom-right face + geometry.add_uint_triangle(idx_a[Bottom], idx_b[Bottom], idx_b[Right]); + geometry.add_uint_triangle(idx_a[Bottom], idx_b[Right], idx_a[Right]); + // top-right face + geometry.add_uint_triangle(idx_a[Right], idx_b[Right], idx_b[Top]); + geometry.add_uint_triangle(idx_a[Right], idx_b[Top], idx_a[Top]); + // top-left face + geometry.add_uint_triangle(idx_a[Top], idx_b[Top], idx_b[Left]); + geometry.add_uint_triangle(idx_a[Top], idx_b[Left], idx_a[Left]); + // bottom-left face + geometry.add_uint_triangle(idx_a[Left], idx_b[Left], idx_b[Bottom]); + geometry.add_uint_triangle(idx_a[Left], idx_b[Bottom], idx_a[Bottom]); + } +} +#else // caller is responsible for supplying NO lines with zero length static void thick_lines_to_indexed_vertex_array( const Lines &lines, @@ -1729,7 +2320,30 @@ static void point_to_indexed_vertex_array(const Vec3crd& point, volume.push_triangle(idxs[3], idxs[1], idxs[4]); volume.push_triangle(idxs[0], idxs[3], idxs[4]); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void _3DScene::thick_lines_to_verts( + const Lines& lines, + const std::vector& widths, + const std::vector& heights, + bool closed, + double top_z, + GUI::GLModel::Geometry& geometry) +{ + thick_lines_to_geometry(lines, widths, heights, closed, top_z, geometry); +} + +void _3DScene::thick_lines_to_verts( + const Lines3& lines, + const std::vector& widths, + const std::vector& heights, + bool closed, + GUI::GLModel::Geometry& geometry) +{ + thick_lines_to_geometry(lines, widths, heights, closed, geometry); +} +#else void _3DScene::thick_lines_to_verts( const Lines &lines, const std::vector &widths, @@ -1771,8 +2385,21 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, flo { extrusionentity_to_verts(extrusion_path.polyline, extrusion_path.width, extrusion_path.height, print_z, volume); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Fill in the qverts and tverts with quads and triangles for the extrusion_path. +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void _3DScene::extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) +{ + Polyline polyline = extrusion_path.polyline; + polyline.remove_duplicate_points(); + polyline.translate(copy); + const Lines lines = polyline.lines(); + std::vector widths(lines.size(), extrusion_path.width); + std::vector heights(lines.size(), extrusion_path.height); + thick_lines_to_verts(lines, widths, heights, false, print_z, geometry); +} +#else void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point ©, GLVolume &volume) { Polyline polyline = extrusion_path.polyline; @@ -1783,8 +2410,27 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, flo std::vector heights(lines.size(), extrusion_path.height); thick_lines_to_verts(lines, widths, heights, false, print_z, volume); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Fill in the qverts and tverts with quads and triangles for the extrusion_loop. +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void _3DScene::extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) +{ + Lines lines; + std::vector widths; + std::vector heights; + for (const ExtrusionPath& extrusion_path : extrusion_loop.paths) { + Polyline polyline = extrusion_path.polyline; + polyline.remove_duplicate_points(); + polyline.translate(copy); + const Lines lines_this = polyline.lines(); + append(lines, lines_this); + widths.insert(widths.end(), lines_this.size(), extrusion_path.width); + heights.insert(heights.end(), lines_this.size(), extrusion_path.height); + } + thick_lines_to_verts(lines, widths, heights, true, print_z, geometry); +} +#else void _3DScene::extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point ©, GLVolume &volume) { Lines lines; @@ -1801,8 +2447,27 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, flo } thick_lines_to_verts(lines, widths, heights, true, print_z, volume); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Fill in the qverts and tverts with quads and triangles for the extrusion_multi_path. +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) +{ + Lines lines; + std::vector widths; + std::vector heights; + for (const ExtrusionPath& extrusion_path : extrusion_multi_path.paths) { + Polyline polyline = extrusion_path.polyline; + polyline.remove_duplicate_points(); + polyline.translate(copy); + const Lines lines_this = polyline.lines(); + append(lines, lines_this); + widths.insert(widths.end(), lines_this.size(), extrusion_path.width); + heights.insert(heights.end(), lines_this.size(), extrusion_path.height); + } + thick_lines_to_verts(lines, widths, heights, false, print_z, geometry); +} +#else void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point ©, GLVolume &volume) { Lines lines; @@ -1819,13 +2484,49 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_mult } thick_lines_to_verts(lines, widths, heights, false, print_z, volume); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void _3DScene::extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) +{ + for (const ExtrusionEntity* extrusion_entity : extrusion_entity_collection.entities) + extrusionentity_to_verts(extrusion_entity, print_z, copy, geometry); +} +#else void _3DScene::extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point ©, GLVolume &volume) { for (const ExtrusionEntity *extrusion_entity : extrusion_entity_collection.entities) extrusionentity_to_verts(extrusion_entity, print_z, copy, volume); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void _3DScene::extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) +{ + if (extrusion_entity != nullptr) { + auto* extrusion_path = dynamic_cast(extrusion_entity); + if (extrusion_path != nullptr) + extrusionentity_to_verts(*extrusion_path, print_z, copy, geometry); + else { + auto* extrusion_loop = dynamic_cast(extrusion_entity); + if (extrusion_loop != nullptr) + extrusionentity_to_verts(*extrusion_loop, print_z, copy, geometry); + else { + auto* extrusion_multi_path = dynamic_cast(extrusion_entity); + if (extrusion_multi_path != nullptr) + extrusionentity_to_verts(*extrusion_multi_path, print_z, copy, geometry); + else { + auto* extrusion_entity_collection = dynamic_cast(extrusion_entity); + if (extrusion_entity_collection != nullptr) + extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, geometry); + else + throw Slic3r::RuntimeError("Unexpected extrusion_entity type in to_verts()"); + } + } + } + } +} +#else void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume) { if (extrusion_entity != nullptr) { @@ -1844,9 +2545,8 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, auto *extrusion_entity_collection = dynamic_cast(extrusion_entity); if (extrusion_entity_collection != nullptr) extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, volume); - else { + else throw Slic3r::RuntimeError("Unexpected extrusion_entity type in to_verts()"); - } } } } @@ -1865,5 +2565,6 @@ void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height { thick_point_to_verts(point, width, height, volume); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } // namespace Slic3r diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 5aaa759f2f..ed2aa804eb 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -46,6 +46,7 @@ enum ModelInstanceEPrintVolumeState : unsigned char; // Return appropriate color based on the ModelVolume. extern ColorRGBA color_from_model_volume(const ModelVolume& model_volume); +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // A container for interleaved arrays of 3D vertices and normals, // possibly indexed by triangles and / or quads. class GLIndexedVertexArray { @@ -246,6 +247,7 @@ public: private: BoundingBox m_bounding_box; }; +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL class GLVolume { public: @@ -388,11 +390,17 @@ public: // Is mouse or rectangle selection over this object to select/deselect it ? EHoverState hover; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GUI::GLModel model; +#else // Interleaved triangles & normals with indexed triangles & quads. GLIndexedVertexArray indexed_vertex_array; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Ranges of triangle and quad indices to be rendered. std::pair tverts_range; +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL std::pair qverts_range; +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // If the qverts or tverts contain thick extrusions, then offsets keeps pointers of the starts // of the extrusions per layer. @@ -402,13 +410,17 @@ public: // Bounding box of this volume, in unscaled coordinates. BoundingBoxf3 bounding_box() const { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + return this->model.get_bounding_box(); +#else BoundingBoxf3 out; - if (! this->indexed_vertex_array.bounding_box().isEmpty()) { + if (!this->indexed_vertex_array.bounding_box().isEmpty()) { out.min = this->indexed_vertex_array.bounding_box().min().cast(); out.max = this->indexed_vertex_array.bounding_box().max().cast(); out.defined = true; - }; + } return out; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } void set_color(const ColorRGBA& rgba) { color = rgba; } @@ -498,14 +510,20 @@ public: // convex hull const TriangleMesh* convex_hull() const { return m_convex_hull.get(); } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + bool empty() const { return this->model.is_empty(); } +#else bool empty() const { return this->indexed_vertex_array.empty(); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL void set_range(double low, double high); - void render() const; + void render(); +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); } void release_geometry() { this->indexed_vertex_array.release_geometry(); } +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL void set_bounding_boxes_as_dirty() { m_transformed_bounding_box.reset(); @@ -524,12 +542,20 @@ public: #endif // ENABLE_SHOW_NON_MANIFOLD_EDGES // Return an estimate of the memory consumed by this class. - size_t cpu_memory_used() const { - //FIXME what to do wih m_convex_hull? + size_t cpu_memory_used() const { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + return sizeof(*this) + this->model.cpu_memory_used() + this->print_zs.capacity() * sizeof(coordf_t) + + this->offsets.capacity() * sizeof(size_t); + } + // Return an estimate of the memory held by GPU vertex buffers. + size_t gpu_memory_used() const { return this->model.gpu_memory_used(); } +#else + //FIXME what to do wih m_convex_hull? return sizeof(*this) - sizeof(this->indexed_vertex_array) + this->indexed_vertex_array.cpu_memory_used() + this->print_zs.capacity() * sizeof(coordf_t) + this->offsets.capacity() * sizeof(size_t); } // Return an estimate of the memory held by GPU vertex buffers. size_t gpu_memory_used() const { return this->indexed_vertex_array.gpu_memory_used(); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); } }; @@ -589,11 +615,40 @@ public: GLVolumeCollection() { set_default_slope_normal_z(); } ~GLVolumeCollection() { clear(); } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + std::vector load_object( + const ModelObject* model_object, + int obj_idx, + const std::vector& instance_idxs); + + int load_object_volume( + const ModelObject* model_object, + int obj_idx, + int volume_idx, + int instance_idx); + + // Load SLA auxiliary GLVolumes (for support trees or pad). + void load_object_auxiliary( + const SLAPrintObject* print_object, + int obj_idx, + // pairs of + const std::vector>& instances, + SLAPrintObjectStep milestone, + // Timestamp of the last change of the milestone + size_t timestamp); + +#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL + int load_wipe_tower_preview( + float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width); +#else + int load_wipe_tower_preview( + int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width); +#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#else std::vector load_object( 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 +656,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). @@ -622,13 +676,20 @@ public: int load_wipe_tower_preview( int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized); #endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLVolume* new_toolpath_volume(const ColorRGBA& rgba); + GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba); +#else GLVolume* new_toolpath_volume(const ColorRGBA& rgba, size_t reserve_vbo_floats = 0); GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba, size_t reserve_vbo_floats = 0); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Render the volumes by OpenGL. void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function filter_func = std::function()) const; +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Finalize the initialization of the geometry & indices, // upload the geometry and indices to OpenGL VBO objects // and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs. @@ -636,11 +697,12 @@ public: // Release the geometry data assigned to the volumes. // If OpenGL VBOs were allocated, an OpenGL context has to be active to release them. void release_geometry() { for (auto *v : volumes) v->release_geometry(); } +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Clear the geometry void clear() { for (auto *v : volumes) delete v; volumes.clear(); } bool empty() const { return volumes.empty(); } - void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); } + void set_range(double low, double high) { for (GLVolume* vol : this->volumes) vol->set_range(low, high); } void set_print_volume(const PrintVolume& print_volume) { m_print_volume = print_volume; } @@ -685,9 +747,18 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo struct _3DScene { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + static void thick_lines_to_verts(const Lines& lines, const std::vector& widths, const std::vector& heights, bool closed, double top_z, GUI::GLModel::Geometry& geometry); + static void thick_lines_to_verts(const Lines3& lines, const std::vector& widths, const std::vector& heights, bool closed, GUI::GLModel::Geometry& geometry); + static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); + static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); + static void extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); + static void extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); + static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); +#else static void thick_lines_to_verts(const Lines& lines, const std::vector& widths, const std::vector& heights, bool closed, double top_z, GLVolume& volume); static void thick_lines_to_verts(const Lines3& lines, const std::vector& widths, const std::vector& heights, bool closed, GLVolume& volume); - static void extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume); + static void extrusionentity_to_verts(const Polyline& polyline, float width, float height, float print_z, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GLVolume& volume); @@ -696,6 +767,7 @@ struct _3DScene static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GLVolume& volume); static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume); static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL }; } 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/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index a4808a0cf0..cfe6fe418a 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; @@ -695,13 +711,26 @@ void GCodeViewer::init() m_gl_data_initialized = true; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& print) +#else void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& print, bool initialized) +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL { // 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(); @@ -725,7 +754,11 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr m_filament_densities = gcode_result.filament_densities; if (wxGetApp().is_editor()) +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + load_shells(print); +#else load_shells(print, initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL else { Pointfs bed_shape; std::string texture; @@ -1266,9 +1299,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 +1326,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 +1344,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 +1393,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 +1446,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 +1534,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 +1678,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 +1746,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 +1761,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 +2129,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: { @@ -2213,7 +2297,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) progress_dialog->Destroy(); } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void GCodeViewer::load_shells(const Print& print) +#else void GCodeViewer::load_shells(const Print& print, bool initialized) +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL { if (print.objects().empty()) // no shells, return @@ -2230,7 +2318,11 @@ 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); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + m_shells.volumes.load_object(model_obj, object_id, instance_ids); +#else + m_shells.volumes.load_object(model_obj, object_id, instance_ids, initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // adjust shells' z if raft is present const SlicingParameters& slicing_parameters = obj->slicing_parameters(); @@ -2254,6 +2346,15 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) const float depth = print.wipe_tower_data(extruders_count).depth; const float brim_width = print.wipe_tower_data(extruders_count).brim_width; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL + m_shells.volumes.load_wipe_tower_preview(config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, + !print.is_step_done(psWipeTower), brim_width); +#else + m_shells.volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, + !print.is_step_done(psWipeTower), brim_width); +#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#else #if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL m_shells.volumes.load_wipe_tower_preview(config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, !print.is_step_done(psWipeTower), brim_width, initialized); @@ -2261,6 +2362,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) m_shells.volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, !print.is_step_done(psWipeTower), brim_width, initialized); #endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } } @@ -3123,6 +3225,7 @@ void GCodeViewer::render_shells() if (shader == nullptr) return; +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // when the background processing is enabled, it may happen that the shells data have been loaded // before opengl has been initialized for the preview canvas. // when this happens, the volumes' data have not been sent to gpu yet. @@ -3130,6 +3233,7 @@ void GCodeViewer::render_shells() if (!v->indexed_vertex_array.has_VBOs()) v->finalize_geometry(true); } +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // glsafe(::glDepthMask(GL_FALSE)); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 22e866fe6e..bdb3ed983e 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 @@ -816,7 +823,11 @@ public: void init(); // extract rendering data from the given parameters +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + void load(const GCodeProcessorResult& gcode_result, const Print& print); +#else void load(const GCodeProcessorResult& gcode_result, const Print& print, bool initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // recalculate ranges in dependence of what is visible and sets tool/print colors void refresh(const GCodeProcessorResult& gcode_result, const std::vector& str_tool_colors); #if ENABLE_PREVIEW_LAYOUT @@ -876,7 +887,11 @@ public: private: void load_toolpaths(const GCodeProcessorResult& gcode_result); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + void load_shells(const Print& print); +#else void load_shells(const Print& print, bool initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #if !ENABLE_PREVIEW_LAYOUT void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; #endif // !ENABLE_PREVIEW_LAYOUT diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ec81c1ba2c..76fae46645 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -87,9 +87,11 @@ static const Slic3r::ColorRGB ERROR_BG_LIGHT_COLOR = { 0.753f, 0.192f, 0.039f // Number of floats static constexpr const size_t MAX_VERTEX_BUFFER_SIZE = 131072 * 6; // 3.15MB // Reserve size in number of floats. +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL static constexpr const size_t VERTEX_BUFFER_RESERVE_SIZE = 131072 * 2; // 1.05MB // Reserve size in number of floats, maximum sum of all preallocated buffers. //static constexpr const size_t VERTEX_BUFFER_RESERVE_SIZE_SUM_MAX = 1024 * 1024 * 128 / 4; // 128MB +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL namespace Slic3r { namespace GUI { @@ -447,8 +449,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 +458,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); @@ -496,17 +497,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(); @@ -517,29 +518,27 @@ 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)); glsafe(::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0)); glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data())); 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) { + for (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() @@ -1107,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) @@ -1158,6 +1156,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)); @@ -1187,6 +1186,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)); @@ -1194,9 +1194,11 @@ bool GLCanvas3D::init() if (m_main_toolbar.is_enabled()) m_layers_editing.init(); +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // on linux the gl context is not valid until the canvas is not shown on screen // we defer the geometry finalization of volumes until the first call to render() m_volumes.finalize_geometry(true); +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL if (m_gizmos.is_enabled() && !m_gizmos.init()) std::cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std::endl; @@ -1357,11 +1359,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()); @@ -1806,7 +1803,11 @@ 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); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + return m_volumes.load_object(&model_object, obj_idx, instance_idxs); +#else + return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) @@ -2031,7 +2032,11 @@ 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); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx); +#else + m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL m_volumes.volumes.back()->geometry_id = key.geometry_id; update_object_list = true; } else { @@ -2088,31 +2093,55 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re GLVolume &volume = *m_volumes.volumes[it->volume_idx]; if (! volume.offsets.empty() && state.step[istep].timestamp != volume.offsets.front()) { // The backend either produced a new hollowed mesh, or it invalidated the one that the front end has seen. +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume.model.reset(); +#else volume.indexed_vertex_array.release_geometry(); - if (state.step[istep].state == PrintStateBase::DONE) { +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (state.step[istep].state == PrintStateBase::DONE) { TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles); assert(! mesh.empty()); mesh.transform(sla_print->sla_trafo(*m_model->objects[volume.object_idx()]).inverse()); #if ENABLE_SMOOTH_NORMALS +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume.model.init_from(mesh, true); +#else volume.indexed_vertex_array.load_mesh(mesh, true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#else +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume.model.init_from(mesh); #else volume.indexed_vertex_array.load_mesh(mesh); -#endif // ENABLE_SMOOTH_NORMALS - } else { - // Reload the original volume. -#if ENABLE_SMOOTH_NORMALS - volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); -#else - volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #endif // ENABLE_SMOOTH_NORMALS } + else { + // Reload the original volume. +#if ENABLE_SMOOTH_NORMALS +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); +#else + volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#else +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); +#else + volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#endif // ENABLE_SMOOTH_NORMALS + } +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL volume.finalize_geometry(true); - } +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + } //FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable // to the GLVolume. We should refactor GLVolume significantly, so that the GLVolume will not contain member variables // of various concenrs (model vs. 3D print path). volume.offsets = { state.step[istep].timestamp }; - } else if (state.step[istep].state == PrintStateBase::DONE) { + } + else if (state.step[istep].state == PrintStateBase::DONE) { // Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created. ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); @@ -2124,7 +2153,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re instances[istep].emplace_back(std::pair(instance_idx, print_instance_idx)); else shift_zs[object_idx] = 0.; - } else { + } + else { // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); @@ -2134,7 +2164,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re for (size_t istep = 0; istep < sla_steps.size(); ++istep) if (!instances[istep].empty()) +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp); +#else m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed @@ -2164,6 +2198,17 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re float depth = print->wipe_tower_data(extruders_count).depth; float brim_width = print->wipe_tower_data(extruders_count).brim_width; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL + int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( + x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower), + brim_width); +#else + int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( + 1000, x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower), + brim_width); +#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#else #if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower), @@ -2173,6 +2218,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re 1000, x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower), brim_width, m_initialized); #endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL if (volume_idx_wipe_tower_old != -1) map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new; } @@ -2232,9 +2278,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re m_dirty = true; } +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume& vol_old, bool gl_initialized, size_t prealloc_size = VERTEX_BUFFER_RESERVE_SIZE) { - // Assign the large pre-allocated buffers to the new GLVolume. + // Assign the large pre-allocated buffers to the new GLVolume. vol_new.indexed_vertex_array = std::move(vol_old.indexed_vertex_array); // Copy the content back to the old GLVolume. vol_old.indexed_vertex_array = vol_new.indexed_vertex_array; @@ -2246,10 +2293,15 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume& // Finalize the old geometry, possibly move data to the graphics card. vol_old.finalize_geometry(gl_initialized); } +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, const std::vector& str_tool_colors) { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + m_gcode_viewer.load(gcode_result, *this->fff_print()); +#else m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL if (wxGetApp().is_editor()) { m_gcode_viewer.update_shells_color_by_extruder(m_config); @@ -4345,7 +4397,11 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const shader->set_uniform("emission_factor", 0.0f); for (GLVolume* vol : visible_volumes) { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + vol->model.set_color((vol->printable && !vol->is_outside) ? (current_printer_technology() == ptSLA ? vol->color : ColorRGBA::ORANGE()) : ColorRGBA::GRAY()); +#else shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? (current_printer_technology() == ptSLA ? vol->color : ColorRGBA::ORANGE()) : ColorRGBA::GRAY()); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // the volume may have been deactivated by an active gizmo bool is_active = vol->is_active; vol->is_active = true; @@ -5014,9 +5070,9 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_be // clamp max bb size with respect to bed bb size if (!m_picking_enabled) { - static const double max_scale_factor = 1.5; + static const double max_scale_factor = 2.0; const Vec3d bb_size = bb.size(); - const Vec3d bed_bb_size = bed_bb.size(); + const Vec3d bed_bb_size = m_bed.build_volume().bounding_volume().size(); if (bb_size.x() > max_scale_factor * bed_bb_size.x() || bb_size.y() > max_scale_factor * bed_bb_size.y() || bb_size.z() > max_scale_factor * bed_bb_size.z()) { @@ -5551,6 +5607,12 @@ void GLCanvas3D::_render_overlays() void GLCanvas3D::_render_volumes_for_picking() const { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + // do not cull backfaces to show broken geometry, if any glsafe(::glDisable(GL_CULL_FACE)); @@ -5566,9 +5628,17 @@ void GLCanvas3D::_render_volumes_for_picking() const // we reserve color = (0,0,0) for occluders (as the printbed) // so we shift volumes' id by 1 to get the proper color const unsigned int id = 1 + volume.second.first; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume.first->model.set_color(picking_decode(id)); + shader->start_using(); +#else glsafe(::glColor4fv(picking_decode(id).data())); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL volume.first->render(); - } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + shader->stop_using(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + } } glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); @@ -5783,32 +5853,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(); @@ -5823,39 +5942,81 @@ 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()); - 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()) { @@ -5879,6 +6040,7 @@ void GLCanvas3D::_render_sla_slices() glsafe(::glPopMatrix()); } } +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL } } @@ -6062,23 +6224,48 @@ void GLCanvas3D::_load_print_toolpaths(const BuildVolume &build_volume) skirt_height = std::min(skirt_height, print_zs.size()); print_zs.erase(print_zs.begin() + skirt_height, print_zs.end()); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLVolume* volume = m_volumes.new_toolpath_volume(color); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; +#else GLVolume *volume = m_volumes.new_toolpath_volume(color, VERTEX_BUFFER_RESERVE_SIZE); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL for (size_t i = 0; i < skirt_height; ++ i) { volume->print_zs.emplace_back(print_zs[i]); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume->offsets.emplace_back(init_data.indices_count()); + if (i == 0) + _3DScene::extrusionentity_to_verts(print->brim(), print_zs[i], Point(0, 0), init_data); + _3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), init_data); +#else volume->offsets.emplace_back(volume->indexed_vertex_array.quad_indices.size()); volume->offsets.emplace_back(volume->indexed_vertex_array.triangle_indices.size()); if (i == 0) _3DScene::extrusionentity_to_verts(print->brim(), print_zs[i], Point(0, 0), *volume); _3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), *volume); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one. +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (init_data.vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) { + volume->model.init_from(std::move(init_data)); +#else if (volume->indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { - GLVolume &vol = *volume; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLVolume &vol = *volume; volume = m_volumes.new_toolpath_volume(vol.color); +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL reserve_new_volume_finalize_old_volume(*volume, vol, m_initialized); +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume->model.init_from(std::move(init_data)); + volume->is_outside = !contains(build_volume, volume->model); +#else volume->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(volume->indexed_vertex_array.vertices_and_normals_interleaved, volume->indexed_vertex_array.bounding_box()); volume->indexed_vertex_array.finalize_geometry(m_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const BuildVolume& build_volume, const std::vector& str_tool_colors, const std::vector& color_print_values) @@ -6250,7 +6437,12 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c // Allocate the volume before locking. GLVolume *volume = new GLVolume(color); volume->is_extrusion_path = true; - tbb::spin_mutex::scoped_lock lock; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + // to prevent sending data to gpu (in the main thread) while + // editing the model geometry + volume->model.disable_render(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + tbb::spin_mutex::scoped_lock lock; // Lock by ROII, so if the emplace_back() fails, the lock will be released. lock.acquire(new_volume_mutex); m_volumes.volumes.emplace_back(volume); @@ -6262,31 +6454,57 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c tbb::blocked_range(0, ctxt.layers.size(), grain_size), [&ctxt, &new_volume, is_selected_separate_extruder, this](const tbb::blocked_range& range) { GLVolumePtrs vols; - auto volume = [&ctxt, &vols](size_t layer_idx, int extruder, int feature) -> GLVolume& { - return *vols[ctxt.color_by_color_print()? +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + std::vector geometries; + auto select_geometry = [&ctxt, &geometries](size_t layer_idx, int extruder, int feature) -> GLModel::Geometry& { + return geometries[ctxt.color_by_color_print() ? ctxt.color_print_color_idx_by_layer_idx_and_extruder(layer_idx, extruder) : - ctxt.color_by_tool() ? - std::min(ctxt.number_tools() - 1, std::max(extruder - 1, 0)) : - feature - ]; + ctxt.color_by_tool() ? + std::min(ctxt.number_tools() - 1, std::max(extruder - 1, 0)) : + feature + ]; }; +#else + auto volume = [&ctxt, &vols](size_t layer_idx, int extruder, int feature) -> GLVolume& { + return *vols[ctxt.color_by_color_print() ? + ctxt.color_print_color_idx_by_layer_idx_and_extruder(layer_idx, extruder) : + ctxt.color_by_tool() ? + std::min(ctxt.number_tools() - 1, std::max(extruder - 1, 0)) : + feature + ]; + }; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL if (ctxt.color_by_color_print() || ctxt.color_by_tool()) { - for (size_t i = 0; i < ctxt.number_tools(); ++i) + for (size_t i = 0; i < ctxt.number_tools(); ++i) { vols.emplace_back(new_volume(ctxt.color_tool(i))); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + geometries.emplace_back(GLModel::Geometry()); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + } } - else + else { vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) }; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + geometries = { GLModel::Geometry(), GLModel::Geometry(), GLModel::Geometry() }; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + } + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + assert(vols.size() == geometries.size()); + for (GLModel::Geometry& g : geometries) { + g.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; + } +#else for (GLVolume *vol : vols) // Reserving number of vertices (3x position + 3x color) vol->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { const Layer *layer = ctxt.layers[idx_layer]; - if (is_selected_separate_extruder) - { + if (is_selected_separate_extruder) { bool at_least_one_has_correct_extruder = false; - for (const LayerRegion* layerm : layer->regions()) - { + for (const LayerRegion* layerm : layer->regions()) { if (layerm->slices.surfaces.empty()) continue; const PrintRegionConfig& cfg = layerm->region().config(); @@ -6301,17 +6519,27 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c continue; } - for (GLVolume *vol : vols) +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + for (size_t i = 0; i < vols.size(); ++i) { + GLVolume* vol = vols[i]; + if (vol->print_zs.empty() || vol->print_zs.back() != layer->print_z) { + vol->print_zs.emplace_back(layer->print_z); + vol->offsets.emplace_back(geometries[i].indices_count()); + } + } +#else + for (GLVolume* vol : vols) if (vol->print_zs.empty() || vol->print_zs.back() != layer->print_z) { vol->print_zs.emplace_back(layer->print_z); vol->offsets.emplace_back(vol->indexed_vertex_array.quad_indices.size()); vol->offsets.emplace_back(vol->indexed_vertex_array.triangle_indices.size()); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + for (const PrintInstance &instance : *ctxt.shifted_copies) { const Point © = instance.shift; for (const LayerRegion *layerm : layer->regions()) { - if (is_selected_separate_extruder) - { + if (is_selected_separate_extruder) { const PrintRegionConfig& cfg = layerm->region().config(); if (cfg.perimeter_extruder.value != m_selected_extruder || cfg.infill_extruder.value != m_selected_extruder || @@ -6319,19 +6547,31 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c continue; } if (ctxt.has_perimeters) +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, + select_geometry(idx_layer, layerm->region().config().perimeter_extruder.value, 0)); +#else _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, volume(idx_layer, layerm->region().config().perimeter_extruder.value, 0)); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL if (ctxt.has_infill) { for (const ExtrusionEntity *ee : layerm->fills.entities) { // fill represents infill extrusions of a single island. const auto *fill = dynamic_cast(ee); if (! fill->entities.empty()) +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy, + select_geometry(idx_layer, is_solid_infill(fill->entities.front()->role()) ? + layerm->region().config().solid_infill_extruder : + layerm->region().config().infill_extruder, 1)); +#else _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy, volume(idx_layer, is_solid_infill(fill->entities.front()->role()) ? layerm->region().config().solid_infill_extruder : layerm->region().config().infill_extruder, 1)); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } } } @@ -6339,28 +6579,50 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c const SupportLayer *support_layer = dynamic_cast(layer); if (support_layer) { for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, + select_geometry(idx_layer, (extrusion_entity->role() == erSupportMaterial) ? + support_layer->object()->config().support_material_extruder : + support_layer->object()->config().support_material_interface_extruder, 2)); +#else _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, volume(idx_layer, (extrusion_entity->role() == erSupportMaterial) ? support_layer->object()->config().support_material_extruder : support_layer->object()->config().support_material_interface_extruder, 2)); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } } } // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one. for (size_t i = 0; i < vols.size(); ++i) { GLVolume &vol = *vols[i]; - if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { - vols[i] = new_volume(vol.color); - reserve_new_volume_finalize_old_volume(*vols[i], vol, false); - } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (geometries[i].vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) { + vol.model.init_from(std::move(geometries[i])); +#else + if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + vols[i] = new_volume(vol.color); +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + reserve_new_volume_finalize_old_volume(*vols[i], vol, false); +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + } } } + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + for (size_t i = 0; i < vols.size(); ++i) { + if (!geometries[i].is_empty()) + vols[i]->model.init_from(std::move(geometries[i])); + } +#else for (GLVolume *vol : vols) // Ideally one would call vol->indexed_vertex_array.finalize() here to move the buffers to the OpenGL driver, // but this code runs in parallel and the OpenGL driver is not thread safe. vol->indexed_vertex_array.shrink_to_fit(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL }); BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results" << m_volumes.log_memory_info() << log_memory_info(); @@ -6375,8 +6637,14 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c } for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) { GLVolume* v = m_volumes.volumes[i]; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + v->is_outside = !contains(build_volume, v->model); + // We are done editinig the model, now it can be sent to gpu + v->model.enable_render(); +#else v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box()); v->indexed_vertex_array.finalize_geometry(m_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info(); @@ -6396,10 +6664,10 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con struct Ctxt { - const Print *print; - const std::vector* tool_colors; - Vec2f wipe_tower_pos; - float wipe_tower_angle; + const Print *print; + const std::vector *tool_colors; + Vec2f wipe_tower_pos; + float wipe_tower_angle; static ColorRGBA color_support() { return ColorRGBA::GREENISH(); } @@ -6441,6 +6709,11 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con auto new_volume = [this, &new_volume_mutex](const ColorRGBA& color) { auto *volume = new GLVolume(color); volume->is_extrusion_path = true; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + // to prevent sending data to gpu (in the main thread) while + // editing the model geometry + volume->model.disable_render(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL tbb::spin_mutex::scoped_lock lock; lock.acquire(new_volume_mutex); m_volumes.volumes.emplace_back(volume); @@ -6454,23 +6727,46 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con [&ctxt, &new_volume](const tbb::blocked_range& range) { // Bounding box of this slab of a wipe tower. GLVolumePtrs vols; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + std::vector geometries; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL if (ctxt.color_by_tool()) { - for (size_t i = 0; i < ctxt.number_tools(); ++i) + for (size_t i = 0; i < ctxt.number_tools(); ++i) { vols.emplace_back(new_volume(ctxt.color_tool(i))); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + geometries.emplace_back(GLModel::Geometry()); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + } } - else + else { vols = { new_volume(ctxt.color_support()) }; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + geometries = { GLModel::Geometry() }; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + } + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + assert(vols.size() == geometries.size()); + for (GLModel::Geometry& g : geometries) { + g.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; + } +#else for (GLVolume *volume : vols) // Reserving number of vertices (3x position + 3x color) volume->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) { const std::vector &layer = ctxt.tool_change(idx_layer); for (size_t i = 0; i < vols.size(); ++i) { GLVolume &vol = *vols[i]; if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) { vol.print_zs.emplace_back(layer.front().print_z); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + vol.offsets.emplace_back(geometries[i].indices_count()); +#else vol.offsets.emplace_back(vol.indexed_vertex_array.quad_indices.size()); vol.offsets.emplace_back(vol.indexed_vertex_array.triangle_indices.size()); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } } for (const WipeTower::ToolChangeResult &extrusions : layer) { @@ -6512,21 +6808,42 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con e_prev = e; } + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + _3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z, + geometries[ctxt.volume_idx(e.tool, 0)]); +#else _3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z, *vols[ctxt.volume_idx(e.tool, 0)]); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } } } for (size_t i = 0; i < vols.size(); ++i) { GLVolume &vol = *vols[i]; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (geometries[i].vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) { + vol.model.init_from(std::move(geometries[i])); +#else if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL vols[i] = new_volume(vol.color); +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL reserve_new_volume_finalize_old_volume(*vols[i], vol, false); +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } } + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + for (size_t i = 0; i < vols.size(); ++i) { + if (!geometries[i].is_empty()) + vols[i]->model.init_from(std::move(geometries[i])); + } +#else for (GLVolume *vol : vols) vol->indexed_vertex_array.shrink_to_fit(); - }); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + }); BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results" << m_volumes.log_memory_info() << log_memory_info(); // Remove empty volumes from the newly added volumes. @@ -6540,8 +6857,14 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con } for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) { GLVolume* v = m_volumes.volumes[i]; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + v->is_outside = !contains(build_volume, v->model); + // We are done editinig the model, now it can be sent to gpu + v->model.enable_render(); +#else v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box()); v->indexed_vertex_array.finalize_geometry(m_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info(); @@ -6565,11 +6888,21 @@ void GLCanvas3D::_load_sla_shells() m_volumes.volumes.emplace_back(new GLVolume(color)); GLVolume& v = *m_volumes.volumes.back(); #if ENABLE_SMOOTH_NORMALS +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + v.model.init_from(mesh, true); +#else v.indexed_vertex_array.load_mesh(mesh, true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#else +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + v.model.init_from(mesh); #else v.indexed_vertex_array.load_mesh(mesh); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #endif // ENABLE_SMOOTH_NORMALS +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL v.indexed_vertex_array.finalize_geometry(m_initialized); +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled; v.composite_id.volume_id = volume_id; v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0.0)); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 51bb2b1bc1..48fef56110 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() @@ -511,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 @@ -681,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; diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index e1dc3305c7..8620c7eaf8 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -8,15 +8,56 @@ #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/Polygon.hpp" +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#include "libslic3r/BuildVolume.hpp" +#include "libslic3r/Geometry/ConvexHull.hpp" +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #include #include +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_SMOOTH_NORMALS +#include +#include +#include +#endif // ENABLE_SMOOTH_NORMALS +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + #include namespace Slic3r { namespace GUI { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_SMOOTH_NORMALS +static void smooth_normals_corner(const TriangleMesh& mesh, std::vector& normals) +{ + using MapMatrixXfUnaligned = Eigen::Map>; + using MapMatrixXiUnaligned = Eigen::Map>; + + std::vector face_normals = its_face_normals(mesh.its); + + Eigen::MatrixXd vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), + Eigen::Index(mesh.its.vertices.size()), 3).cast(); + Eigen::MatrixXi indices = MapMatrixXiUnaligned(mesh.its.indices.front().data(), + Eigen::Index(mesh.its.indices.size()), 3); + Eigen::MatrixXd in_normals = MapMatrixXfUnaligned(face_normals.front().data(), + Eigen::Index(face_normals.size()), 3).cast(); + Eigen::MatrixXd out_normals; + + igl::per_corner_normals(vertices, indices, in_normals, 1.0, out_normals); + + normals = std::vector(mesh.its.vertices.size()); + for (size_t i = 0; i < mesh.its.indices.size(); ++i) { + for (size_t j = 0; j < 3; ++j) { + normals[mesh.its.indices[i][j]] = out_normals.row(i * 3 + j).cast(); + } + } +} +#endif // ENABLE_SMOOTH_NORMALS +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + #if ENABLE_GLBEGIN_GLEND_REMOVAL void GLModel::Geometry::reserve_vertices(size_t vertices_count) { @@ -207,6 +248,37 @@ Vec2f GLModel::Geometry::extract_tex_coord_2(size_t id) const return { *(start + 0), *(start + 1) }; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void GLModel::Geometry::set_vertex(size_t id, const Vec3f& position, const Vec3f& normal) +{ + assert(format.vertex_layout == EVertexLayout::P3N3); + assert(id < vertices_count()); + if (id < vertices_count()) { + float* start = &vertices[id * vertex_stride_floats(format)]; + *(start + 0) = position.x(); + *(start + 1) = position.y(); + *(start + 2) = position.z(); + *(start + 3) = normal.x(); + *(start + 4) = normal.y(); + *(start + 5) = normal.z(); + } +} + +void GLModel::Geometry::set_ushort_index(size_t id, unsigned short index) +{ + assert(id < indices_count()); + if (id < indices_count()) + ::memcpy(indices.data() + id * sizeof(unsigned short), &index, sizeof(unsigned short)); +} + +void GLModel::Geometry::set_uint_index(size_t id, unsigned int index) +{ + assert(id < indices_count()); + if (id < indices_count()) + ::memcpy(indices.data() + id * sizeof(unsigned int), &index, sizeof(unsigned int)); +} +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + unsigned int GLModel::Geometry::extract_uint_index(size_t id) const { if (format.index_type != EIndexType::UINT) { @@ -219,7 +291,7 @@ unsigned int GLModel::Geometry::extract_uint_index(size_t id) const return -1; } - unsigned int ret = -1; + unsigned int ret = (unsigned int)-1; ::memcpy(&ret, indices.data() + id * index_stride_bytes(format), sizeof(unsigned int)); return ret; } @@ -236,11 +308,23 @@ unsigned short GLModel::Geometry::extract_ushort_index(size_t id) const return -1; } - unsigned short ret = -1; + unsigned short ret = (unsigned short)-1; ::memcpy(&ret, indices.data() + id * index_stride_bytes(format), sizeof(unsigned short)); return ret; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void GLModel::Geometry::remove_vertex(size_t id) +{ + assert(id < vertices_count()); + if (id < vertices_count()) { + size_t stride = vertex_stride_floats(format); + std::vector::iterator it = vertices.begin() + id * stride; + vertices.erase(it, it + stride); + } +} +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + size_t GLModel::Geometry::vertex_stride_floats(const Format& format) { switch (format.vertex_layout) @@ -328,6 +412,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) @@ -456,10 +545,58 @@ void GLModel::init_from(const Geometry& data) } #if ENABLE_GLBEGIN_GLEND_REMOVAL +#if ENABLE_SMOOTH_NORMALS +void GLModel::init_from(const TriangleMesh& mesh, bool smooth_normals) +{ + if (smooth_normals) { + if (is_initialized()) { + // call reset() if you want to reuse this model + assert(false); + return; + } + + if (mesh.its.vertices.empty() || mesh.its.indices.empty()) { + assert(false); + return; + } + + std::vector normals; + smooth_normals_corner(mesh, normals); + + const indexed_triangle_set& its = mesh.its; + Geometry& data = m_render_data.geometry; + data.format = { Geometry::EPrimitiveType::Triangles, Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(3 * its.indices.size()) }; + data.reserve_vertices(3 * its.indices.size()); + data.reserve_indices(3 * its.indices.size()); + + // vertices + for (size_t i = 0; i < its.vertices.size(); ++i) { + data.add_vertex(its.vertices[i], normals[i]); + } + + // indices + for (size_t i = 0; i < its.indices.size(); ++i) { + const stl_triangle_vertex_indices& idx = its.indices[i]; + if (data.format.index_type == GLModel::Geometry::EIndexType::USHORT) + data.add_ushort_triangle((unsigned short)idx(0), (unsigned short)idx(1), (unsigned short)idx(2)); + else + data.add_uint_triangle((unsigned int)idx(0), (unsigned int)idx(1), (unsigned int)idx(2)); + } + + // update bounding box + for (size_t i = 0; i < vertices_count(); ++i) { + m_bounding_box.merge(m_render_data.geometry.extract_position_3(i).cast()); + } + } + else + init_from(mesh.its); +} +#else void GLModel::init_from(const TriangleMesh& mesh) { init_from(mesh.its); } +#endif // ENABLE_SMOOTH_NORMALS void GLModel::init_from(const indexed_triangle_set& its) #else @@ -479,21 +616,24 @@ 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.format = { Geometry::EPrimitiveType::Triangles, Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(3 * its.indices.size()) }; data.reserve_vertices(3 * its.indices.size()); data.reserve_indices(3 * its.indices.size()); // vertices + indices unsigned int vertices_counter = 0; for (uint32_t i = 0; i < its.indices.size(); ++i) { - stl_triangle_vertex_indices face = its.indices[i]; - stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] }; - stl_vertex n = face_normal_normalized(vertex); + const stl_triangle_vertex_indices face = its.indices[i]; + const stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] }; + const stl_vertex n = face_normal_normalized(vertex); for (size_t j = 0; j < 3; ++j) { data.add_vertex(vertex[j], n); } vertices_counter += 3; - data.add_uint_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); + if (data.format.index_type == GLModel::Geometry::EIndexType::USHORT) + data.add_ushort_triangle((unsigned short)vertices_counter - 3, (unsigned short)vertices_counter - 2, (unsigned short)vertices_counter - 1); + else + data.add_uint_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); } // update bounding box @@ -716,6 +856,9 @@ void GLModel::render() void GLModel::render() const #endif // ENABLE_GLBEGIN_GLEND_REMOVAL { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + render(std::make_pair(0, indices_count())); +#else GLShaderProgram* shader = wxGetApp().get_current_shader(); #if ENABLE_GLBEGIN_GLEND_REMOVAL @@ -804,8 +947,71 @@ void GLModel::render() const glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } #endif // ENABLE_GLBEGIN_GLEND_REMOVAL +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void GLModel::render(const std::pair& range) +{ + if (m_render_disabled) + return; + + if (range.second == range.first) + return; + + GLShaderProgram* shader = wxGetApp().get_current_shader(); + + if (shader == nullptr) + return; + + // sends data to gpu if not done yet + if (m_render_data.vbo_id == 0 || m_render_data.ibo_id == 0) { + if (m_render_data.geometry.vertices_count() > 0 && m_render_data.geometry.indices_count() > 0 && !send_to_gpu()) + return; + } + + const Geometry& data = m_render_data.geometry; + + 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); + const bool normal = Geometry::has_normal(data.format); + const bool tex_coord = Geometry::has_tex_coord(data.format); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_render_data.vbo_id)); + + if (position) { + glsafe(::glVertexPointer(Geometry::position_stride_floats(data.format), GL_FLOAT, vertex_stride_bytes, (const void*)Geometry::position_offset_bytes(data.format))); + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + } + if (normal) { + glsafe(::glNormalPointer(GL_FLOAT, vertex_stride_bytes, (const void*)Geometry::normal_offset_bytes(data.format))); + glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + } + if (tex_coord) { + glsafe(::glTexCoordPointer(Geometry::tex_coord_stride_floats(data.format), GL_FLOAT, vertex_stride_bytes, (const void*)Geometry::tex_coord_offset_bytes(data.format))); + glsafe(::glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + } + + shader->set_uniform("uniform_color", data.color); + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_render_data.ibo_id)); + glsafe(::glDrawElements(mode, range.second - range.first + 1, index_type, (const void*)(range.first * Geometry::index_stride_bytes(data.format)))); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + if (tex_coord) + glsafe(::glDisableClientState(GL_TEXTURE_COORD_ARRAY)); + if (normal) + glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); + if (position) + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); +} +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + #if ENABLE_GLBEGIN_GLEND_REMOVAL void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count) #else @@ -1022,6 +1228,62 @@ static void append_triangle(GLModel::Geometry& data, unsigned short v1, unsigned } #endif // ENABLE_GLBEGIN_GLEND_REMOVAL +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +template +inline bool all_vertices_inside(const GLModel::Geometry& geometry, Fn fn) +{ + const size_t position_stride_floats = geometry.position_stride_floats(geometry.format); + const size_t position_offset_floats = geometry.position_offset_floats(geometry.format); + assert(position_stride_floats == 3); + if (geometry.vertices.empty() || position_stride_floats != 3) + return false; + + for (auto it = geometry.vertices.begin(); it != geometry.vertices.end(); ) { + it += position_offset_floats; + if (!fn({ *it, *(it + 1), *(it + 2) })) + return false; + it += (geometry.vertex_stride_floats(geometry.format) - position_offset_floats - position_stride_floats); + } + return true; +} + +bool contains(const BuildVolume& volume, const GLModel& model, bool ignore_bottom) +{ + static constexpr const double epsilon = BuildVolume::BedEpsilon; + switch (volume.type()) { + case BuildVolume::Type::Rectangle: + { + BoundingBox3Base build_volume = volume.bounding_volume().inflated(epsilon); + if (volume.max_print_height() == 0.0) + build_volume.max.z() = std::numeric_limits::max(); + if (ignore_bottom) + build_volume.min.z() = -std::numeric_limits::max(); + const BoundingBoxf3& model_box = model.get_bounding_box(); + return build_volume.contains(model_box.min) && build_volume.contains(model_box.max); + } + case BuildVolume::Type::Circle: + { + const Geometry::Circled& circle = volume.circle(); + const Vec2f c = unscaled(circle.center); + const float r = unscaled(circle.radius) + float(epsilon); + const float r2 = sqr(r); + return volume.max_print_height() == 0.0 ? + all_vertices_inside(model.get_geometry(), [c, r2](const Vec3f& p) { return (to_2d(p) - c).squaredNorm() <= r2; }) : + + all_vertices_inside(model.get_geometry(), [c, r2, z = volume.max_print_height() + epsilon](const Vec3f& p) { return (to_2d(p) - c).squaredNorm() <= r2 && p.z() <= z; }); + } + case BuildVolume::Type::Convex: + //FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently. + case BuildVolume::Type::Custom: + return volume.max_print_height() == 0.0 ? + all_vertices_inside(model.get_geometry(), [&volume](const Vec3f& p) { return Geometry::inside_convex_polygon(volume.top_bottom_convex_hull_decomposition_bed(), to_2d(p).cast()); }) : + all_vertices_inside(model.get_geometry(), [&volume, z = volume.max_print_height() + epsilon](const Vec3f& p) { return Geometry::inside_convex_polygon(volume.top_bottom_convex_hull_decomposition_bed(), to_2d(p).cast()) && p.z() <= z; }); + default: + return true; + } +} +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLModel::Geometry stilized_arrow(unsigned short resolution, float tip_radius, float tip_height, float stem_radius, float stem_height) { #if !ENABLE_GLBEGIN_GLEND_REMOVAL diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 70220a75cb..3b268dab7e 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -14,6 +14,9 @@ namespace Slic3r { class TriangleMesh; class Polygon; using Polygons = std::vector; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +class BuildVolume; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL namespace GUI { @@ -83,11 +86,18 @@ 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 + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + void set_vertex(size_t id, const Vec3f& position, const Vec3f& normal); // EVertexLayout::P3N3 + + void set_ushort_index(size_t id, unsigned short index); + void set_uint_index(size_t id, unsigned int index); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL void add_ushort_index(unsigned short id); void add_uint_index(unsigned int id); @@ -106,7 +116,11 @@ 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(); } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + void remove_vertex(size_t id); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + + bool is_empty() const { return vertices_count() == 0 || indices_count() == 0; } size_t vertices_count() const { return vertices.size() / vertex_stride_floats(format); } size_t indices_count() const { return indices.size() / index_stride_bytes(format); } @@ -135,6 +149,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); @@ -177,6 +193,16 @@ namespace GUI { std::vector m_render_data; #endif // ENABLE_GLBEGIN_GLEND_REMOVAL +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + // By default the vertex and index buffers data are sent to gpu at the first call to render() method. + // If you need to initialize a model from outside the main thread, so that a call to render() may happen + // before the initialization is complete, use the methods: + // disable_render() + // ... do your initialization ... + // enable_render() + // to keep the data on cpu side until needed. + bool m_render_disabled{ false }; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL BoundingBoxf3 m_bounding_box; std::string m_filename; @@ -195,8 +221,16 @@ namespace GUI { size_t indices_size_bytes() const { return indices_count() * Geometry::index_stride_bytes(m_render_data.geometry.format); } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + const Geometry& get_geometry() const { return m_render_data.geometry; } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + void init_from(Geometry&& data); +#if ENABLE_SMOOTH_NORMALS + void init_from(const TriangleMesh& mesh, bool smooth_normals = false); +#else void init_from(const TriangleMesh& mesh); +#endif // ENABLE_SMOOTH_NORMALS #else void init_from(const Geometry& data); void init_from(const indexed_triangle_set& its, const BoundingBoxf3& bbox); @@ -217,9 +251,15 @@ namespace GUI { void reset(); #if ENABLE_GLBEGIN_GLEND_REMOVAL void render(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + void render(const std::pair& range); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL void render_instanced(unsigned int instances_vbo, unsigned int instances_count); bool is_initialized() const { return vertices_count() > 0 && indices_count() > 0; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + bool is_empty() const { return m_render_data.geometry.is_empty(); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #else void render() const; void render_instanced(unsigned int instances_vbo, unsigned int instances_count) const; @@ -230,6 +270,29 @@ namespace GUI { const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } const std::string& get_filename() const { return m_filename; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + bool is_render_disabled() const { return m_render_disabled; } + void enable_render() { m_render_disabled = false; } + void disable_render() { m_render_disabled = true; } + + size_t cpu_memory_used() const { + size_t ret = 0; + if (!m_render_data.geometry.vertices.empty()) + ret += vertices_size_bytes(); + if (!m_render_data.geometry.indices.empty()) + ret += indices_size_bytes(); + return ret; + } + size_t gpu_memory_used() const { + size_t ret = 0; + if (m_render_data.geometry.vertices.empty()) + ret += vertices_size_bytes(); + if (m_render_data.geometry.indices.empty()) + ret += indices_size_bytes(); + return ret; + } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + private: #if ENABLE_GLBEGIN_GLEND_REMOVAL bool send_to_gpu(); @@ -238,6 +301,10 @@ namespace GUI { #endif // ENABLE_GLBEGIN_GLEND_REMOVAL }; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + bool contains(const BuildVolume& volume, const GLModel& model, bool ignore_bottom = true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + // create an arrow with cylindrical stem and conical tip, with the given dimensions and resolution // the origin of the arrow is in the center of the stem cap // the arrow has its axis of symmetry along the Z axis and is pointing upward diff --git a/src/slic3r/GUI/GLSelectionRectangle.cpp b/src/slic3r/GUI/GLSelectionRectangle.cpp index 515da6de3a..8cf3247cb7 100644 --- a/src/slic3r/GUI/GLSelectionRectangle.cpp +++ b/src/slic3r/GUI/GLSelectionRectangle.cpp @@ -98,7 +98,7 @@ namespace GUI { color[1] = (m_state == Select) ? 1.0f : 0.3f; color[2] = 0.3f; glsafe(::glColor3fv(color)); -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL +#endif // !ENABLE_GLBEGIN_GLEND_REMOVAL glsafe(::glDisable(GL_DEPTH_TEST)); 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. diff --git a/src/slic3r/GUI/GalleryDialog.cpp b/src/slic3r/GUI/GalleryDialog.cpp index 1191e5c2e6..0bc741c962 100644 --- a/src/slic3r/GUI/GalleryDialog.cpp +++ b/src/slic3r/GUI/GalleryDialog.cpp @@ -274,9 +274,13 @@ static void generate_thumbnail_from_model(const std::string& filename) GLVolumeCollection volumes; volumes.volumes.push_back(new GLVolume()); - GLVolume* volume = volumes.volumes[0]; + GLVolume* volume = volumes.volumes.back(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume->model.init_from(model.mesh()); +#else volume->indexed_vertex_array.load_mesh(model.mesh()); volume->indexed_vertex_array.finalize_geometry(true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL volume->set_instance_transformation(model.objects[0]->instances[0]->get_transformation()); volume->set_volume_transformation(model.objects[0]->volumes[0]->get_transformation()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 219a322f78..e0a583f0b3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -395,14 +395,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 4f806b711f..d3fd294b3e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -1254,8 +1254,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 @@ -1264,7 +1263,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/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 189b62061e..c97a4a788e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -172,15 +172,14 @@ void GLGizmoRotate::on_render() if (shader != nullptr) { shader->start_using(); - const float radius = Offset + m_parent.get_selection().get_bounding_box().radius(); - const bool radius_changed = std::abs(m_old_radius - radius) > EPSILON; - m_old_radius = radius; + const bool radius_changed = std::abs(m_old_radius - m_radius) > EPSILON; + m_old_radius = m_radius; ColorRGBA color((m_hover_id != -1) ? m_drag_color : m_highlight_color); render_circle(color, radius_changed); if (m_hover_id != -1) { - const bool hover_radius_changed = std::abs(m_old_hover_radius - radius) > EPSILON; - m_old_hover_radius = radius; + const bool hover_radius_changed = std::abs(m_old_hover_radius - m_radius) > EPSILON; + m_old_hover_radius = m_radius; render_scale(color, hover_radius_changed); render_snap_radii(color, hover_radius_changed); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index a87a51444a..3a503407ce 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -129,6 +129,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(); @@ -136,6 +144,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(); @@ -179,11 +188,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. @@ -238,9 +248,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; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index e70c1111b9..4b75befb6c 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1444,8 +1444,8 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); + const int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); + const int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); if (fb_width == 0 || fb_height == 0) return; draw_data->ScaleClipRects(io.DisplayFramebufferScale); @@ -1488,8 +1488,7 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) // Render command lists ImVec2 pos = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) - { + for (int n = 0; n < draw_data->CmdListsCount; ++n) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; @@ -1497,19 +1496,14 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) glsafe(::glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv)))); glsafe(::glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col)))); - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; ++cmd_i) { const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; if (pcmd->UserCallback) - { // User callback (registered via ImDrawList::AddCallback) pcmd->UserCallback(cmd_list, pcmd); - } - else - { + else { ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y); - if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) - { + if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) { // Apply scissor/clipping rectangle glsafe(::glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y))); diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp index 1bb8cdf6c6..96702d158f 100644 --- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -1,5 +1,6 @@ #include "SLAImportJob.hpp" +#include "libslic3r/SLAPrint.hpp" #include "libslic3r/Format/SL1.hpp" #include "slic3r/GUI/GUI.hpp" 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); 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" diff --git a/src/slic3r/GUI/Notebook.hpp b/src/slic3r/GUI/Notebook.hpp index ff5020b9c4..af03a6a080 100644 --- a/src/slic3r/GUI/Notebook.hpp +++ b/src/slic3r/GUI/Notebook.hpp @@ -245,7 +245,7 @@ public: GetBtnsListCtrl()->Rescale(); } - void Notebook::OnNavigationKey(wxNavigationKeyEvent& event) + void OnNavigationKey(wxNavigationKeyEvent& event) { if (event.IsWindowChange()) { // change pages diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 6616cc20d7..41a11cff6e 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -233,8 +233,9 @@ OpenGLManager::~OpenGLManager() bool OpenGLManager::init_gl() { if (!m_gl_initialized) { - if (glewInit() != GLEW_OK) { - BOOST_LOG_TRIVIAL(error) << "Unable to init glew library"; + GLenum err = glewInit(); + if (err != GLEW_OK) { + BOOST_LOG_TRIVIAL(error) << "Unable to init glew library: " << glewGetErrorString(err); return false; } m_gl_initialized = true; diff --git a/tests/libnest2d/CMakeLists.txt b/tests/libnest2d/CMakeLists.txt index bcb7594520..9bafe84a04 100644 --- a/tests/libnest2d/CMakeLists.txt +++ b/tests/libnest2d/CMakeLists.txt @@ -4,4 +4,6 @@ target_link_libraries(${_TEST_NAME}_tests test_common libnest2d ) set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") # catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") -add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "${CATCH_EXTRA_ARGS} exclude:[NotWorking]") +set(_catch_args "exclude:[NotWorking]") +list(APPEND _catch_args "${CATCH_EXTRA_ARGS}") +add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${_catch_args}) diff --git a/tests/sla_print/CMakeLists.txt b/tests/sla_print/CMakeLists.txt index dc583f1a1c..88c1cd1866 100644 --- a/tests/sla_print/CMakeLists.txt +++ b/tests/sla_print/CMakeLists.txt @@ -3,9 +3,14 @@ add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp sla_print_tests.cpp sla_test_utils.hpp sla_test_utils.cpp sla_supptgen_tests.cpp - sla_raycast_tests.cpp) + sla_raycast_tests.cpp + sla_archive_export_tests.cpp) target_link_libraries(${_TEST_NAME}_tests test_common libslic3r) set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") +if (WIN32) + prusaslicer_copy_dlls(${_TEST_NAME}_tests) +endif() + # catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${CATCH_EXTRA_ARGS}) diff --git a/tests/sla_print/sla_archive_export_tests.cpp b/tests/sla_print/sla_archive_export_tests.cpp new file mode 100644 index 0000000000..9dbe5bc64f --- /dev/null +++ b/tests/sla_print/sla_archive_export_tests.cpp @@ -0,0 +1,40 @@ +#include +#include + +#include "libslic3r/SLAPrint.hpp" +#include "libslic3r/Format/SLAArchive.hpp" + +#include + +using namespace Slic3r; + +TEST_CASE("Archive export test", "[sla_archives]") { + constexpr const char *PNAME = "20mm_cube"; + + for (auto &archname : SLAArchive::registered_archives()) { + INFO(std::string("Testing archive type: ") + archname); + SLAPrint print; + SLAFullPrintConfig fullcfg; + + auto m = Model::read_from_file(TEST_DATA_DIR PATH_SEPARATOR + std::string(PNAME) + ".obj", nullptr); + + fullcfg.set("sla_archive_format", archname); + fullcfg.set("supports_enable", false); + fullcfg.set("pad_enable", false); + + DynamicPrintConfig cfg; + cfg.apply(fullcfg); + + print.set_status_callback([](const PrintBase::SlicingStatus&) {}); + print.apply(m, cfg); + print.process(); + + ThumbnailsList thumbnails; + auto outputfname = std::string("output.") + SLAArchive::get_extension(archname); + + print.export_print(outputfname, thumbnails, PNAME); + + // Not much can be checked about the archives... + REQUIRE(boost::filesystem::exists(outputfname)); + } +} diff --git a/tests/slic3rutils/CMakeLists.txt b/tests/slic3rutils/CMakeLists.txt index be1b645d70..256e6efd68 100644 --- a/tests/slic3rutils/CMakeLists.txt +++ b/tests/slic3rutils/CMakeLists.txt @@ -15,4 +15,6 @@ if (WIN32) endif() # catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") -add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "${CATCH_EXTRA_ARGS} exclude:[NotWorking]") +set(_catch_args "exclude:[NotWorking]") +list(APPEND _catch_args "${CATCH_EXTRA_ARGS}") +add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${_catch_args})