Merge branch 'master' into fs_mouse

This commit is contained in:
Filip Sykala 2022-02-23 14:43:42 +01:00
commit 0d48cf5ab9
50 changed files with 2772 additions and 289 deletions

View File

@ -506,6 +506,12 @@ endif ()
# Find the Cereal serialization library # Find the Cereal serialization library
find_package(cereal REQUIRED) 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 # l10n
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization") set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization")

View File

@ -12,8 +12,8 @@ endif()
prusaslicer_add_cmake_project(wxWidgets prusaslicer_add_cmake_project(wxWidgets
# GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" # GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets"
# GIT_TAG tm_cross_compile #${_wx_git_tag} # GIT_TAG tm_cross_compile #${_wx_git_tag}
URL https://github.com/prusa3d/wxWidgets/archive/73f029adfcc82fb3aa4b01220a013f716e57d110.zip URL https://github.com/prusa3d/wxWidgets/archive/489f6118256853cf5b299d595868641938566cdb.zip
URL_HASH SHA256=c35fe0187db497b6a3f477e24ed5e307028657ff0c2554385810b6e7961ad2e4 URL_HASH SHA256=5b22d465377cedd8044bba69bea958b248953fd3628c1de4913a84d4e6f6175b
DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG
CMAKE_ARGS CMAKE_ARGS
-DwxBUILD_PRECOMP=ON -DwxBUILD_PRECOMP=ON

View File

@ -64,6 +64,13 @@ technology = FFF
family = PREDATOR family = PREDATOR
default_materials = Generic PLA @PREDATOR; Generic PETG @PREDATOR; Generic ABS @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 # All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface. # 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 ########## ########## 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -126,7 +126,7 @@ if (NOT WIN32 AND NOT APPLE)
set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
endif () endif ()
target_link_libraries(PrusaSlicer libslic3r cereal) target_link_libraries(PrusaSlicer libslic3r libcereal)
if (APPLE) if (APPLE)
# add_compile_options(-stdlib=libc++) # add_compile_options(-stdlib=libc++)
# add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE) # add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE)

View File

@ -94,6 +94,12 @@ public:
// Called on initial G-code preview on OpenGL vertex buffer interleaved normals and vertices. // 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<float>& paths, const Eigen::AlignedBox<float, 3>& bbox, bool ignore_bottom = true) const; bool all_paths_inside_vertices_and_normals_interleaved(const std::vector<float>& paths, const Eigen::AlignedBox<float, 3>& bbox, bool ignore_bottom = true) const;
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
const std::pair<std::vector<Vec2d>, std::vector<Vec2d>>& top_bottom_convex_hull_decomposition_scene() const { return m_top_bottom_convex_hull_decomposition_scene; }
const std::pair<std::vector<Vec2d>, std::vector<Vec2d>>& top_bottom_convex_hull_decomposition_bed() const { return m_top_bottom_convex_hull_decomposition_bed; }
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
private: private:
// Source definition of the print bed geometry (PrintConfig::bed_shape) // Source definition of the print bed geometry (PrintConfig::bed_shape)
std::vector<Vec2d> m_bed_shape; std::vector<Vec2d> m_bed_shape;

View File

@ -94,10 +94,14 @@ set(SLIC3R_SOURCES
Format/objparser.hpp Format/objparser.hpp
Format/STL.cpp Format/STL.cpp
Format/STL.hpp Format/STL.hpp
Format/SLAArchive.hpp
Format/SLAArchive.cpp
Format/SL1.hpp Format/SL1.hpp
Format/SL1.cpp Format/SL1.cpp
Format/SL1_SVG.hpp Format/SL1_SVG.hpp
Format/SL1_SVG.cpp Format/SL1_SVG.cpp
Format/pwmx.hpp
Format/pwmx.cpp
GCode/ThumbnailData.cpp GCode/ThumbnailData.cpp
GCode/ThumbnailData.hpp GCode/ThumbnailData.hpp
GCode/Thumbnails.cpp GCode/Thumbnails.cpp
@ -354,7 +358,7 @@ find_package(JPEG REQUIRED)
target_link_libraries(libslic3r target_link_libraries(libslic3r
libnest2d libnest2d
admesh admesh
cereal libcereal
libigl libigl
miniz miniz
boost_libs boost_libs

View File

@ -5,6 +5,7 @@
#include <utility> #include <utility>
#include <cstddef> #include <cstddef>
#include <iterator> #include <iterator>
#include <algorithm>
#include "libslic3r/libslic3r.h" #include "libslic3r/libslic3r.h"
@ -44,7 +45,8 @@ size_t max_concurrency(const EP &ep)
template<class EP, class It, class Fn, class = ExecutionPolicyOnly<EP>> template<class EP, class It, class Fn, class = ExecutionPolicyOnly<EP>>
void for_each(const EP &ep, It from, It to, Fn &&fn, size_t granularity = 1) void for_each(const EP &ep, It from, It to, Fn &&fn, size_t granularity = 1)
{ {
AsTraits<EP>::for_each(ep, from, to, std::forward<Fn>(fn), granularity); AsTraits<EP>::for_each(ep, from, to, std::forward<Fn>(fn),
std::max(granularity, size_t(1)));
} }
// A reduce operation with the execution policy passed as argument. // A reduce operation with the execution policy passed as argument.
@ -68,7 +70,7 @@ T reduce(const EP & ep,
return AsTraits<EP>::reduce(ep, from, to, init, return AsTraits<EP>::reduce(ep, from, to, init,
std::forward<MergeFn>(mergefn), std::forward<MergeFn>(mergefn),
std::forward<AccessFn>(accessfn), std::forward<AccessFn>(accessfn),
granularity); std::max(granularity, size_t(1)));
} }
// An overload of reduce method to be used with iterators as 'from' and 'to' // 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( return reduce(
ep, from, to, init, std::forward<MergeFn>(mergefn), ep, from, to, init, std::forward<MergeFn>(mergefn),
[](const auto &i) { return i; }, granularity); [](const auto &i) { return i; }, std::max(granularity, size_t(1)));
} }
template<class EP, template<class EP,
@ -103,7 +105,8 @@ T accumulate(const EP & ep,
size_t granularity = 1) size_t granularity = 1)
{ {
return reduce(ep, from, to, init, std::plus<T>{}, return reduce(ep, from, to, init, std::plus<T>{},
std::forward<AccessFn>(accessfn), granularity); std::forward<AccessFn>(accessfn),
std::max(granularity, size_t(1)));
} }
@ -119,7 +122,7 @@ T accumulate(const EP &ep,
{ {
return reduce( return reduce(
ep, from, to, init, std::plus<T>{}, [](const auto &i) { return i; }, ep, from, to, init, std::plus<T>{}, [](const auto &i) { return i; },
granularity); std::max(granularity, size_t(1)));
} }
} // namespace execution_policy } // namespace execution_policy

View File

@ -20,11 +20,14 @@
#include "libslic3r/miniz_extension.hpp" #include "libslic3r/miniz_extension.hpp"
#include "libslic3r/PNGReadWrite.hpp" #include "libslic3r/PNGReadWrite.hpp"
#include "libslic3r/LocalesUtils.hpp" #include "libslic3r/LocalesUtils.hpp"
#include "libslic3r/GCode/ThumbnailData.hpp"
#include <boost/property_tree/ini_parser.hpp> #include <boost/property_tree/ini_parser.hpp>
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <miniz.h>
namespace marchsq { namespace marchsq {
template<> struct _RasterTraits<Slic3r::png::ImageGreyscale> { template<> struct _RasterTraits<Slic3r::png::ImageGreyscale> {
@ -482,10 +485,31 @@ sla::RasterEncoder SL1Archive::get_encoder() const
return sla::PNGRasterEncoder{}; return sla::PNGRasterEncoder{};
} }
void SL1Archive::export_print(Zipper& zipper, static void write_thumbnail(Zipper &zipper, const ThumbnailData &data)
const SLAPrint &print,
const std::string &prjname)
{ {
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<const std::uint8_t *>(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 = std::string project =
prjname.empty() ? prjname.empty() ?
boost::filesystem::path(zipper.get_filename()).stem().string() : 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()); 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) { } catch(std::exception& e) {
BOOST_LOG_TRIVIAL(error) << e.what(); BOOST_LOG_TRIVIAL(error) << e.what();
// Rethrow the exception // Rethrow the exception

View File

@ -3,8 +3,12 @@
#include <string> #include <string>
#include "SLAArchive.hpp"
#include "libslic3r/Zipper.hpp" #include "libslic3r/Zipper.hpp"
#include "libslic3r/SLAPrint.hpp" #include "libslic3r/PrintConfig.hpp"
struct indexed_triangle_set;
namespace Slic3r { namespace Slic3r {
@ -23,8 +27,11 @@ public:
SL1Archive() = default; SL1Archive() = default;
explicit SL1Archive(const SLAPrinterConfig &cfg): m_cfg(cfg) {} explicit SL1Archive(const SLAPrinterConfig &cfg): m_cfg(cfg) {}
explicit SL1Archive(SLAPrinterConfig &&cfg): m_cfg(std::move(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); ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out);

View File

@ -2,10 +2,13 @@
#include "SLA/RasterBase.hpp" #include "SLA/RasterBase.hpp"
#include "libslic3r/LocalesUtils.hpp" #include "libslic3r/LocalesUtils.hpp"
#include "libslic3r/ClipperUtils.hpp" #include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/BoundingBox.hpp"
#include <limits> #include <limits>
#include <cstdint> #include <cstdint>
#include <algorithm> #include <algorithm>
#include <string_view>
using namespace std::literals;
namespace Slic3r { namespace Slic3r {
@ -77,20 +80,23 @@ void append_svg(std::string &buf, const Polygon &poly)
char intbuf[coord_t_bufsize]; char intbuf[coord_t_bufsize];
buf += std::string("<path d=\"M ") + decimal_from(c.x(), intbuf); buf += "<path d=\"M "sv;
buf += std::string(" ") + decimal_from(c.y(), intbuf) + " m"; buf += decimal_from(c.x(), intbuf);
buf += " "sv;
buf += decimal_from(c.y(), intbuf);
buf += " m"sv;
for (auto &p : poly) { for (auto &p : poly) {
auto d = p - c; auto d = p - c;
if (d.squaredNorm() == 0) continue; if (d.squaredNorm() == 0) continue;
buf += " "; buf += " "sv;
buf += decimal_from(p.x() - c.x(), intbuf); buf += decimal_from(p.x() - c.x(), intbuf);
buf += " "; buf += " "sv;
buf += decimal_from(p.y() - c.y(), intbuf); buf += decimal_from(p.y() - c.y(), intbuf);
c = p; c = p;
} }
buf += " z\""; // mark path as closed buf += " z\""sv; // mark path as closed
buf += " />\n"; buf += " />\n"sv;
} }
} // namespace } // namespace
@ -167,12 +173,12 @@ public:
sla::EncodedRaster encode(sla::RasterEncoder /*encoder*/) const override sla::EncodedRaster encode(sla::RasterEncoder /*encoder*/) const override
{ {
std::vector<uint8_t> data; std::vector<uint8_t> data;
constexpr const char finish[] = "</svg>\n"; constexpr auto finish = "</svg>\n"sv;
data.reserve(m_svg.size() + std::size(finish)); data.reserve(m_svg.size() + std::size(finish));
std::copy(m_svg.begin(), m_svg.end(), std::back_inserter(data)); 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"}; return sla::EncodedRaster{std::move(data), "svg"};
} }

View File

@ -0,0 +1,74 @@
#include "SLAArchive.hpp"
#include "SL1.hpp"
#include "SL1_SVG.hpp"
#include "pwmx.hpp"
#include "libslic3r/libslic3r.h"
#include <string>
#include <map>
#include <memory>
#include <tuple>
namespace Slic3r {
using ArchiveFactory = std::function<std::unique_ptr<SLAArchive>(const SLAPrinterConfig&)>;
struct ArchiveEntry {
const char *ext;
ArchiveFactory factoryfn;
};
static const std::map<std::string, ArchiveEntry> REGISTERED_ARCHIVES {
{
"SL1",
{ "sl1", [] (const auto &cfg) { return std::make_unique<SL1Archive>(cfg); } }
},
{
"SL2",
{ "sl2", [] (const auto &cfg) { return std::make_unique<SL1_SVGArchive>(cfg); } }
},
{
"pwmx",
{ "pwmx", [] (const auto &cfg) { return std::make_unique<PwmxArchive>(cfg); } }
}
};
std::unique_ptr<SLAArchive>
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<const char*>& SLAArchive::registered_archives()
{
static std::vector<const char*> 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

View File

@ -0,0 +1,64 @@
#ifndef SLAARCHIVE_HPP
#define SLAARCHIVE_HPP
#include <vector>
#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<sla::EncodedRaster> m_layers;
virtual std::unique_ptr<sla::RasterBase> 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<class Fn, class CancelFn, class EP = ExecutionTBB>
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<SLAArchive> create(const std::string &archtype, const SLAPrinterConfig&);
// Get the names of currently known archiver implementations
static const std::vector<const char *> & 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

View File

@ -0,0 +1,564 @@
#include "pwmx.hpp"
#include "GCode/ThumbnailData.hpp"
#include "SLA/RasterBase.hpp"
#include "libslic3r/SLAPrint.hpp"
#include <sstream>
#include <iostream>
#include <fstream>
#include <boost/algorithm/string/replace.hpp>
#include <boost/log/trivial.hpp>
#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<uint8_t> 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<const std::uint8_t *>(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<std::string, std::string>;
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<class T> 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<sla::RasterBase> PwmxArchive::create_raster() const
{
sla::Resolution res;
sla::PixelDim pxdim;
std::array<bool, 2> 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<uint8_t> 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<const char*>(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<const char*>(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

View File

@ -0,0 +1,37 @@
#ifndef _SLIC3R_FORMAT_PWMX_HPP_
#define _SLIC3R_FORMAT_PWMX_HPP_
#include <string>
#include "SLAArchive.hpp"
#include "libslic3r/PrintConfig.hpp"
namespace Slic3r {
class PwmxArchive: public SLAArchive {
SLAPrinterConfig m_cfg;
protected:
std::unique_ptr<sla::RasterBase> 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_

View File

@ -274,7 +274,6 @@ namespace Slic3r {
// Otherwise, leave control to the user completely. // Otherwise, leave control to the user completely.
std::string toolchange_gcode_str; std::string toolchange_gcode_str;
const std::string& toolchange_gcode = gcodegen.config().toolchange_gcode.value; 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()) { if (! toolchange_gcode.empty()) {
DynamicConfig config; DynamicConfig config;
int previous_extruder_id = gcodegen.writer().extruder() ? (int)gcodegen.writer().extruder()->id() : -1; 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_num", new ConfigOptionInt(gcodegen.m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z)); config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z));
config.set_key_value("toolchange_z", new ConfigOptionFloat(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); toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config);
check_add_eol(toolchange_gcode_str); check_add_eol(toolchange_gcode_str);
} }
@ -305,6 +304,9 @@ namespace Slic3r {
if (!start_filament_gcode.empty()) { if (!start_filament_gcode.empty()) {
// Process the start_filament_gcode for the active filament only. // Process the start_filament_gcode for the active filament only.
DynamicConfig config; 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)); 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); 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); 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 // Write the custom start G-code
file.writeln(start_gcode); 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); this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
print.throw_if_canceled(); print.throw_if_canceled();
@ -1899,6 +1892,8 @@ namespace ProcessLayer
// && !MMU1 // && !MMU1
) { ) {
//! FIXME_in_fw show message during print pause //! 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; DynamicConfig cfg;
cfg.set_key_value("color_change_extruder", new ConfigOptionInt(m600_extruder_before_layer)); 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); 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; DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); 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("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", gcode += this->placeholder_parser_process("layer_gcode",
print.config().layer_gcode.value, m_writer.extruder()->id(), &config) print.config().layer_gcode.value, m_writer.extruder()->id(), &config)
+ "\n"; + "\n";
config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
} }
if (! first_layer && ! m_second_layer_things_done) { 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); const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id);
if (! start_filament_gcode.empty()) { if (! start_filament_gcode.empty()) {
// Process the start_filament_gcode for the filament. // 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); check_add_eol(gcode);
} }
gcode += m_writer.toolchange(extruder_id); 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); const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id);
if (! start_filament_gcode.empty()) { if (! start_filament_gcode.empty()) {
// Process the start_filament_gcode for the new filament. // 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); check_add_eol(gcode);
} }
// Set the new extruder to the operating temperature. // Set the new extruder to the operating temperature.

View File

@ -1196,6 +1196,7 @@ void GCodeProcessor::reset()
m_line_id = 0; m_line_id = 0;
m_last_line_id = 0; m_last_line_id = 0;
m_feedrate = 0.0f; m_feedrate = 0.0f;
m_feed_multiply.reset();
m_width = 0.0f; m_width = 0.0f;
m_height = 0.0f; m_height = 0.0f;
m_forced_width = 0.0f; m_forced_width = 0.0f;
@ -1698,6 +1699,7 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool
break; break;
case '2': case '2':
switch (cmd[3]) { switch (cmd[3]) {
case '0': { process_M220(line); break; } // Set Feedrate Percentage
case '1': { process_M221(line); break; } // Set extrude factor override percentage case '1': { process_M221(line); break; } // Set extrude factor override percentage
default: break; default: break;
} }
@ -2498,7 +2500,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
// updates feedrate from line, if present // updates feedrate from line, if present
if (line.has_f()) 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 // calculates movement deltas
float max_abs_delta = 0.0f; float max_abs_delta = 0.0f;
@ -2863,7 +2865,7 @@ void GCodeProcessor::process_G61(const GCodeReader::GCodeLine& line)
modified = true; modified = true;
} }
if (line.has_f()) if (line.has_f())
m_feedrate = line.f(); m_feedrate = m_feed_multiply.current * line.f();
if (!modified) if (!modified)
m_end_position = m_saved_position; 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) void GCodeProcessor::process_M221(const GCodeReader::GCodeLine& line)
{ {
float value_s; float value_s;

View File

@ -525,6 +525,17 @@ namespace Slic3r {
unsigned int m_line_id; unsigned int m_line_id;
unsigned int m_last_line_id; unsigned int m_last_line_id;
float m_feedrate; // mm/s 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_width; // mm
float m_height; // mm float m_height; // mm
float m_forced_width; // mm float m_forced_width; // mm
@ -719,6 +730,9 @@ namespace Slic3r {
// Advanced settings // Advanced settings
void process_M205(const GCodeReader::GCodeLine& line); void process_M205(const GCodeReader::GCodeLine& line);
// Set Feedrate Percentage
void process_M220(const GCodeReader::GCodeLine& line);
// Set extrude factor override percentage // Set extrude factor override percentage
void process_M221(const GCodeReader::GCodeLine& line); void process_M221(const GCodeReader::GCodeLine& line);

View File

@ -982,7 +982,7 @@ bool SupportTreeBuildsteps::connect_to_model_body(Head &head)
double w = dist - 2 * head.r_pin_mm - head.r_back_mm; double w = dist - 2 * head.r_pin_mm - head.r_back_mm;
if (w < 0.) { if (w < 0.) {
BOOST_LOG_TRIVIAL(error) << "Pinhead width is negative!"; BOOST_LOG_TRIVIAL(warning) << "Pinhead width is negative!";
w = 0.; w = 0.;
} }

View File

@ -3,6 +3,7 @@
#include "Format/SL1.hpp" #include "Format/SL1.hpp"
#include "Format/SL1_SVG.hpp" #include "Format/SL1_SVG.hpp"
#include "Format/pwmx.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Geometry.hpp" #include "Geometry.hpp"
@ -244,12 +245,8 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con
// Handle changes to object config defaults // Handle changes to object config defaults
m_default_object_config.apply_only(config, object_diff, true); m_default_object_config.apply_only(config, object_diff, true);
if (!m_archiver || !printer_diff.empty()) { if (!m_archiver || !printer_diff.empty())
if (m_printer_config.sla_archive_format.value == "SL1") m_archiver = SLAArchive::create(m_printer_config.sla_archive_format.value.c_str(), m_printer_config);
m_archiver = std::make_unique<SL1Archive>(m_printer_config);
else if (m_printer_config.sla_archive_format.value == "SL2")
m_archiver = std::make_unique<SL1_SVGArchive>(m_printer_config);
}
struct ModelObjectStatus { struct ModelObjectStatus {
enum Status { enum Status {

View File

@ -9,6 +9,8 @@
#include "Point.hpp" #include "Point.hpp"
#include "MTUtils.hpp" #include "MTUtils.hpp"
#include "Zipper.hpp" #include "Zipper.hpp"
#include "Format/SLAArchive.hpp"
#include "GCode/ThumbnailData.hpp"
#include "libslic3r/Execution/ExecutionTBB.hpp" #include "libslic3r/Execution/ExecutionTBB.hpp"
@ -389,47 +391,6 @@ struct SLAPrintStatistics
} }
}; };
class SLAArchive {
protected:
std::vector<sla::EncodedRaster> m_layers;
virtual std::unique_ptr<sla::RasterBase> 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<class Fn, class CancelFn, class EP = ExecutionTBB>
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. * @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. // TODO: use this structure for the preview in the future.
const std::vector<PrintLayer>& print_layers() const { return m_printer_input; } const std::vector<PrintLayer>& 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 = "") void export_print(const std::string &fname, const std::string &projectname = "")
{ {
Zipper zipper(fname); ThumbnailsList thumbnails; //empty thumbnail list
export_print(zipper, projectname); 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: private:

View File

@ -76,6 +76,8 @@
#define ENABLE_RELOAD_FROM_DISK_REWORK (1 && ENABLE_2_5_0_ALPHA1) #define ENABLE_RELOAD_FROM_DISK_REWORK (1 && ENABLE_2_5_0_ALPHA1)
// Enable showing toolpaths center of gravity // Enable showing toolpaths center of gravity
#define ENABLE_SHOW_TOOLPATHS_COG (1 && ENABLE_2_5_0_ALPHA1) #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_ #endif // _prusaslicer_technologies_h_

View File

@ -270,7 +270,7 @@ endforeach()
encoding_check(libslic3r_gui) 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) if (MSVC)
target_link_libraries(libslic3r_gui Setupapi.lib) target_link_libraries(libslic3r_gui Setupapi.lib)

View File

@ -312,8 +312,7 @@ void Bed3D::init_triangles()
return; return;
GLModel::Geometry init_data; 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, GLModel::Geometry::index_type(triangles.size()) };
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3T2, index_type };
init_data.reserve_vertices(triangles.size()); init_data.reserve_vertices(triangles.size());
init_data.reserve_indices(triangles.size() / 3); init_data.reserve_indices(triangles.size() / 3);
@ -335,10 +334,10 @@ void Bed3D::init_triangles()
unsigned int vertices_counter = 0; unsigned int vertices_counter = 0;
for (const Vec2f& v : triangles) { for (const Vec2f& v : triangles) {
const Vec3f p = { v.x(), v.y(), GROUND_Z }; 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; ++vertices_counter;
if (vertices_counter % 3 == 0) { 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); init_data.add_ushort_triangle((unsigned short)vertices_counter - 3, (unsigned short)vertices_counter - 2, (unsigned short)vertices_counter - 1);
else else
init_data.add_uint_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); 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)); std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines));
GLModel::Geometry init_data; 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, GLModel::Geometry::index_type(2 * gridlines.size()) };
init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, index_type };
init_data.reserve_vertices(2 * gridlines.size()); init_data.reserve_vertices(2 * gridlines.size());
init_data.reserve_indices(2 * gridlines.size()); init_data.reserve_indices(2 * gridlines.size());
@ -390,7 +388,7 @@ void Bed3D::init_gridlines()
init_data.add_vertex(Vec3f(unscale<float>(l.a.x()), unscale<float>(l.a.y()), GROUND_Z)); init_data.add_vertex(Vec3f(unscale<float>(l.a.x()), unscale<float>(l.a.y()), GROUND_Z));
init_data.add_vertex(Vec3f(unscale<float>(l.b.x()), unscale<float>(l.b.y()), GROUND_Z)); init_data.add_vertex(Vec3f(unscale<float>(l.b.x()), unscale<float>(l.b.y()), GROUND_Z));
const unsigned int vertices_counter = (unsigned int)init_data.vertices_count(); 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); init_data.add_ushort_line((unsigned short)vertices_counter - 2, (unsigned short)vertices_counter - 1);
else else
init_data.add_uint_line(vertices_counter - 2, vertices_counter - 1); init_data.add_uint_line(vertices_counter - 2, vertices_counter - 1);

View File

@ -1,10 +1,12 @@
#include <GL/glew.h> #include <GL/glew.h>
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_SMOOTH_NORMALS #if ENABLE_SMOOTH_NORMALS
#include <igl/per_face_normals.h> #include <igl/per_face_normals.h>
#include <igl/per_corner_normals.h> #include <igl/per_corner_normals.h>
#include <igl/per_vertex_normals.h> #include <igl/per_vertex_normals.h>
#endif // ENABLE_SMOOTH_NORMALS #endif // ENABLE_SMOOTH_NORMALS
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#include "3DScene.hpp" #include "3DScene.hpp"
#include "GLShader.hpp" #include "GLShader.hpp"
@ -69,6 +71,7 @@ void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char
namespace Slic3r { namespace Slic3r {
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_SMOOTH_NORMALS #if ENABLE_SMOOTH_NORMALS
static void smooth_normals_corner(TriangleMesh& mesh, std::vector<stl_normal>& normals) static void smooth_normals_corner(TriangleMesh& mesh, std::vector<stl_normal>& normals)
{ {
@ -287,6 +290,7 @@ void GLIndexedVertexArray::render(
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
} }
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
const float GLVolume::SinkingContours::HalfWidth = 0.25f; const float GLVolume::SinkingContours::HalfWidth = 0.25f;
@ -400,8 +404,7 @@ void GLVolume::NonManifoldEdges::update()
if (!edges.empty()) { if (!edges.empty()) {
GUI::GLModel::Geometry init_data; GUI::GLModel::Geometry init_data;
#if ENABLE_GLBEGIN_GLEND_REMOVAL #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, GUI::GLModel::Geometry::index_type(2 * edges.size()) };
init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Lines, GUI::GLModel::Geometry::EVertexLayout::P3, index_type };
init_data.reserve_vertices(2 * edges.size()); init_data.reserve_vertices(2 * edges.size());
init_data.reserve_indices(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<float>()); init_data.add_vertex((Vec3f)mesh.its.vertices[edge.first].cast<float>());
init_data.add_vertex((Vec3f)mesh.its.vertices[edge.second].cast<float>()); init_data.add_vertex((Vec3f)mesh.its.vertices[edge.second].cast<float>());
vertices_count += 2; 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); init_data.add_ushort_line((unsigned short)vertices_count - 2, (unsigned short)vertices_count - 1);
else else
init_data.add_uint_line(vertices_count - 2, vertices_count - 1); 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_neutral_color(false)
, force_sinking_contours(false) , force_sinking_contours(false)
, tverts_range(0, size_t(-1)) , tverts_range(0, size_t(-1))
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
, qverts_range(0, size_t(-1)) , qverts_range(0, size_t(-1))
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
{ {
color = { r, g, b, a }; color = { r, g, b, a };
set_render_color(color); set_render_color(color);
@ -600,6 +605,36 @@ const BoundingBoxf3& GLVolume::transformed_non_sinking_bounding_box() const
return *m_transformed_non_sinking_bounding_box; 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) void GLVolume::set_range(double min_z, double max_z)
{ {
this->qverts_range.first = 0; 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) { if (this->print_zs.front() > max_z || this->print_zs.back() < min_z) {
this->qverts_range.second = 0; this->qverts_range.second = 0;
this->tverts_range.second = 0; this->tverts_range.second = 0;
} else { }
else {
// Then find the lowest layer to be displayed. // Then find the lowest layer to be displayed.
size_t i = 0; size_t i = 0;
for (; i < this->print_zs.size() && this->print_zs[i] < min_z; ++ i); 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 shall not happen.
this->qverts_range.second = 0; this->qverts_range.second = 0;
this->tverts_range.second = 0; this->tverts_range.second = 0;
} else { }
else {
// Remember start of the layer. // Remember start of the layer.
this->qverts_range.first = this->offsets[i * 2]; this->qverts_range.first = this->offsets[i * 2];
this->tverts_range.first = this->offsets[i * 2 + 1]; 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) if (!is_active)
return; return;
@ -646,7 +684,14 @@ void GLVolume::render() const
glsafe(::glPushMatrix()); glsafe(::glPushMatrix());
glsafe(::glMultMatrixd(world_matrix().data())); glsafe(::glMultMatrixd(world_matrix().data()));
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (tverts_range == std::make_pair<size_t, size_t>(0, -1))
model.render();
else
model.render(this->tverts_range);
#else
this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); this->indexed_vertex_array.render(this->tverts_range, this->qverts_range);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
glsafe(::glPopMatrix()); glsafe(::glPopMatrix());
if (this->is_left_handed()) if (this->is_left_handed())
@ -681,36 +726,59 @@ void GLVolume::render_non_manifold_edges()
} }
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES #endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
std::vector<int> GLVolumeCollection::load_object(
const ModelObject* model_object,
int obj_idx,
const std::vector<int>& instance_idxs)
#else
std::vector<int> GLVolumeCollection::load_object( std::vector<int> GLVolumeCollection::load_object(
const ModelObject *model_object, const ModelObject *model_object,
int obj_idx, int obj_idx,
const std::vector<int> &instance_idxs, const std::vector<int> &instance_idxs,
const std::string &color_by,
bool opengl_initialized) bool opengl_initialized)
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
{ {
std::vector<int> volumes_idx; std::vector<int> volumes_idx;
for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++volume_idx) for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++volume_idx)
for (int instance_idx : instance_idxs) 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; 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( int GLVolumeCollection::load_object_volume(
const ModelObject *model_object, const ModelObject *model_object,
int obj_idx, int obj_idx,
int volume_idx, int volume_idx,
int instance_idx, int instance_idx,
const std::string &color_by,
bool opengl_initialized) bool opengl_initialized)
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
{ {
const ModelVolume *model_volume = model_object->volumes[volume_idx]; const ModelVolume *model_volume = model_object->volumes[volume_idx];
const int extruder_id = model_volume->extruder_id(); const int extruder_id = model_volume->extruder_id();
const ModelInstance *instance = model_object->instances[instance_idx]; const ModelInstance *instance = model_object->instances[instance_idx];
const TriangleMesh &mesh = model_volume->mesh(); const TriangleMesh &mesh = model_volume->mesh();
ColorRGBA color = GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4]; this->volumes.emplace_back(new GLVolume());
color.a(model_volume->is_model_part() ? 1.0f : 0.5f);
this->volumes.emplace_back(new GLVolume(color));
GLVolume& v = *this->volumes.back(); 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)); v.set_color(color_from_model_volume(*model_volume));
#if ENABLE_SMOOTH_NORMALS #if ENABLE_SMOOTH_NORMALS
v.indexed_vertex_array.load_mesh(mesh, true); v.indexed_vertex_array.load_mesh(mesh, true);
@ -718,9 +786,9 @@ int GLVolumeCollection::load_object_volume(
v.indexed_vertex_array.load_mesh(mesh); v.indexed_vertex_array.load_mesh(mesh);
#endif // ENABLE_SMOOTH_NORMALS #endif // ENABLE_SMOOTH_NORMALS
v.indexed_vertex_array.finalize_geometry(opengl_initialized); v.indexed_vertex_array.finalize_geometry(opengl_initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx); 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! // GLVolume will reference a convex hull from model_volume!
v.set_convex_hull(model_volume->get_convex_hull_shared_ptr()); v.set_convex_hull(model_volume->get_convex_hull_shared_ptr());
if (extruder_id != -1) if (extruder_id != -1)
@ -737,6 +805,16 @@ int GLVolumeCollection::load_object_volume(
// Load SLA auxiliary GLVolumes (for support trees or pad). // Load SLA auxiliary GLVolumes (for support trees or pad).
// This function produces volumes for multiple instances in a single shot, // This function produces volumes for multiple instances in a single shot,
// as some object specific mesh conversions may be expensive. // 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 <instance_idx, print_instance_idx>
const std::vector<std::pair<size_t, size_t>>& instances,
SLAPrintObjectStep milestone,
// Timestamp of the last change of the milestone
size_t timestamp)
#else
void GLVolumeCollection::load_object_auxiliary( void GLVolumeCollection::load_object_auxiliary(
const SLAPrintObject *print_object, const SLAPrintObject *print_object,
int obj_idx, int obj_idx,
@ -746,6 +824,7 @@ void GLVolumeCollection::load_object_auxiliary(
// Timestamp of the last change of the milestone // Timestamp of the last change of the milestone
size_t timestamp, size_t timestamp,
bool opengl_initialized) bool opengl_initialized)
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
{ {
assert(print_object->is_step_done(milestone)); assert(print_object->is_step_done(milestone));
Transform3d mesh_trafo_inv = print_object->trafo().inverse(); 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]; 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)); this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR));
GLVolume& v = *this->volumes.back(); 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 #if ENABLE_SMOOTH_NORMALS
v.indexed_vertex_array.load_mesh(mesh, true); v.indexed_vertex_array.load_mesh(mesh, true);
#else #else
v.indexed_vertex_array.load_mesh(mesh); v.indexed_vertex_array.load_mesh(mesh);
#endif // ENABLE_SMOOTH_NORMALS #endif // ENABLE_SMOOTH_NORMALS
v.indexed_vertex_array.finalize_geometry(opengl_initialized); 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.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first);
v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id); v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
// Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. // 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 #if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
int GLVolumeCollection::load_wipe_tower_preview( int GLVolumeCollection::load_wipe_tower_preview(
float pos_x, float pos_y, float width, float depth, float height, 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, 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) float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized)
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL #endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
{ {
if (depth < 0.01f) if (depth < 0.01f)
return int(this->volumes.size() - 1); return int(this->volumes.size() - 1);
@ -844,9 +944,16 @@ int GLVolumeCollection::load_wipe_tower_preview(
volumes.emplace_back(new GLVolume(color)); volumes.emplace_back(new GLVolume(color));
GLVolume& v = *volumes.back(); 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); v.indexed_vertex_array.load_mesh(mesh);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.set_convex_hull(mesh.convex_hull_3d()); v.set_convex_hull(mesh.convex_hull_3d());
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.indexed_vertex_array.finalize_geometry(opengl_initialized); 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_offset(Vec3d(pos_x, pos_y, 0.0));
v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle)); v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL #if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
@ -861,6 +968,22 @@ int GLVolumeCollection::load_wipe_tower_preview(
return int(volumes.size() - 1); 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* GLVolumeCollection::new_toolpath_volume(const ColorRGBA& rgba, size_t reserve_vbo_floats)
{ {
GLVolume *out = new_nontoolpath_volume(rgba, 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); this->volumes.emplace_back(out);
return out; return out;
} }
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func) GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func)
{ {
@ -965,7 +1089,10 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_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("z_range", m_z_range, 2);
shader->set_uniform("clipping_plane", m_clipping_plane, 4); shader->set_uniform("clipping_plane", m_clipping_plane, 4);
shader->set_uniform("print_volume.type", static_cast<int>(m_print_volume.type)); shader->set_uniform("print_volume.type", static_cast<int>(m_print_volume.type));
@ -985,6 +1112,10 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
#endif // ENABLE_ENVIRONMENT_MAP #endif // ENABLE_ENVIRONMENT_MAP
glcheck(); 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(); volume.first->render();
#if ENABLE_ENVIRONMENT_MAP #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()) + ")"; 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<double>& widths,
const std::vector<double>& 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<int, 4> idx_prev = { -1, -1, -1, -1 };
std::array<int, 4> 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<double>(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<int, 4> idx_a = { 0, 0, 0, 0 };
std::array<int, 4> 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<double>& widths,
const std::vector<double>& 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<int, 4> idx_prev = { -1, -1, -1, -1 };
std::array<int, 4> 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<Vec3d, 4> a = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() };
std::array<Vec3d, 4> 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<double>(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<int, 4> idx_a = { 0, 0, 0, 0};
std::array<int, 4> 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<float>(), (Vec3f)n_top.cast<float>());
}
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<float>(), (Vec3f)n_bottom.cast<float>());
idx_a[Left] = idx_last++;
geometry.add_vertex((Vec3f)a[Left].cast<float>(), (Vec3f)n_left.cast<float>());
idx_a[Right] = idx_last++;
geometry.add_vertex((Vec3f)a[Right].cast<float>(), (Vec3f)n_right.cast<float>());
}
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<float>(), (Vec3f)n_right.cast<float>());
idx_a[Left] = idx_last++;
geometry.add_vertex((Vec3f)a[Left].cast<float>(), (Vec3f)n_left.cast<float>());
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<float>(), (Vec3f)n_top.cast<float>());
}
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<float>(), (Vec3f)n_bottom.cast<float>());
}
// Generate new vertices for the end of this line segment.
idx_b[Left] = idx_last++;
geometry.add_vertex((Vec3f)b[Left].cast<float>(), (Vec3f)n_left.cast<float>());
idx_b[Right] = idx_last++;
geometry.add_vertex((Vec3f)b[Right].cast<float>(), (Vec3f)n_right.cast<float>());
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 // caller is responsible for supplying NO lines with zero length
static void thick_lines_to_indexed_vertex_array( static void thick_lines_to_indexed_vertex_array(
const Lines &lines, 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[3], idxs[1], idxs[4]);
volume.push_triangle(idxs[0], idxs[3], 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<double>& widths,
const std::vector<double>& 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<double>& widths,
const std::vector<double>& heights,
bool closed,
GUI::GLModel::Geometry& geometry)
{
thick_lines_to_geometry(lines, widths, heights, closed, geometry);
}
#else
void _3DScene::thick_lines_to_verts( void _3DScene::thick_lines_to_verts(
const Lines &lines, const Lines &lines,
const std::vector<double> &widths, const std::vector<double> &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); 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. // 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<double> widths(lines.size(), extrusion_path.width);
std::vector<double> 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 &copy, GLVolume &volume) void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point &copy, GLVolume &volume)
{ {
Polyline polyline = extrusion_path.polyline; Polyline polyline = extrusion_path.polyline;
@ -1783,8 +2410,27 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, flo
std::vector<double> heights(lines.size(), extrusion_path.height); std::vector<double> heights(lines.size(), extrusion_path.height);
thick_lines_to_verts(lines, widths, heights, false, print_z, volume); 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. // 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<double> widths;
std::vector<double> 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 &copy, GLVolume &volume) void _3DScene::extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point &copy, GLVolume &volume)
{ {
Lines lines; 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); 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. // 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<double> widths;
std::vector<double> 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 &copy, GLVolume &volume) void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point &copy, GLVolume &volume)
{ {
Lines lines; 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); 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 &copy, GLVolume &volume) void _3DScene::extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point &copy, GLVolume &volume)
{ {
for (const ExtrusionEntity *extrusion_entity : extrusion_entity_collection.entities) for (const ExtrusionEntity *extrusion_entity : extrusion_entity_collection.entities)
extrusionentity_to_verts(extrusion_entity, print_z, copy, volume); 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<const ExtrusionPath*>(extrusion_entity);
if (extrusion_path != nullptr)
extrusionentity_to_verts(*extrusion_path, print_z, copy, geometry);
else {
auto* extrusion_loop = dynamic_cast<const ExtrusionLoop*>(extrusion_entity);
if (extrusion_loop != nullptr)
extrusionentity_to_verts(*extrusion_loop, print_z, copy, geometry);
else {
auto* extrusion_multi_path = dynamic_cast<const ExtrusionMultiPath*>(extrusion_entity);
if (extrusion_multi_path != nullptr)
extrusionentity_to_verts(*extrusion_multi_path, print_z, copy, geometry);
else {
auto* extrusion_entity_collection = dynamic_cast<const ExtrusionEntityCollection*>(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 &copy, GLVolume &volume) void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point &copy, GLVolume &volume)
{ {
if (extrusion_entity != nullptr) { if (extrusion_entity != nullptr) {
@ -1844,9 +2545,8 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity,
auto *extrusion_entity_collection = dynamic_cast<const ExtrusionEntityCollection*>(extrusion_entity); auto *extrusion_entity_collection = dynamic_cast<const ExtrusionEntityCollection*>(extrusion_entity);
if (extrusion_entity_collection != nullptr) if (extrusion_entity_collection != nullptr)
extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, volume); extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, volume);
else { else
throw Slic3r::RuntimeError("Unexpected extrusion_entity type in to_verts()"); 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); thick_point_to_verts(point, width, height, volume);
} }
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
} // namespace Slic3r } // namespace Slic3r

View File

@ -46,6 +46,7 @@ enum ModelInstanceEPrintVolumeState : unsigned char;
// Return appropriate color based on the ModelVolume. // Return appropriate color based on the ModelVolume.
extern ColorRGBA color_from_model_volume(const ModelVolume& model_volume); extern ColorRGBA color_from_model_volume(const ModelVolume& model_volume);
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// A container for interleaved arrays of 3D vertices and normals, // A container for interleaved arrays of 3D vertices and normals,
// possibly indexed by triangles and / or quads. // possibly indexed by triangles and / or quads.
class GLIndexedVertexArray { class GLIndexedVertexArray {
@ -246,6 +247,7 @@ public:
private: private:
BoundingBox m_bounding_box; BoundingBox m_bounding_box;
}; };
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
class GLVolume { class GLVolume {
public: public:
@ -388,11 +390,17 @@ public:
// Is mouse or rectangle selection over this object to select/deselect it ? // Is mouse or rectangle selection over this object to select/deselect it ?
EHoverState hover; EHoverState hover;
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
GUI::GLModel model;
#else
// Interleaved triangles & normals with indexed triangles & quads. // Interleaved triangles & normals with indexed triangles & quads.
GLIndexedVertexArray indexed_vertex_array; GLIndexedVertexArray indexed_vertex_array;
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// Ranges of triangle and quad indices to be rendered. // Ranges of triangle and quad indices to be rendered.
std::pair<size_t, size_t> tverts_range; std::pair<size_t, size_t> tverts_range;
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
std::pair<size_t, size_t> qverts_range; std::pair<size_t, size_t> qverts_range;
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// If the qverts or tverts contain thick extrusions, then offsets keeps pointers of the starts // If the qverts or tverts contain thick extrusions, then offsets keeps pointers of the starts
// of the extrusions per layer. // of the extrusions per layer.
@ -402,13 +410,17 @@ public:
// Bounding box of this volume, in unscaled coordinates. // Bounding box of this volume, in unscaled coordinates.
BoundingBoxf3 bounding_box() const { BoundingBoxf3 bounding_box() const {
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
return this->model.get_bounding_box();
#else
BoundingBoxf3 out; 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<double>(); out.min = this->indexed_vertex_array.bounding_box().min().cast<double>();
out.max = this->indexed_vertex_array.bounding_box().max().cast<double>(); out.max = this->indexed_vertex_array.bounding_box().max().cast<double>();
out.defined = true; out.defined = true;
}; }
return out; return out;
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
} }
void set_color(const ColorRGBA& rgba) { color = rgba; } void set_color(const ColorRGBA& rgba) { color = rgba; }
@ -498,14 +510,20 @@ public:
// convex hull // convex hull
const TriangleMesh* convex_hull() const { return m_convex_hull.get(); } 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(); } bool empty() const { return this->indexed_vertex_array.empty(); }
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void set_range(double low, double high); 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 finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); }
void release_geometry() { this->indexed_vertex_array.release_geometry(); } void release_geometry() { this->indexed_vertex_array.release_geometry(); }
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void set_bounding_boxes_as_dirty() { void set_bounding_boxes_as_dirty() {
m_transformed_bounding_box.reset(); m_transformed_bounding_box.reset();
@ -524,12 +542,20 @@ public:
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES #endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
// Return an estimate of the memory consumed by this class. // Return an estimate of the memory consumed by this class.
size_t cpu_memory_used() const { size_t cpu_memory_used() const {
//FIXME what to do wih m_convex_hull? #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 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. // 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(); } 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(); } 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() { set_default_slope_normal_z(); }
~GLVolumeCollection() { clear(); } ~GLVolumeCollection() { clear(); }
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
std::vector<int> load_object(
const ModelObject* model_object,
int obj_idx,
const std::vector<int>& 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 <instance_idx, print_instance_idx>
const std::vector<std::pair<size_t, size_t>>& 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<int> load_object( std::vector<int> load_object(
const ModelObject *model_object, const ModelObject *model_object,
int obj_idx, int obj_idx,
const std::vector<int> &instance_idxs, const std::vector<int> &instance_idxs,
const std::string &color_by,
bool opengl_initialized); bool opengl_initialized);
int load_object_volume( int load_object_volume(
@ -601,7 +656,6 @@ public:
int obj_idx, int obj_idx,
int volume_idx, int volume_idx,
int instance_idx, int instance_idx,
const std::string &color_by,
bool opengl_initialized); bool opengl_initialized);
// Load SLA auxiliary GLVolumes (for support trees or pad). // Load SLA auxiliary GLVolumes (for support trees or pad).
@ -622,13 +676,20 @@ public:
int load_wipe_tower_preview( 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); 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_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_toolpath_volume(const ColorRGBA& rgba, size_t reserve_vbo_floats = 0);
GLVolume* new_nontoolpath_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. // Render the volumes by OpenGL.
void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const; void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// Finalize the initialization of the geometry & indices, // Finalize the initialization of the geometry & indices,
// upload the geometry and indices to OpenGL VBO objects // 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. // 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. // Release the geometry data assigned to the volumes.
// If OpenGL VBOs were allocated, an OpenGL context has to be active to release them. // 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(); } void release_geometry() { for (auto *v : volumes) v->release_geometry(); }
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// Clear the geometry // Clear the geometry
void clear() { for (auto *v : volumes) delete v; volumes.clear(); } void clear() { for (auto *v : volumes) delete v; volumes.clear(); }
bool empty() const { return volumes.empty(); } 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; } 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 struct _3DScene
{ {
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GUI::GLModel::Geometry& geometry);
static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& 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<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GLVolume& volume); static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GLVolume& volume);
static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GLVolume& volume); static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& 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, 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 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); 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 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 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); static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}; };
} }

View File

@ -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() void BackgroundSlicingProcess::process_sla()
{ {
assert(m_print == m_sla_print); assert(m_print == m_sla_print);
@ -189,12 +178,7 @@ void BackgroundSlicingProcess::process_sla()
ThumbnailsList thumbnails = this->render_thumbnails( ThumbnailsList thumbnails = this->render_thumbnails(
ThumbnailsParams{current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, true, true}); ThumbnailsParams{current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, true, true});
Zipper zipper(export_path); m_sla_print->export_print(export_path, thumbnails);
m_sla_print->export_print(zipper);
for (const ThumbnailData& data : thumbnails)
if (data.is_valid())
write_thumbnail(zipper, data);
zipper.finalize();
m_print->set_status(100, (boost::format(_utf8(L("Masked SLA file exported to %1%"))) % export_path).str()); m_print->set_status(100, (boost::format(_utf8(L("Masked SLA file exported to %1%"))) % export_path).str());
} else if (! m_upload_job.empty()) { } else if (! m_upload_job.empty()) {
@ -739,13 +723,7 @@ void BackgroundSlicingProcess::prepare_upload()
ThumbnailsList thumbnails = this->render_thumbnails( ThumbnailsList thumbnails = this->render_thumbnails(
ThumbnailsParams{current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, true, true}); ThumbnailsParams{current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, true, true});
// true, false, true, true); // renders also supports and pad m_sla_print->export_print(source_path.string(),thumbnails, m_upload_job.upload_data.upload_path.string());
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_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()); 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());

View File

@ -103,7 +103,11 @@ void GCodeViewer::IBuffer::reset()
count = 0; 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 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) { auto matches_percent = [](float value1, float value2, float max_percent) {
return std::abs(value2 - value1) / value1 <= 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::Seam:
case EMoveType::Extrude: { case EMoveType::Extrude: {
// use rounding to reduce the number of generated paths // 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 && 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 && 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) && height == round_to_bin(move.height) && width == round_to_bin(move.width) &&
matches_percent(volumetric_rate, move.volumetric_rate(), 0.05f); matches_percent(volumetric_rate, move.volumetric_rate(), 0.05f);
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
} }
case EMoveType::Travel: { case EMoveType::Travel: {
return type == move.type && feedrate == move.feedrate && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id; 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; 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) 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 // 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) if (m_last_result_id == gcode_result.id)
return; return;
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
m_last_result_id = gcode_result.id; 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 // release gpu memory, if used
reset(); reset();
@ -725,7 +754,11 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr
m_filament_densities = gcode_result.filament_densities; m_filament_densities = gcode_result.filament_densities;
if (wxGetApp().is_editor()) if (wxGetApp().is_editor())
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
load_shells(print);
#else
load_shells(print, initialized); load_shells(print, initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
else { else {
Pointfs bed_shape; Pointfs bed_shape;
std::string texture; std::string texture;
@ -1266,9 +1299,15 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
// add current vertex // add current vertex
add_vertex(curr); 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, 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) { unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr)) { if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr)) {
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
// add starting index // add starting index
indices.push_back(static_cast<IBufferType>(indices.size())); indices.push_back(static_cast<IBufferType>(indices.size()));
buffer.add_path(curr, ibuffer_id, indices.size() - 1, move_id - 1); 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 // 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) { auto store_vertex = [](VertexBuffer& vertices, const Vec3f& position, const Vec3f& normal) {
// append position // append position
vertices.push_back(position.x()); vertices.push_back(position.x());
@ -1299,7 +1344,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
vertices.push_back(normal.z()); 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)) { 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.add_path(curr, vbuffer_id, vertices.size(), move_id - 1);
buffer.paths.back().sub_paths.back().first.position = prev.position; 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 }; 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, #if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
TBuffer& buffer, size_t& vbuffer_size, unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) { 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_dir;
static Vec3f prev_up; static Vec3f prev_up;
static float sq_prev_length; 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]); 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)) { 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.add_path(curr, ibuffer_id, indices.size(), move_id - 1);
buffer.paths.back().sub_paths.back().first.position = prev.position; 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; 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))) if (next != nullptr && (curr.type != next->type || !last_path.matches(*next)))
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
// ending cap triangles // ending cap triangles
append_ending_cap_triangles(indices, is_first_segment ? first_seg_v_offsets : non_first_seg_v_offsets); 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); 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<MultiVertexBuffer> vertices(m_buffers.size()); std::vector<MultiVertexBuffer> vertices(m_buffers.size());
std::vector<MultiIndexBuffer> indices(m_buffers.size()); std::vector<MultiIndexBuffer> indices(m_buffers.size());
std::vector<InstanceBuffer> instances(m_buffers.size()); std::vector<InstanceBuffer> instances(m_buffers.size());
@ -1678,7 +1746,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
v_multibuffer.push_back(VertexBuffer()); v_multibuffer.push_back(VertexBuffer());
if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) { if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) {
Path& last_path = t_buffer.paths.back(); 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)) if (prev.type == curr.type && last_path.matches(curr))
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
last_path.add_sub_path(prev, static_cast<unsigned int>(v_multibuffer.size()) - 1, 0, move_id - 1); last_path.add_sub_path(prev, static_cast<unsigned int>(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::Point: { add_vertices_as_point(curr, v_buffer); break; }
case TBuffer::ERenderPrimitiveType::Line: { add_vertices_as_line(prev, 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<unsigned int>(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<unsigned int>(v_multibuffer.size()) - 1, v_buffer, move_id); break; } case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast<unsigned int>(v_multibuffer.size()) - 1, v_buffer, move_id); break; }
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
case TBuffer::ERenderPrimitiveType::InstancedModel: case TBuffer::ERenderPrimitiveType::InstancedModel:
{ {
add_model_instance(curr, inst_buffer, inst_id_buffer, move_id); add_model_instance(curr, inst_buffer, inst_id_buffer, move_id);
@ -2053,12 +2129,20 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
break; break;
} }
case TBuffer::ERenderPrimitiveType::Line: { case TBuffer::ERenderPrimitiveType::Line: {
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
add_indices_as_line(prev, curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id, account_for_volumetric_rate);
#else
add_indices_as_line(prev, curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id); add_indices_as_line(prev, curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id);
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
curr_vertex_buffer.second += t_buffer.max_vertices_per_segment(); curr_vertex_buffer.second += t_buffer.max_vertices_per_segment();
break; break;
} }
case TBuffer::ERenderPrimitiveType::Triangle: { 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<unsigned int>(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<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id); add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id);
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
break; break;
} }
case TBuffer::ERenderPrimitiveType::BatchedModel: { case TBuffer::ERenderPrimitiveType::BatchedModel: {
@ -2213,7 +2297,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
progress_dialog->Destroy(); progress_dialog->Destroy();
} }
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void GCodeViewer::load_shells(const Print& print)
#else
void GCodeViewer::load_shells(const Print& print, bool initialized) void GCodeViewer::load_shells(const Print& print, bool initialized)
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
{ {
if (print.objects().empty()) if (print.objects().empty())
// no shells, return // 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(); 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 // adjust shells' z if raft is present
const SlicingParameters& slicing_parameters = obj->slicing_parameters(); 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 depth = print.wipe_tower_data(extruders_count).depth;
const float brim_width = print.wipe_tower_data(extruders_count).brim_width; 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 #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, 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); !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, 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); !print.is_step_done(psWipeTower), brim_width, initialized);
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL #endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
} }
} }
@ -3123,6 +3225,7 @@ void GCodeViewer::render_shells()
if (shader == nullptr) if (shader == nullptr)
return; return;
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// when the background processing is enabled, it may happen that the shells data have been loaded // 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. // before opengl has been initialized for the preview canvas.
// when this happens, the volumes' data have not been sent to gpu yet. // 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()) if (!v->indexed_vertex_array.has_VBOs())
v->finalize_geometry(true); v->finalize_geometry(true);
} }
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// glsafe(::glDepthMask(GL_FALSE)); // glsafe(::glDepthMask(GL_FALSE));

View File

@ -212,7 +212,11 @@ class GCodeViewer
unsigned char cp_color_id{ 0 }; unsigned char cp_color_id{ 0 };
std::vector<Sub_Path> sub_paths; std::vector<Sub_Path> 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; bool matches(const GCodeProcessorResult::MoveVertex& move) const;
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
size_t vertices_count() const { size_t vertices_count() const {
return sub_paths.empty() ? 0 : sub_paths.back().last.s_id - sub_paths.front().first.s_id + 1; return sub_paths.empty() ? 0 : sub_paths.back().last.s_id - sub_paths.front().first.s_id + 1;
} }
@ -762,6 +766,9 @@ public:
private: private:
bool m_gl_data_initialized{ false }; bool m_gl_data_initialized{ false };
unsigned int m_last_result_id{ 0 }; 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 }; size_t m_moves_count{ 0 };
std::vector<TBuffer> m_buffers{ static_cast<size_t>(EMoveType::Extrude) }; std::vector<TBuffer> m_buffers{ static_cast<size_t>(EMoveType::Extrude) };
// bounding box of toolpaths // bounding box of toolpaths
@ -816,7 +823,11 @@ public:
void init(); void init();
// extract rendering data from the given parameters // 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); 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 // recalculate ranges in dependence of what is visible and sets tool/print colors
void refresh(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors); void refresh(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors);
#if ENABLE_PREVIEW_LAYOUT #if ENABLE_PREVIEW_LAYOUT
@ -876,7 +887,11 @@ public:
private: private:
void load_toolpaths(const GCodeProcessorResult& gcode_result); 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); void load_shells(const Print& print, bool initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if !ENABLE_PREVIEW_LAYOUT #if !ENABLE_PREVIEW_LAYOUT
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
#endif // !ENABLE_PREVIEW_LAYOUT #endif // !ENABLE_PREVIEW_LAYOUT

View File

@ -87,9 +87,11 @@ static const Slic3r::ColorRGB ERROR_BG_LIGHT_COLOR = { 0.753f, 0.192f, 0.039f
// Number of floats // Number of floats
static constexpr const size_t MAX_VERTEX_BUFFER_SIZE = 131072 * 6; // 3.15MB static constexpr const size_t MAX_VERTEX_BUFFER_SIZE = 131072 * 6; // 3.15MB
// Reserve size in number of floats. // Reserve size in number of floats.
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
static constexpr const size_t VERTEX_BUFFER_RESERVE_SIZE = 131072 * 2; // 1.05MB 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. // 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 //static constexpr const size_t VERTEX_BUFFER_RESERVE_SIZE_SUM_MAX = 1024 * 1024 * 128 / 4; // 128MB
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
@ -447,8 +449,7 @@ void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect)
m_profile.profile.reset(); m_profile.profile.reset();
GLModel::Geometry init_data; 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, GLModel::Geometry::index_type(m_layer_height_profile.size() / 2) };
init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P2, index_type };
init_data.color = ColorRGBA::BLUE(); init_data.color = ColorRGBA::BLUE();
init_data.reserve_vertices(m_layer_height_profile.size() / 2); init_data.reserve_vertices(m_layer_height_profile.size() / 2);
init_data.reserve_indices(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) { 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, 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)); 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); init_data.add_ushort_index((unsigned short)i / 2);
else else
init_data.add_uint_index(i / 2); 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->is_allowed());
assert(this->last_object_id != -1); 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"); GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height");
if (shader == nullptr) if (shader == nullptr)
return; return;
GLShaderProgram* current_shader = wxGetApp().get_current_shader(); shader->start_using();
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;
generate_layer_height_texture(); 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)); shader->set_uniform("z_cursor_band_width", float(this->band_width));
// Initialize the layer height texture mapping. // Initialize the layer height texture mapping.
GLsizei w = (GLsizei)m_layers_texture.width; const GLsizei w = (GLsizei)m_layers_texture.width;
GLsizei h = (GLsizei)m_layers_texture.height; const GLsizei h = (GLsizei)m_layers_texture.height;
GLsizei half_w = w / 2; const GLsizei half_w = w / 2;
GLsizei half_h = h / 2; const GLsizei half_h = h / 2;
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); 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, 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(::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, 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)); 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. // 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; continue;
shader->set_uniform("volume_world_matrix", glvolume->world_matrix()); 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(); glvolume->render();
} }
// Revert back to the previous shader. // Revert back to the previous shader.
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
if (current_shader != nullptr)
current_shader->start_using();
} }
void GLCanvas3D::LayersEditing::adjust_layer_height_profile() void GLCanvas3D::LayersEditing::adjust_layer_height_profile()
@ -1107,7 +1106,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)
, m_moving(false) , m_moving(false)
, m_tab_down(false) , m_tab_down(false)
, m_cursor_type(Standard) , m_cursor_type(Standard)
, m_color_by("volume")
, m_reload_delayed(false) , m_reload_delayed(false)
#if ENABLE_RENDER_PICKING_PASS #if ENABLE_RENDER_PICKING_PASS
, m_show_picking_texture(false) , m_show_picking_texture(false)
@ -1158,6 +1156,7 @@ bool GLCanvas3D::init()
glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
#if !ENABLE_GLBEGIN_GLEND_REMOVAL
// Set antialiasing / multisampling // Set antialiasing / multisampling
glsafe(::glDisable(GL_LINE_SMOOTH)); glsafe(::glDisable(GL_LINE_SMOOTH));
glsafe(::glDisable(GL_POLYGON_SMOOTH)); glsafe(::glDisable(GL_POLYGON_SMOOTH));
@ -1187,6 +1186,7 @@ bool GLCanvas3D::init()
// A handy trick -- have surface material mirror the color. // A handy trick -- have surface material mirror the color.
glsafe(::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)); glsafe(::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE));
glsafe(::glEnable(GL_COLOR_MATERIAL)); glsafe(::glEnable(GL_COLOR_MATERIAL));
#endif // !ENABLE_GLBEGIN_GLEND_REMOVAL
if (m_multisample_allowed) if (m_multisample_allowed)
glsafe(::glEnable(GL_MULTISAMPLE)); glsafe(::glEnable(GL_MULTISAMPLE));
@ -1194,9 +1194,11 @@ bool GLCanvas3D::init()
if (m_main_toolbar.is_enabled()) if (m_main_toolbar.is_enabled())
m_layers_editing.init(); m_layers_editing.init();
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// on linux the gl context is not valid until the canvas is not shown on screen // 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() // we defer the geometry finalization of volumes until the first call to render()
m_volumes.finalize_geometry(true); m_volumes.finalize_geometry(true);
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (m_gizmos.is_enabled() && !m_gizmos.init()) 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; 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; m_dirty = true;
} }
void GLCanvas3D::set_color_by(const std::string& value)
{
m_color_by = value;
}
void GLCanvas3D::refresh_camera_scene_box() void GLCanvas3D::refresh_camera_scene_box()
{ {
wxGetApp().plater()->get_camera().set_scene_box(scene_bounding_box()); wxGetApp().plater()->get_camera().set_scene_box(scene_bounding_box());
@ -1806,7 +1803,11 @@ std::vector<int> GLCanvas3D::load_object(const ModelObject& model_object, int ob
instance_idxs.emplace_back(i); 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<int> GLCanvas3D::load_object(const Model& model, int obj_idx) std::vector<int> 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 // Note the index of the loaded volume, so that we can reload the main model GLVolume with the hollowed mesh
// later in this function. // later in this function.
it->volume_idx = m_volumes.volumes.size(); 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; m_volumes.volumes.back()->geometry_id = key.geometry_id;
update_object_list = true; update_object_list = true;
} else { } 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]; GLVolume &volume = *m_volumes.volumes[it->volume_idx];
if (! volume.offsets.empty() && state.step[istep].timestamp != volume.offsets.front()) { 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. // 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(); 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); TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles);
assert(! mesh.empty()); assert(! mesh.empty());
mesh.transform(sla_print->sla_trafo(*m_model->objects[volume.object_idx()]).inverse()); mesh.transform(sla_print->sla_trafo(*m_model->objects[volume.object_idx()]).inverse());
#if ENABLE_SMOOTH_NORMALS #if ENABLE_SMOOTH_NORMALS
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume.model.init_from(mesh, true);
#else
volume.indexed_vertex_array.load_mesh(mesh, true); volume.indexed_vertex_array.load_mesh(mesh, true);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#else
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume.model.init_from(mesh);
#else #else
volume.indexed_vertex_array.load_mesh(mesh); volume.indexed_vertex_array.load_mesh(mesh);
#endif // ENABLE_SMOOTH_NORMALS #endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
} 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_SMOOTH_NORMALS #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); 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 //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 // 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). // of various concenrs (model vs. 3D print path).
volume.offsets = { state.step[istep].timestamp }; 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. // 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); 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); 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<size_t, size_t>(instance_idx, print_instance_idx)); instances[istep].emplace_back(std::pair<size_t, size_t>(instance_idx, print_instance_idx));
else else
shift_zs[object_idx] = 0.; shift_zs[object_idx] = 0.;
} else { }
else {
// Recycling an old GLVolume. Update the Object/Instance indices into the current Model. // 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]->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()); 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) for (size_t istep = 0; istep < sla_steps.size(); ++istep)
if (!instances[istep].empty()) 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); 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 // 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 depth = print->wipe_tower_data(extruders_count).depth;
float brim_width = print->wipe_tower_data(extruders_count).brim_width; 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 #if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(
x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower), 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), 1000, x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower),
brim_width, m_initialized); brim_width, m_initialized);
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL #endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (volume_idx_wipe_tower_old != -1) if (volume_idx_wipe_tower_old != -1)
map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new; 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; 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) 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); vol_new.indexed_vertex_array = std::move(vol_old.indexed_vertex_array);
// Copy the content back to the old GLVolume. // Copy the content back to the old GLVolume.
vol_old.indexed_vertex_array = vol_new.indexed_vertex_array; 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. // Finalize the old geometry, possibly move data to the graphics card.
vol_old.finalize_geometry(gl_initialized); vol_old.finalize_geometry(gl_initialized);
} }
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors) void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& 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); m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (wxGetApp().is_editor()) { if (wxGetApp().is_editor()) {
m_gcode_viewer.update_shells_color_by_extruder(m_config); 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); shader->set_uniform("emission_factor", 0.0f);
for (GLVolume* vol : visible_volumes) { 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()); 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 // the volume may have been deactivated by an active gizmo
bool is_active = vol->is_active; bool is_active = vol->is_active;
vol->is_active = true; 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 // clamp max bb size with respect to bed bb size
if (!m_picking_enabled) { 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 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() || if (bb_size.x() > max_scale_factor * bed_bb_size.x() ||
bb_size.y() > max_scale_factor * bed_bb_size.y() || bb_size.y() > max_scale_factor * bed_bb_size.y() ||
bb_size.z() > max_scale_factor * bed_bb_size.z()) { 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 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 // do not cull backfaces to show broken geometry, if any
glsafe(::glDisable(GL_CULL_FACE)); 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) // we reserve color = (0,0,0) for occluders (as the printbed)
// so we shift volumes' id by 1 to get the proper color // so we shift volumes' id by 1 to get the proper color
const unsigned int id = 1 + volume.second.first; 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())); glsafe(::glColor4fv(picking_decode(id).data()));
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume.first->render(); volume.first->render();
} #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
shader->stop_using();
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
} }
glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
@ -5783,32 +5853,81 @@ void GLCanvas3D::_render_sla_slices()
if (!obj->is_step_done(slaposSliceSupports)) if (!obj->is_step_done(slaposSliceSupports))
continue; 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_bottom = m_sla_caps[0].triangles.find(i);
SlaCap::ObjectIdToTrianglesMap::iterator it_caps_top = m_sla_caps[1].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()) if (it_caps_bottom == m_sla_caps[0].triangles.end())
it_caps_bottom = m_sla_caps[0].triangles.emplace(i, SlaCap::Triangles()).first; it_caps_bottom = m_sla_caps[0].triangles.emplace(i, SlaCap::Triangles()).first;
if (!m_sla_caps[0].matches(clip_min_z)) { if (!m_sla_caps[0].matches(clip_min_z)) {
m_sla_caps[0].z = 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.object.clear();
it_caps_bottom->second.supports.clear(); it_caps_bottom->second.supports.clear();
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
} }
if (it_caps_top == m_sla_caps[1].triangles.end()) if (it_caps_top == m_sla_caps[1].triangles.end())
it_caps_top = m_sla_caps[1].triangles.emplace(i, SlaCap::Triangles()).first; it_caps_top = m_sla_caps[1].triangles.emplace(i, SlaCap::Triangles()).first;
if (!m_sla_caps[1].matches(clip_max_z)) { if (!m_sla_caps[1].matches(clip_max_z)) {
m_sla_caps[1].z = 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.object.clear();
it_caps_top->second.supports.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_obj_triangles = it_caps_bottom->second.object;
Pointf3s &bottom_sup_triangles = it_caps_bottom->second.supports; Pointf3s &bottom_sup_triangles = it_caps_bottom->second.supports;
Pointf3s &top_obj_triangles = it_caps_top->second.object; Pointf3s &top_obj_triangles = it_caps_top->second.object;
Pointf3s &top_sup_triangles = it_caps_top->second.supports; 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<float>());
++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()) && 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 layer_height = print->default_object_config().layer_height.value;
double initial_layer_height = print->material_config().initial_layer_height.value; double initial_layer_height = print->material_config().initial_layer_height.value;
bool left_handed = obj->is_left_handed(); 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)); 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. // 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()) { if (slice_low.is_valid()) {
const ExPolygons& obj_bottom = slice_low.get_slice(soModel); const ExPolygons& obj_bottom = slice_low.get_slice(soModel);
const ExPolygons& sup_bottom = slice_low.get_slice(soSupport); 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 // calculate model bottom cap
if (bottom_obj_triangles.empty() && !obj_bottom.empty()) if (bottom_obj_triangles.empty() && !obj_bottom.empty())
bottom_obj_triangles = triangulate_expolygons_3d(obj_bottom, clip_min_z - plane_shift_z, ! left_handed); bottom_obj_triangles = triangulate_expolygons_3d(obj_bottom, clip_min_z - plane_shift_z, ! left_handed);
// calculate support bottom cap // calculate support bottom cap
if (bottom_sup_triangles.empty() && !sup_bottom.empty()) 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()) { if (slice_high.is_valid()) {
const ExPolygons& obj_top = slice_high.get_slice(soModel); const ExPolygons& obj_top = slice_high.get_slice(soModel);
const ExPolygons& sup_top = slice_high.get_slice(soSupport); 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 // calculate model top cap
if (top_obj_triangles.empty() && !obj_top.empty()) if (top_obj_triangles.empty() && !obj_top.empty())
top_obj_triangles = triangulate_expolygons_3d(obj_top, clip_max_z + plane_shift_z, left_handed); top_obj_triangles = triangulate_expolygons_3d(obj_top, clip_max_z + plane_shift_z, left_handed);
// calculate support top cap // calculate support top cap
if (top_sup_triangles.empty() && !sup_top.empty()) if (top_sup_triangles.empty() && !sup_top.empty())
top_sup_triangles = triangulate_expolygons_3d(sup_top, clip_max_z + plane_shift_z, left_handed); 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<double>(inst.shift.x()), unscale<double>(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()) { if (!bottom_obj_triangles.empty() || !top_obj_triangles.empty() || !bottom_sup_triangles.empty() || !top_sup_triangles.empty()) {
for (const SLAPrintObject::Instance& inst : obj->instances()) { for (const SLAPrintObject::Instance& inst : obj->instances()) {
glsafe(::glPushMatrix()); glsafe(::glPushMatrix());
glsafe(::glTranslated(unscale<double>(inst.shift.x()), unscale<double>(inst.shift.y()), 0)); glsafe(::glTranslated(unscale<double>(inst.shift.x()), unscale<double>(inst.shift.y()), 0.0));
glsafe(::glRotatef(Geometry::rad2deg(inst.rotation), 0.0, 0.0, 1.0)); glsafe(::glRotatef(Geometry::rad2deg(inst.rotation), 0.0f, 0.0f, 1.0f));
if (obj->is_left_handed()) if (obj->is_left_handed())
// The polygons are mirrored by X. // 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(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glColor3f(1.0f, 0.37f, 0.0f)); glsafe(::glColor3f(1.0f, 0.37f, 0.0f));
if (!bottom_obj_triangles.empty()) { if (!bottom_obj_triangles.empty()) {
@ -5879,6 +6040,7 @@ void GLCanvas3D::_render_sla_slices()
glsafe(::glPopMatrix()); 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()); skirt_height = std::min(skirt_height, print_zs.size());
print_zs.erase(print_zs.begin() + skirt_height, print_zs.end()); 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); 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) { for (size_t i = 0; i < skirt_height; ++ i) {
volume->print_zs.emplace_back(print_zs[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.quad_indices.size());
volume->offsets.emplace_back(volume->indexed_vertex_array.triangle_indices.size()); volume->offsets.emplace_back(volume->indexed_vertex_array.triangle_indices.size());
if (i == 0) if (i == 0)
_3DScene::extrusionentity_to_verts(print->brim(), print_zs[i], Point(0, 0), *volume); _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); _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. // 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) { 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); volume = m_volumes.new_toolpath_volume(vol.color);
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
reserve_new_volume_finalize_old_volume(*volume, vol, m_initialized); 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->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); 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<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values) void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const BuildVolume& build_volume, const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values)
@ -6250,7 +6437,12 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
// Allocate the volume before locking. // Allocate the volume before locking.
GLVolume *volume = new GLVolume(color); GLVolume *volume = new GLVolume(color);
volume->is_extrusion_path = true; 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 by ROII, so if the emplace_back() fails, the lock will be released.
lock.acquire(new_volume_mutex); lock.acquire(new_volume_mutex);
m_volumes.volumes.emplace_back(volume); m_volumes.volumes.emplace_back(volume);
@ -6262,31 +6454,57 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size), tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size),
[&ctxt, &new_volume, is_selected_separate_extruder, this](const tbb::blocked_range<size_t>& range) { [&ctxt, &new_volume, is_selected_separate_extruder, this](const tbb::blocked_range<size_t>& range) {
GLVolumePtrs vols; GLVolumePtrs vols;
auto volume = [&ctxt, &vols](size_t layer_idx, int extruder, int feature) -> GLVolume& { #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
return *vols[ctxt.color_by_color_print()? std::vector<GLModel::Geometry> 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_print_color_idx_by_layer_idx_and_extruder(layer_idx, extruder) :
ctxt.color_by_tool() ? ctxt.color_by_tool() ?
std::min<int>(ctxt.number_tools() - 1, std::max<int>(extruder - 1, 0)) : std::min<int>(ctxt.number_tools() - 1, std::max<int>(extruder - 1, 0)) :
feature 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<int>(ctxt.number_tools() - 1, std::max<int>(extruder - 1, 0)) :
feature
];
};
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (ctxt.color_by_color_print() || ctxt.color_by_tool()) { 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))); 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()) }; 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) for (GLVolume *vol : vols)
// Reserving number of vertices (3x position + 3x color) // Reserving number of vertices (3x position + 3x color)
vol->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6); 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) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
const Layer *layer = ctxt.layers[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; 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()) if (layerm->slices.surfaces.empty())
continue; continue;
const PrintRegionConfig& cfg = layerm->region().config(); const PrintRegionConfig& cfg = layerm->region().config();
@ -6301,17 +6519,27 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
continue; 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) { if (vol->print_zs.empty() || vol->print_zs.back() != layer->print_z) {
vol->print_zs.emplace_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.quad_indices.size());
vol->offsets.emplace_back(vol->indexed_vertex_array.triangle_indices.size()); vol->offsets.emplace_back(vol->indexed_vertex_array.triangle_indices.size());
} }
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
for (const PrintInstance &instance : *ctxt.shifted_copies) { for (const PrintInstance &instance : *ctxt.shifted_copies) {
const Point &copy = instance.shift; const Point &copy = instance.shift;
for (const LayerRegion *layerm : layer->regions()) { for (const LayerRegion *layerm : layer->regions()) {
if (is_selected_separate_extruder) if (is_selected_separate_extruder) {
{
const PrintRegionConfig& cfg = layerm->region().config(); const PrintRegionConfig& cfg = layerm->region().config();
if (cfg.perimeter_extruder.value != m_selected_extruder || if (cfg.perimeter_extruder.value != m_selected_extruder ||
cfg.infill_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; continue;
} }
if (ctxt.has_perimeters) 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, _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy,
volume(idx_layer, layerm->region().config().perimeter_extruder.value, 0)); volume(idx_layer, layerm->region().config().perimeter_extruder.value, 0));
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (ctxt.has_infill) { if (ctxt.has_infill) {
for (const ExtrusionEntity *ee : layerm->fills.entities) { for (const ExtrusionEntity *ee : layerm->fills.entities) {
// fill represents infill extrusions of a single island. // fill represents infill extrusions of a single island.
const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee); const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (! fill->entities.empty()) 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, _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy,
volume(idx_layer, volume(idx_layer,
is_solid_infill(fill->entities.front()->role()) ? is_solid_infill(fill->entities.front()->role()) ?
layerm->region().config().solid_infill_extruder : layerm->region().config().solid_infill_extruder :
layerm->region().config().infill_extruder, layerm->region().config().infill_extruder,
1)); 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<const SupportLayer*>(layer); const SupportLayer *support_layer = dynamic_cast<const SupportLayer*>(layer);
if (support_layer) { if (support_layer) {
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) 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, _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy,
volume(idx_layer, volume(idx_layer,
(extrusion_entity->role() == erSupportMaterial) ? (extrusion_entity->role() == erSupportMaterial) ?
support_layer->object()->config().support_material_extruder : support_layer->object()->config().support_material_extruder :
support_layer->object()->config().support_material_interface_extruder, support_layer->object()->config().support_material_interface_extruder,
2)); 2));
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
} }
} }
} }
// Ensure that no volume grows over the limits. If the volume is too large, allocate a new one. // 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) { for (size_t i = 0; i < vols.size(); ++i) {
GLVolume &vol = *vols[i]; GLVolume &vol = *vols[i];
if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
vols[i] = new_volume(vol.color); if (geometries[i].vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) {
reserve_new_volume_finalize_old_volume(*vols[i], vol, false); 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) for (GLVolume *vol : vols)
// Ideally one would call vol->indexed_vertex_array.finalize() here to move the buffers to the OpenGL driver, // 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. // but this code runs in parallel and the OpenGL driver is not thread safe.
vol->indexed_vertex_array.shrink_to_fit(); 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(); 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) { for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) {
GLVolume* v = m_volumes.volumes[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->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); 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(); 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 struct Ctxt
{ {
const Print *print; const Print *print;
const std::vector<ColorRGBA>* tool_colors; const std::vector<ColorRGBA> *tool_colors;
Vec2f wipe_tower_pos; Vec2f wipe_tower_pos;
float wipe_tower_angle; float wipe_tower_angle;
static ColorRGBA color_support() { return ColorRGBA::GREENISH(); } 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 new_volume = [this, &new_volume_mutex](const ColorRGBA& color) {
auto *volume = new GLVolume(color); auto *volume = new GLVolume(color);
volume->is_extrusion_path = true; 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; tbb::spin_mutex::scoped_lock lock;
lock.acquire(new_volume_mutex); lock.acquire(new_volume_mutex);
m_volumes.volumes.emplace_back(volume); 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<size_t>& range) { [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
// Bounding box of this slab of a wipe tower. // Bounding box of this slab of a wipe tower.
GLVolumePtrs vols; GLVolumePtrs vols;
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
std::vector<GLModel::Geometry> geometries;
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (ctxt.color_by_tool()) { 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))); 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()) }; 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) for (GLVolume *volume : vols)
// Reserving number of vertices (3x position + 3x color) // Reserving number of vertices (3x position + 3x color)
volume->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6); 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) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.tool_change(idx_layer); const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.tool_change(idx_layer);
for (size_t i = 0; i < vols.size(); ++i) { for (size_t i = 0; i < vols.size(); ++i) {
GLVolume &vol = *vols[i]; GLVolume &vol = *vols[i];
if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) { if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) {
vol.print_zs.emplace_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.quad_indices.size());
vol.offsets.emplace_back(vol.indexed_vertex_array.triangle_indices.size()); vol.offsets.emplace_back(vol.indexed_vertex_array.triangle_indices.size());
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
} }
} }
for (const WipeTower::ToolChangeResult &extrusions : layer) { for (const WipeTower::ToolChangeResult &extrusions : layer) {
@ -6512,21 +6808,42 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con
e_prev = e; 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, _3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z,
*vols[ctxt.volume_idx(e.tool, 0)]); *vols[ctxt.volume_idx(e.tool, 0)]);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
} }
} }
} }
for (size_t i = 0; i < vols.size(); ++i) { for (size_t i = 0; i < vols.size(); ++i) {
GLVolume &vol = *vols[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) { if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
vols[i] = new_volume(vol.color); vols[i] = new_volume(vol.color);
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
reserve_new_volume_finalize_old_volume(*vols[i], vol, false); 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) for (GLVolume *vol : vols)
vol->indexed_vertex_array.shrink_to_fit(); 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(); 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. // 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) { for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) {
GLVolume* v = m_volumes.volumes[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->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); 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(); 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)); m_volumes.volumes.emplace_back(new GLVolume(color));
GLVolume& v = *m_volumes.volumes.back(); GLVolume& v = *m_volumes.volumes.back();
#if ENABLE_SMOOTH_NORMALS #if ENABLE_SMOOTH_NORMALS
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.model.init_from(mesh, true);
#else
v.indexed_vertex_array.load_mesh(mesh, true); v.indexed_vertex_array.load_mesh(mesh, true);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#else
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.model.init_from(mesh);
#else #else
v.indexed_vertex_array.load_mesh(mesh); v.indexed_vertex_array.load_mesh(mesh);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#endif // ENABLE_SMOOTH_NORMALS #endif // ENABLE_SMOOTH_NORMALS
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.indexed_vertex_array.finalize_geometry(m_initialized); v.indexed_vertex_array.finalize_geometry(m_initialized);
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled; v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled;
v.composite_id.volume_id = volume_id; v.composite_id.volume_id = volume_id;
v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0.0)); v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0.0));

View File

@ -335,6 +335,16 @@ class GLCanvas3D
struct SlaCap struct SlaCap
{ {
#if ENABLE_GLBEGIN_GLEND_REMOVAL
struct Triangles
{
GLModel object;
GLModel supports;
};
typedef std::map<unsigned int, Triangles> ObjectIdToModelsMap;
double z;
ObjectIdToModelsMap triangles;
#else
struct Triangles struct Triangles
{ {
Pointf3s object; Pointf3s object;
@ -343,6 +353,7 @@ class GLCanvas3D
typedef std::map<unsigned int, Triangles> ObjectIdToTrianglesMap; typedef std::map<unsigned int, Triangles> ObjectIdToTrianglesMap;
double z; double z;
ObjectIdToTrianglesMap triangles; ObjectIdToTrianglesMap triangles;
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
SlaCap() { reset(); } SlaCap() { reset(); }
void reset() { z = DBL_MAX; triangles.clear(); } void reset() { z = DBL_MAX; triangles.clear(); }
@ -473,7 +484,7 @@ private:
std::array<ClippingPlane, 2> m_clipping_planes; std::array<ClippingPlane, 2> m_clipping_planes;
ClippingPlane m_camera_clipping_plane; ClippingPlane m_camera_clipping_plane;
bool m_use_clipping_planes; bool m_use_clipping_planes;
SlaCap m_sla_caps[2]; std::array<SlaCap, 2> m_sla_caps;
std::string m_sidebar_field; std::string m_sidebar_field;
// when true renders an extra frame by not resetting m_dirty to false // when true renders an extra frame by not resetting m_dirty to false
// see request_extra_frame() // 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) // I just don't want to do it now before a release (Lukas Matena 24.3.2019)
bool m_render_sla_auxiliaries; bool m_render_sla_auxiliaries;
std::string m_color_by;
bool m_reload_delayed; bool m_reload_delayed;
#if ENABLE_RENDER_PICKING_PASS #if ENABLE_RENDER_PICKING_PASS
@ -681,8 +690,6 @@ public:
bool get_use_clipping_planes() const { return m_use_clipping_planes; } bool get_use_clipping_planes() const { return m_use_clipping_planes; }
const std::array<ClippingPlane, 2> &get_clipping_planes() const { return m_clipping_planes; }; const std::array<ClippingPlane, 2> &get_clipping_planes() const { return m_clipping_planes; };
void set_color_by(const std::string& value);
void refresh_camera_scene_box(); void refresh_camera_scene_box();
BoundingBoxf3 volumes_bounding_box() const; BoundingBoxf3 volumes_bounding_box() const;

View File

@ -8,15 +8,56 @@
#include "libslic3r/TriangleMesh.hpp" #include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/Model.hpp" #include "libslic3r/Model.hpp"
#include "libslic3r/Polygon.hpp" #include "libslic3r/Polygon.hpp"
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#include "libslic3r/BuildVolume.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp"
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#include <boost/filesystem/operations.hpp> #include <boost/filesystem/operations.hpp>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_SMOOTH_NORMALS
#include <igl/per_face_normals.h>
#include <igl/per_corner_normals.h>
#include <igl/per_vertex_normals.h>
#endif // ENABLE_SMOOTH_NORMALS
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#include <GL/glew.h> #include <GL/glew.h>
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_SMOOTH_NORMALS
static void smooth_normals_corner(const TriangleMesh& mesh, std::vector<stl_normal>& normals)
{
using MapMatrixXfUnaligned = Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
using MapMatrixXiUnaligned = Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
std::vector<Vec3f> face_normals = its_face_normals(mesh.its);
Eigen::MatrixXd vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(),
Eigen::Index(mesh.its.vertices.size()), 3).cast<double>();
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<double>();
Eigen::MatrixXd out_normals;
igl::per_corner_normals(vertices, indices, in_normals, 1.0, out_normals);
normals = std::vector<stl_normal>(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<float>();
}
}
}
#endif // ENABLE_SMOOTH_NORMALS
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_GLBEGIN_GLEND_REMOVAL #if ENABLE_GLBEGIN_GLEND_REMOVAL
void GLModel::Geometry::reserve_vertices(size_t vertices_count) 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) }; 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 unsigned int GLModel::Geometry::extract_uint_index(size_t id) const
{ {
if (format.index_type != EIndexType::UINT) { if (format.index_type != EIndexType::UINT) {
@ -219,7 +291,7 @@ unsigned int GLModel::Geometry::extract_uint_index(size_t id) const
return -1; return -1;
} }
unsigned int ret = -1; unsigned int ret = (unsigned int)-1;
::memcpy(&ret, indices.data() + id * index_stride_bytes(format), sizeof(unsigned int)); ::memcpy(&ret, indices.data() + id * index_stride_bytes(format), sizeof(unsigned int));
return ret; return ret;
} }
@ -236,11 +308,23 @@ unsigned short GLModel::Geometry::extract_ushort_index(size_t id) const
return -1; return -1;
} }
unsigned short ret = -1; unsigned short ret = (unsigned short)-1;
::memcpy(&ret, indices.data() + id * index_stride_bytes(format), sizeof(unsigned short)); ::memcpy(&ret, indices.data() + id * index_stride_bytes(format), sizeof(unsigned short));
return ret; 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<float>::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) size_t GLModel::Geometry::vertex_stride_floats(const Format& format)
{ {
switch (format.vertex_layout) 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) bool GLModel::Geometry::has_position(const Format& format)
{ {
switch (format.vertex_layout) switch (format.vertex_layout)
@ -456,10 +545,58 @@ void GLModel::init_from(const Geometry& data)
} }
#if ENABLE_GLBEGIN_GLEND_REMOVAL #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<stl_normal> 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<double>());
}
}
else
init_from(mesh.its);
}
#else
void GLModel::init_from(const TriangleMesh& mesh) void GLModel::init_from(const TriangleMesh& mesh)
{ {
init_from(mesh.its); init_from(mesh.its);
} }
#endif // ENABLE_SMOOTH_NORMALS
void GLModel::init_from(const indexed_triangle_set& its) void GLModel::init_from(const indexed_triangle_set& its)
#else #else
@ -479,21 +616,24 @@ void GLModel::init_from(const indexed_triangle_set& its, const BoundingBoxf3 &bb
} }
Geometry& data = m_render_data.geometry; 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_vertices(3 * its.indices.size());
data.reserve_indices(3 * its.indices.size()); data.reserve_indices(3 * its.indices.size());
// vertices + indices // vertices + indices
unsigned int vertices_counter = 0; unsigned int vertices_counter = 0;
for (uint32_t i = 0; i < its.indices.size(); ++i) { for (uint32_t i = 0; i < its.indices.size(); ++i) {
stl_triangle_vertex_indices face = its.indices[i]; const stl_triangle_vertex_indices face = its.indices[i];
stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] }; const 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_vertex n = face_normal_normalized(vertex);
for (size_t j = 0; j < 3; ++j) { for (size_t j = 0; j < 3; ++j) {
data.add_vertex(vertex[j], n); data.add_vertex(vertex[j], n);
} }
vertices_counter += 3; 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 // update bounding box
@ -716,6 +856,9 @@ void GLModel::render()
void GLModel::render() const void GLModel::render() const
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL #endif // ENABLE_GLBEGIN_GLEND_REMOVAL
{ {
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
render(std::make_pair<size_t, size_t>(0, indices_count()));
#else
GLShaderProgram* shader = wxGetApp().get_current_shader(); GLShaderProgram* shader = wxGetApp().get_current_shader();
#if ENABLE_GLBEGIN_GLEND_REMOVAL #if ENABLE_GLBEGIN_GLEND_REMOVAL
@ -804,8 +947,71 @@ void GLModel::render() const
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
} }
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL #endif // ENABLE_GLBEGIN_GLEND_REMOVAL
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
} }
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void GLModel::render(const std::pair<size_t, size_t>& 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 #if ENABLE_GLBEGIN_GLEND_REMOVAL
void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count) void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count)
#else #else
@ -1022,6 +1228,62 @@ static void append_triangle(GLModel::Geometry& data, unsigned short v1, unsigned
} }
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL #endif // ENABLE_GLBEGIN_GLEND_REMOVAL
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
template<typename Fn>
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<Vec3d> build_volume = volume.bounding_volume().inflated(epsilon);
if (volume.max_print_height() == 0.0)
build_volume.max.z() = std::numeric_limits<double>::max();
if (ignore_bottom)
build_volume.min.z() = -std::numeric_limits<double>::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<float>(circle.center);
const float r = unscaled<double>(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<double>()); }) :
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<double>()) && 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) GLModel::Geometry stilized_arrow(unsigned short resolution, float tip_radius, float tip_height, float stem_radius, float stem_height)
{ {
#if !ENABLE_GLBEGIN_GLEND_REMOVAL #if !ENABLE_GLBEGIN_GLEND_REMOVAL

View File

@ -14,6 +14,9 @@ namespace Slic3r {
class TriangleMesh; class TriangleMesh;
class Polygon; class Polygon;
using Polygons = std::vector<Polygon>; using Polygons = std::vector<Polygon>;
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
class BuildVolume;
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
namespace GUI { namespace GUI {
@ -83,11 +86,18 @@ namespace GUI {
void reserve_vertices(size_t vertices_count); void reserve_vertices(size_t vertices_count);
void reserve_indices(size_t indices_count); void reserve_indices(size_t indices_count);
void add_vertex(const Vec2f& position); void add_vertex(const Vec2f& position); // EVertexLayout::P2
void add_vertex(const Vec2f& position, const Vec2f& tex_coord); void add_vertex(const Vec2f& position, const Vec2f& tex_coord); // EVertexLayout::P2T2
void add_vertex(const Vec3f& position); void add_vertex(const Vec3f& position); // EVertexLayout::P3
void add_vertex(const Vec3f& position, const Vec2f& tex_coord); void add_vertex(const Vec3f& position, const Vec2f& tex_coord); // EVertexLayout::P3T2
void add_vertex(const Vec3f& position, const Vec3f& normal); 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_ushort_index(unsigned short id);
void add_uint_index(unsigned int id); void add_uint_index(unsigned int id);
@ -106,7 +116,11 @@ namespace GUI {
unsigned int extract_uint_index(size_t id) const; unsigned int extract_uint_index(size_t id) const;
unsigned short extract_ushort_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 vertices_count() const { return vertices.size() / vertex_stride_floats(format); }
size_t indices_count() const { return indices.size() / index_stride_bytes(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 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_position(const Format& format);
static bool has_normal(const Format& format); static bool has_normal(const Format& format);
static bool has_tex_coord(const Format& format); static bool has_tex_coord(const Format& format);
@ -177,6 +193,16 @@ namespace GUI {
std::vector<RenderData> m_render_data; std::vector<RenderData> m_render_data;
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL #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; BoundingBoxf3 m_bounding_box;
std::string m_filename; 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); } 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); 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); void init_from(const TriangleMesh& mesh);
#endif // ENABLE_SMOOTH_NORMALS
#else #else
void init_from(const Geometry& data); void init_from(const Geometry& data);
void init_from(const indexed_triangle_set& its, const BoundingBoxf3& bbox); void init_from(const indexed_triangle_set& its, const BoundingBoxf3& bbox);
@ -217,9 +251,15 @@ namespace GUI {
void reset(); void reset();
#if ENABLE_GLBEGIN_GLEND_REMOVAL #if ENABLE_GLBEGIN_GLEND_REMOVAL
void render(); void render();
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void render(const std::pair<size_t, size_t>& range);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void render_instanced(unsigned int instances_vbo, unsigned int instances_count); void render_instanced(unsigned int instances_vbo, unsigned int instances_count);
bool is_initialized() const { return vertices_count() > 0 && indices_count() > 0; } 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 #else
void render() const; void render() const;
void render_instanced(unsigned int instances_vbo, unsigned int instances_count) 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 BoundingBoxf3& get_bounding_box() const { return m_bounding_box; }
const std::string& get_filename() const { return m_filename; } 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: private:
#if ENABLE_GLBEGIN_GLEND_REMOVAL #if ENABLE_GLBEGIN_GLEND_REMOVAL
bool send_to_gpu(); bool send_to_gpu();
@ -238,6 +301,10 @@ namespace GUI {
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL #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 // 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 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 // the arrow has its axis of symmetry along the Z axis and is pointing upward

View File

@ -98,7 +98,7 @@ namespace GUI {
color[1] = (m_state == Select) ? 1.0f : 0.3f; color[1] = (m_state == Select) ? 1.0f : 0.3f;
color[2] = 0.3f; color[2] = 0.3f;
glsafe(::glColor3fv(color)); glsafe(::glColor3fv(color));
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL #endif // !ENABLE_GLBEGIN_GLEND_REMOVAL
glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_DEPTH_TEST));

View File

@ -496,7 +496,7 @@ static const FileWildcards file_wildcards_by_type[FT_SIZE] = {
/* FT_TEX */ { "Texture"sv, { ".png"sv, ".svg"sv } }, /* 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. // This function produces a Win32 file dialog file template mask to be consumed by wxWidgets on all platforms.

View File

@ -274,9 +274,13 @@ static void generate_thumbnail_from_model(const std::string& filename)
GLVolumeCollection volumes; GLVolumeCollection volumes;
volumes.volumes.push_back(new GLVolume()); 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.load_mesh(model.mesh());
volume->indexed_vertex_array.finalize_geometry(true); volume->indexed_vertex_array.finalize_geometry(true);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume->set_instance_transformation(model.objects[0]->instances[0]->get_transformation()); volume->set_instance_transformation(model.objects[0]->instances[0]->get_transformation());
volume->set_volume_transformation(model.objects[0]->volumes[0]->get_transformation()); volume->set_volume_transformation(model.objects[0]->volumes[0]->get_transformation());

View File

@ -395,14 +395,13 @@ void GLGizmoFlatten::update_planes()
for (auto& plane : m_planes) { for (auto& plane : m_planes) {
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
GLModel::Geometry init_data; 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, GLModel::Geometry::index_type(plane.vertices.size()) };
init_data.format = { GLModel::Geometry::EPrimitiveType::TriangleFan, GLModel::Geometry::EVertexLayout::P3N3, index_type };
init_data.reserve_vertices(plane.vertices.size()); init_data.reserve_vertices(plane.vertices.size());
init_data.reserve_indices(plane.vertices.size()); init_data.reserve_indices(plane.vertices.size());
// vertices + indices // vertices + indices
for (size_t i = 0; i < plane.vertices.size(); ++i) { for (size_t i = 0; i < plane.vertices.size(); ++i) {
init_data.add_vertex((Vec3f)plane.vertices[i].cast<float>(), (Vec3f)plane.normal.cast<float>()); init_data.add_vertex((Vec3f)plane.vertices[i].cast<float>(), (Vec3f)plane.normal.cast<float>());
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); init_data.add_ushort_index((unsigned short)i);
else else
init_data.add_uint_index((unsigned int)i); init_data.add_uint_index((unsigned int)i);

View File

@ -1254,8 +1254,7 @@ void TriangleSelectorGUI::update_paint_contour()
GLModel::Geometry init_data; GLModel::Geometry init_data;
const std::vector<Vec2i> contour_edges = this->get_seed_fill_contour(); const std::vector<Vec2i> 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, GLModel::Geometry::index_type(2 * contour_edges.size()) };
init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, index_type };
init_data.reserve_vertices(2 * contour_edges.size()); init_data.reserve_vertices(2 * contour_edges.size());
init_data.reserve_indices(2 * contour_edges.size()); init_data.reserve_indices(2 * contour_edges.size());
// vertices + indices // 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(0)].v);
init_data.add_vertex(m_vertices[edge(1)].v); init_data.add_vertex(m_vertices[edge(1)].v);
vertices_count += 2; 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); init_data.add_ushort_line((unsigned short)vertices_count - 2, (unsigned short)vertices_count - 1);
else else
init_data.add_uint_line(vertices_count - 2, vertices_count - 1); init_data.add_uint_line(vertices_count - 2, vertices_count - 1);

View File

@ -172,15 +172,14 @@ void GLGizmoRotate::on_render()
if (shader != nullptr) { if (shader != nullptr) {
shader->start_using(); shader->start_using();
const float radius = Offset + m_parent.get_selection().get_bounding_box().radius(); const bool radius_changed = std::abs(m_old_radius - m_radius) > EPSILON;
const bool radius_changed = std::abs(m_old_radius - radius) > EPSILON; m_old_radius = m_radius;
m_old_radius = radius;
ColorRGBA color((m_hover_id != -1) ? m_drag_color : m_highlight_color); ColorRGBA color((m_hover_id != -1) ? m_drag_color : m_highlight_color);
render_circle(color, radius_changed); render_circle(color, radius_changed);
if (m_hover_id != -1) { if (m_hover_id != -1) {
const bool hover_radius_changed = std::abs(m_old_hover_radius - radius) > EPSILON; const bool hover_radius_changed = std::abs(m_old_hover_radius - m_radius) > EPSILON;
m_old_hover_radius = radius; m_old_hover_radius = m_radius;
render_scale(color, hover_radius_changed); render_scale(color, hover_radius_changed);
render_snap_radii(color, hover_radius_changed); render_snap_radii(color, hover_radius_changed);

View File

@ -129,6 +129,14 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
if (! has_points && ! has_holes) if (! has_points && ! has_holes)
return; 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"); GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light");
if (shader != nullptr) if (shader != nullptr)
shader->start_using(); shader->start_using();
@ -136,6 +144,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
if (shader != nullptr) if (shader != nullptr)
shader->stop_using(); shader->stop_using();
}); });
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); 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(); 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 #if ENABLE_GLBEGIN_GLEND_REMOVAL
m_cone.set_color(render_color); m_cone.set_color(render_color);
m_sphere.set_color(render_color); m_sphere.set_color(render_color);
if (!picking)
#else #else
m_cone.set_color(-1, render_color); m_cone.set_color(-1, render_color);
m_sphere.set_color(-1, render_color); m_sphere.set_color(-1, render_color);
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
if (shader && !picking) if (shader && !picking)
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
shader->set_uniform("emission_factor", 0.5f); 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. // 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); m_cylinder.set_color(render_color);
#else #else
m_cylinder.set_color(-1, render_color); m_cylinder.set_color(-1, render_color);
if (shader != nu)
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL #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) { for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) {
if (is_mesh_point_clipped(drain_hole.pos.cast<double>())) if (is_mesh_point_clipped(drain_hole.pos.cast<double>()))
continue; continue;

View File

@ -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) // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); const 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_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y);
if (fb_width == 0 || fb_height == 0) if (fb_width == 0 || fb_height == 0)
return; return;
draw_data->ScaleClipRects(io.DisplayFramebufferScale); draw_data->ScaleClipRects(io.DisplayFramebufferScale);
@ -1488,8 +1488,7 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data)
// Render command lists // Render command lists
ImVec2 pos = draw_data->DisplayPos; 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 ImDrawList* cmd_list = draw_data->CmdLists[n];
const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;
const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.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(::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)))); 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]; const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback) if (pcmd->UserCallback)
{
// User callback (registered via ImDrawList::AddCallback) // User callback (registered via ImDrawList::AddCallback)
pcmd->UserCallback(cmd_list, pcmd); 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); 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 // 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))); 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)));

View File

@ -1,5 +1,6 @@
#include "SLAImportJob.hpp" #include "SLAImportJob.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/Format/SL1.hpp" #include "libslic3r/Format/SL1.hpp"
#include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI.hpp"

View File

@ -189,8 +189,7 @@ void MeshClipper::recalculate_triangles()
m_model.reset(); m_model.reset();
GLModel::Geometry init_data; 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, GLModel::Geometry::index_type(m_triangles2d.size()) };
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, index_type };
init_data.reserve_vertices(m_triangles2d.size()); init_data.reserve_vertices(m_triangles2d.size());
init_data.reserve_indices(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<float>(), (Vec3f)up.cast<float>()); init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>()); init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
const size_t idx = it - m_triangles2d.cbegin(); 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); init_data.add_ushort_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2);
else else
init_data.add_uint_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2); init_data.add_uint_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2);

View File

@ -3,6 +3,7 @@
#include "libslic3r/Point.hpp" #include "libslic3r/Point.hpp"
#include "libslic3r/Geometry.hpp" #include "libslic3r/Geometry.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/SLA/IndexedMesh.hpp" #include "libslic3r/SLA/IndexedMesh.hpp"
#include "admesh/stl.h" #include "admesh/stl.h"

View File

@ -245,7 +245,7 @@ public:
GetBtnsListCtrl()->Rescale(); GetBtnsListCtrl()->Rescale();
} }
void Notebook::OnNavigationKey(wxNavigationKeyEvent& event) void OnNavigationKey(wxNavigationKeyEvent& event)
{ {
if (event.IsWindowChange()) { if (event.IsWindowChange()) {
// change pages // change pages

View File

@ -233,8 +233,9 @@ OpenGLManager::~OpenGLManager()
bool OpenGLManager::init_gl() bool OpenGLManager::init_gl()
{ {
if (!m_gl_initialized) { if (!m_gl_initialized) {
if (glewInit() != GLEW_OK) { GLenum err = glewInit();
BOOST_LOG_TRIVIAL(error) << "Unable to init glew library"; if (err != GLEW_OK) {
BOOST_LOG_TRIVIAL(error) << "Unable to init glew library: " << glewGetErrorString(err);
return false; return false;
} }
m_gl_initialized = true; m_gl_initialized = true;

View File

@ -4,4 +4,6 @@ target_link_libraries(${_TEST_NAME}_tests test_common libnest2d )
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") # 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})

View File

@ -3,9 +3,14 @@ add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp
sla_print_tests.cpp sla_print_tests.cpp
sla_test_utils.hpp sla_test_utils.cpp sla_test_utils.hpp sla_test_utils.cpp
sla_supptgen_tests.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) target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") 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}: ") # catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${CATCH_EXTRA_ARGS}) add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${CATCH_EXTRA_ARGS})

View File

@ -0,0 +1,40 @@
#include <catch2/catch.hpp>
#include <test_utils.hpp>
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/Format/SLAArchive.hpp"
#include <boost/filesystem.hpp>
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));
}
}

View File

@ -15,4 +15,6 @@ if (WIN32)
endif() endif()
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") # 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})