mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-06 22:47:32 -06:00

* NEW:add move and rotate gizmo in assemble view Cherry-picked from bambulab/BambuStudio@d9e47bd9a9 Co-authored-by: zhou.xu <zhou.xu@bambulab.com> * Deselect other parts if Alt is pressed when selecting Cherry-picked from bambulab/BambuStudio@f5eb2899e7 Co-authored-by: zhou.xu <zhou.xu@bambulab.com> * FIX:z offset is error after copy and paste several objects jira: STUDIO-6753 STUDIO-7135 Change-Id: I6d9c8eb0c957ff1e3194709704ceb6c3920baa4f (cherry picked from commit 847a7141a6f47e409566b19e73c0ebdeb08f39e2) (cherry picked from commit a5cc52beb7eef5848368e660ca4f14e95ad5f7d5) * FIX:arrow direction in scaling tool is incorrect Jira: STUDIO-5672 Change-Id: I82c0ab336805e34c8380f93e64d3b9dbbf283805 (cherry picked from commit f6f27b700f0305854fcdbcb1191af25a4b8bdbe4) * FIX:world cs is displayed incorrectly The value of world coordinate system for model_volume is displayed incorrectly Jira: STUDIO-6399 code is from PrusaSlicer thanks for PrusaSlicer and enricoturri1966 commit 325709c5ae9b937867b36103a41d12a102c99292 Author: enricoturri1966 <enricoturri@seznam.cz> Date: Thu Jan 26 15:49:00 2023 +0100 SPE-1419 - Fixed reset skew resetting mirror, reset scale resetting mirror, changed labels in Object Manipulator panel, scale of instances using the Object Manipulator panel always made as absolute Change-Id: I30fdd39effd73b8dc027e4263fa7e64937b84326 Cherry-picked from bambulab/BambuStudio@0b46b9848b Co-authored-by: enricoturri1966 <enricoturri@seznam.cz> * FIX:fix scale problem add tool tip for move,rotate,scale gizmo Jira: STUDIO-6425 STUDIO-6419 Change-Id: I0b89c9b70f83cde21c6a407bcecd78c925515cfa Cherry-picked from bambulab/BambuStudio@6dad59102b Co-authored-by: zhou.xu <zhou.xu@bambulab.com> * NEW:add Object coordinates in move gizmo jira: none Part of the code references PrusaSlicer,thanks for PrusaSlicer and enricoturri1966 commit c12eeee12f9e2c91a9dfe7905f1370143805f038 Author: enricoturri1966 <enricoturri@seznam.cz> Date: Mon Oct 2 14:26:36 2023 +0200 SPE-1926: Tech ENABLE_CGAL_BOUNDING_SPHERE - Use selection's bounding sphere center as pivot for rotations Change-Id: Iae7e4539c198af3ff1aa99e1c0ce015fbcf80256 (cherry picked from commit 2b73bc915ee27218c9803ba0a01b0d3e47adf1da) Cherry-picked from bambulab/BambuStudio@98cce3b656 Co-authored-by: enricoturri1966 <enricoturri@seznam.cz> Co-authored-by: zhou.xu <zhou.xu@bambulab.com> * FIX:fix imgui style at Object coordinate in move tool jira:STUDIO-7141 Change-Id: Ib2900012c28878c4e7ad97eb0cf319f693cb9f6f (cherry picked from commit b7b09c82897678c4f3615713bc5d1cc7a3b17b19) (cherry picked from commit c89732a04619a6d910b723c126515bae802f7167) * ENH:use local cs for non_model_part better jira: STUDIO-7234 Change-Id: I0f0e99429e5e0b7cc4932a661eceffcff4a495f6 (cherry picked from commit b4305a3bfc9e5ae05c1785a710238a70f2dfb44a) (cherry picked from commit b28ac4f812f0024ec619c5d1b3c96e4cef4debdb) * ENH:add a cross mark for object cs jira: STUDIO-6947 Change-Id: Iaaab4f072045756ac3ba12c3f65e1c7f04ba65b8 (cherry picked from commit a2a2f49b4d94f257d36c9d17b4ec952e5dc9f0eb) Cherry-picked from bambulab/BambuStudio@8400e162a7 Co-authored-by: zhou.xu <zhou.xu@bambulab.com> * NEW:add tip button for move,rotate,scale jira: STUDIO-7273 Change-Id: I44aeecd8aaa17ec49ac1d8ff2bee5c3729c52061 (cherry picked from commit 998f33b4ce588f59cef345e327a97f6f669f6089) (cherry picked from commit f5eb2899e7252ea3ff0f8a79ef8d55c6009ebb28) * FIX:scale and size sholud >0 in scale tool jira: STUDIO-7433 Change-Id: Ibd4d00d9ca4762d002049e97a6d0819649f464db (cherry picked from commit eaaf11031ee49009af14abbd05bb4a07c88aceda) (cherry picked from commit 0d393d64b804ba7ae05454bf158de470cc74a6a6) * Fix crossmark rendering * Use combox as coord selection Cherry-picked from bambulab/BambuStudio@56f628dac1 Co-authored-by: zhou.xu <zhou.xu@bambulab.com> * NEW:add "world coordinates" scale for scale gizmo upgrade Transformation class jira:none about 75% code is from PrusaSlicer,thanks for PrusaSlicer and enricoturri1966 commit b32e9366606dce7d4f8de8db84fd902113bdbe28 Author: enricoturri1966 <enricoturri@seznam.cz> Date: Tue Mar 7 14:32:18 2023 +0100 Rework of constrained scaling Change-Id: I1248ea586e6b8f2fb6cdf3aa901ed7f525c3f111 (cherry picked from commit e10381aad1412b0c47afa340b634faa3af9d1a1f) Cherry-picked from bambulab/BambuStudio@c0536c09b4 Co-authored-by: enricoturri1966 <enricoturri@seznam.cz> Co-authored-by: zhou.xu <zhou.xu@bambulab.com> * ENH:set "Rotate (relative)" jira:none code is from PrusaSlicer,thanks for PrusaSlicer and enricoturri1966 commit 243985173e70c189ad9a86eefaaea0757d9749cb Author: enricoturri1966 <enricoturri@seznam.cz> Date: Thu May 12 14:33:41 2022 +0200 Tech ENABLE_TRANSFORMATIONS_BY_MATRICES - Allow for relative rotations Change-Id: I851939093ffb6881542fb21b434e17cc31a6dab2 (cherry picked from commit e412fa3492fa2ef59b84a84be1ede80935fb8a8d) * FIX:limit scaling ratio by grabber in scale tool jira: none Change-Id: I20a4404d4e4025ae230ab46ba8d8d3e5ffed10e3 (cherry picked from commit 97f63f167e80e859fec49666c8986f5a01f61838) * FIX:selection should be not empty when update_ui_from_settings jira: none Change-Id: I74b76733eba03d148dfd70279ec2ba65f19cc39a (cherry picked from commit f402685aee747fe5c3740b2cb80fc2a60e129918) * ENH:add "volume selection" checkbox jira: none Change-Id: I68b5f54e37ea2ab9e2b65ac84abc834060f400df (cherry picked from commit eec7de441bd40408fe688587d2834b0c42c0d66f) * FIX:add can_sequential_clearance_show_in_gizmo api jira: STUDIO-7836 Change-Id: Ie0cded272596bafee4e491e379722dcc23035dc4 (cherry picked from commit 715d2b9b7840939663e99e0ecbfcefd8ecf2904f) * FIX:select all should ban in paint,cut and so on gizmo jira: STUDIO-7872 Change-Id: Ic6496dbdd892814e1fc41625ee34ffc46f171657 (cherry picked from commit 95e8ca728553081db4ecbb3d865c8b999a6ff2fa) * FIX:add wipe tower'position in move gizmo jira: STUDIO-7861 Change-Id: I8147717bc61ba06a7e1fba45532cdadc2ba1174e (cherry picked from commit 065dddb890d3ec81643b9767397bdad72ae69ebd) * ENH:fix text coordinate system calculation jira: STUDIO-6449 Change-Id: I36214c14c348e8f52b96501cd027205819b0dabc (cherry picked from commit 44287812a0cb212f1bf6fe70e32e1075f532886d) Cherry-picked from bambulab/BambuStudio@4091f3e042 Co-authored-by: zhou.xu <zhou.xu@bambulab.com> * FIX:delete old selection.translate api jira: STUDIO-8201 code is from PrusaSlicer,thanks for PrusaSlicer and enricoturri1966 commit 88ce6ccdef5f680709ea8b676688784a7af287dd Author: enricoturri1966 <enricoturri@seznam.cz> Date: Wed May 11 10:54:42 2022 +0200 Tech ENABLE_TRANSFORMATIONS_BY_MATRICES - Change-Id: Iafe963f0f7bf9028f32a4fb4a4cc8cc609662283 Change-Id: Ibbc36c004734f35564f0028dd1e537ac926a2f1f Cherry-picked from bambulab/BambuStudio@c6d9f2685e Co-authored-by: enricoturri1966 <enricoturri@seznam.cz> Co-authored-by: zhou.xu <zhou.xu@bambulab.com> * FIX:add protection for null pointer jira: none Change-Id: I9a9231bab893f5d2afa008f65165269ae176c962 (cherry picked from commit f27a713aaf77b1109fc57b8650efa6b23081f799) * FIX:when two dir is perpendicular to each other,scale error (plane_normal.dot(ray_dir)) jira:STUDIO-8274 Change-Id: Ib3145ab75e18c832d20065d204aa41b75f73b673 (cherry picked from commit fbdc9cd580f835d1a873d08ed64baed3b3db6f9a) * ENH:add "reset real zeros" button in rotate gizmo jira: STUDIO-8291 Change-Id: Ia10e4d8a2a3a073c22a1306aeab9ffa3e7b77c2b (cherry picked from commit 738e3f004daa9082709800e4e3d0d9bbe1b7ed7e) * FIX:add "absolute rotation" in rotate gizmo jira: STUDIO-8726 Change-Id: I23deb4ab11cf24ca4f0f0c5a35a74268c34f60f6 (cherry picked from commit d26b8f9fcadf8f7709a302991e43be711560e84e) (cherry picked from commit 496d69f9d1b91c6bd84804e57a276bccf79f0cbd) * Fix tooltip button size * Fix issue that reative rotation history not cleared after gizmo closed * Show selection box in assemble view * ENH:add an tip icon for assembly view jira: STUDIO-7155 Change-Id: Ie9e4fa578c8aa5bda9ff771d82f396f8b51026bb (cherry picked from commit 515f9473347fb912a9dc8c365f1c318506096083) --------- Co-authored-by: zhou.xu <zhou.xu@bambulab.com> Co-authored-by: enricoturri1966 <enricoturri@seznam.cz> Co-authored-by: SoftFever <softfeverever@gmail.com>
1861 lines
78 KiB
C++
1861 lines
78 KiB
C++
#include <GL/glew.h>
|
|
|
|
#include "3DScene.hpp"
|
|
#include "GLShader.hpp"
|
|
#include "GUI_App.hpp"
|
|
#include "GUI_Colors.hpp"
|
|
#include "Plater.hpp"
|
|
#include "BitmapCache.hpp"
|
|
#include "Camera.hpp"
|
|
|
|
#include "libslic3r/BuildVolume.hpp"
|
|
#include "libslic3r/ExtrusionEntity.hpp"
|
|
#include "libslic3r/ExtrusionEntityCollection.hpp"
|
|
#include "libslic3r/Geometry.hpp"
|
|
#include "libslic3r/Print.hpp"
|
|
#include "libslic3r/SLAPrint.hpp"
|
|
#include "libslic3r/Slicing.hpp"
|
|
#include "libslic3r/Format/STL.hpp"
|
|
#include "libslic3r/Utils.hpp"
|
|
#include "libslic3r/AppConfig.hpp"
|
|
#include "libslic3r/PresetBundle.hpp"
|
|
#include "libslic3r/ClipperUtils.hpp"
|
|
#include "libslic3r/Tesselate.hpp"
|
|
#include "libslic3r/PrintConfig.hpp"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include <boost/log/trivial.hpp>
|
|
|
|
#include <boost/filesystem/operations.hpp>
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
|
|
#include <Eigen/Dense>
|
|
|
|
#ifdef HAS_GLSAFE
|
|
void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char* function_name)
|
|
{
|
|
#if defined(NDEBUG)
|
|
// In release mode, only show OpenGL errors if sufficiently high loglevel.
|
|
if (Slic3r::get_logging_level() < 5)
|
|
return;
|
|
#endif // NDEBUG
|
|
|
|
GLenum err = glGetError();
|
|
if (err == GL_NO_ERROR)
|
|
return;
|
|
const char* sErr = 0;
|
|
switch (err) {
|
|
case GL_INVALID_ENUM: sErr = "Invalid Enum"; break;
|
|
case GL_INVALID_VALUE: sErr = "Invalid Value"; break;
|
|
// be aware that GL_INVALID_OPERATION is generated if glGetError is executed between the execution of glBegin and the corresponding execution of glEnd
|
|
case GL_INVALID_OPERATION: sErr = "Invalid Operation"; break;
|
|
case GL_STACK_OVERFLOW: sErr = "Stack Overflow"; break;
|
|
case GL_STACK_UNDERFLOW: sErr = "Stack Underflow"; break;
|
|
case GL_OUT_OF_MEMORY: sErr = "Out Of Memory"; break;
|
|
default: sErr = "Unknown"; break;
|
|
}
|
|
BOOST_LOG_TRIVIAL(error) << "OpenGL error in " << file_name << ":" << line << ", function " << function_name << "() : " << (int)err << " - " << sErr;
|
|
assert(false);
|
|
}
|
|
#endif // HAS_GLSAFE
|
|
|
|
// BBS
|
|
std::vector<Slic3r::ColorRGBA> get_extruders_colors()
|
|
{
|
|
unsigned char rgba_color[4] = {};
|
|
std::vector<std::string> colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config();
|
|
std::vector<Slic3r::ColorRGBA> colors_out(colors.size());
|
|
for (const std::string &color : colors) {
|
|
Slic3r::GUI::BitmapCache::parse_color4(color, rgba_color);
|
|
size_t color_idx = &color - &colors.front();
|
|
colors_out[color_idx] = {
|
|
float(rgba_color[0]) / 255.f,
|
|
float(rgba_color[1]) / 255.f,
|
|
float(rgba_color[2]) / 255.f,
|
|
float(rgba_color[3]) / 255.f,
|
|
};
|
|
}
|
|
return colors_out;
|
|
}
|
|
|
|
float FullyTransparentMaterialThreshold = 0.1f;
|
|
float FullTransparentModdifiedToFixAlpha = 0.3f;
|
|
// Be careful changing this value because it could break thumbnail color due to rounding error!
|
|
// The color rendering on BambuLab's "send to printer" screen relies on the assumption that this color can be accurately rendered by OpenGL,
|
|
// value like 0.18f could not because in C++ (int)(0.18f * 255) == 45 however in OpenGL it renders this as 46
|
|
// which breaks the `SelectMachineDialog::record_edge_pixels_data()` function!
|
|
float FULL_BLACK_THRESHOLD = 0.2f;
|
|
|
|
Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::ColorRGBA &colors)
|
|
{
|
|
if (colors.a() < FullyTransparentMaterialThreshold) { // completely transparent
|
|
return {1, 1, 1, FullTransparentModdifiedToFixAlpha};
|
|
}
|
|
else if(colors.r() < FULL_BLACK_THRESHOLD && colors.g() < FULL_BLACK_THRESHOLD && colors.b() < FULL_BLACK_THRESHOLD) { // black
|
|
return {FULL_BLACK_THRESHOLD, FULL_BLACK_THRESHOLD, FULL_BLACK_THRESHOLD, colors.a()};
|
|
}
|
|
else
|
|
return colors;
|
|
}
|
|
|
|
namespace Slic3r {
|
|
|
|
|
|
const float GLVolume::SinkingContours::HalfWidth = 0.25f;
|
|
|
|
void GLVolume::SinkingContours::render()
|
|
{
|
|
update();
|
|
|
|
GLShaderProgram* shader = GUI::wxGetApp().get_current_shader();
|
|
if (shader == nullptr)
|
|
return;
|
|
|
|
const GUI::Camera& camera = GUI::wxGetApp().plater()->get_camera();
|
|
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::assemble_transform(m_shift));
|
|
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
|
m_model.render();
|
|
}
|
|
|
|
void GLVolume::SinkingContours::update()
|
|
{
|
|
const int object_idx = m_parent.object_idx();
|
|
const Model& model = GUI::wxGetApp().plater()->model();
|
|
|
|
if (0 <= object_idx && object_idx < int(model.objects.size()) && m_parent.is_sinking() && !m_parent.is_below_printbed()) {
|
|
const BoundingBoxf3& box = m_parent.transformed_convex_hull_bounding_box();
|
|
if (!m_old_box.size().isApprox(box.size()) || m_old_box.min.z() != box.min.z()) {
|
|
m_old_box = box;
|
|
m_shift = Vec3d::Zero();
|
|
|
|
const TriangleMesh& mesh = model.objects[object_idx]->volumes[m_parent.volume_idx()]->mesh();
|
|
|
|
m_model.reset();
|
|
GUI::GLModel::Geometry init_data;
|
|
init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 };
|
|
init_data.color = ColorRGBA::WHITE();
|
|
unsigned int vertices_counter = 0;
|
|
MeshSlicingParams slicing_params;
|
|
slicing_params.trafo = m_parent.world_matrix();
|
|
const Polygons polygons = union_(slice_mesh(mesh.its, 0.0f, slicing_params));
|
|
for (const ExPolygon& expoly : diff_ex(expand(polygons, float(scale_(HalfWidth))), shrink(polygons, float(scale_(HalfWidth))))) {
|
|
const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(expoly);
|
|
init_data.reserve_vertices(init_data.vertices_count() + triangulation.size());
|
|
init_data.reserve_indices(init_data.indices_count() + triangulation.size());
|
|
for (const Vec3d& v : triangulation) {
|
|
init_data.add_vertex((Vec3f)(v.cast<float>() + 0.015f * Vec3f::UnitZ())); // add a small positive z to avoid z-fighting
|
|
++vertices_counter;
|
|
if (vertices_counter % 3 == 0)
|
|
init_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
|
|
}
|
|
}
|
|
m_model.init_from(std::move(init_data));
|
|
}
|
|
else
|
|
m_shift = box.center() - m_old_box.center();
|
|
}
|
|
else
|
|
m_model.reset();
|
|
}
|
|
|
|
ColorRGBA GLVolume::DISABLED_COLOR = ColorRGBA::DARK_GRAY();
|
|
ColorRGBA GLVolume::SLA_SUPPORT_COLOR = ColorRGBA::LIGHT_GRAY();
|
|
ColorRGBA GLVolume::SLA_PAD_COLOR = { 0.0f, 0.2f, 0.0f, 1.0f };
|
|
// BBS
|
|
ColorRGBA GLVolume::NEUTRAL_COLOR = { 0.8f, 0.8f, 0.8f, 1.0f };
|
|
ColorRGBA GLVolume::UNPRINTABLE_COLOR = { 0.0f, 0.0f, 0.0f, 0.5f };
|
|
|
|
ColorRGBA GLVolume::MODEL_MIDIFIER_COL = {1.0f, 1.0f, 0.0f, 0.6f};
|
|
ColorRGBA GLVolume::MODEL_NEGTIVE_COL = {0.3f, 0.3f, 0.3f, 0.4f};
|
|
ColorRGBA GLVolume::SUPPORT_ENFORCER_COL = {0.3f, 0.3f, 1.0f, 0.4f};
|
|
ColorRGBA GLVolume::SUPPORT_BLOCKER_COL = {1.0f, 0.3f, 0.3f, 0.4f};
|
|
|
|
ColorRGBA GLVolume::MODEL_HIDDEN_COL = {0.f, 0.f, 0.f, 0.3f};
|
|
|
|
std::array<ColorRGBA, 5> GLVolume::MODEL_COLOR = { {
|
|
{ 1.0f, 1.0f, 0.0f, 1.f },
|
|
{ 1.0f, 0.5f, 0.5f, 1.f },
|
|
{ 0.5f, 1.0f, 0.5f, 1.f },
|
|
{ 0.5f, 0.5f, 1.0f, 1.f },
|
|
{ 1.0f, 1.0f, 0.0f, 1.f }
|
|
} };
|
|
|
|
void GLVolume::update_render_colors()
|
|
{
|
|
GLVolume::DISABLED_COLOR = GUI::ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Model_Disable]);
|
|
GLVolume::NEUTRAL_COLOR = GUI::ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Model_Neutral]);
|
|
GLVolume::MODEL_COLOR[0] = GUI::ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Modifier]);
|
|
GLVolume::MODEL_COLOR[1] = GUI::ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Negtive_Volume]);
|
|
GLVolume::MODEL_COLOR[2] = GUI::ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Support_Enforcer]);
|
|
GLVolume::MODEL_COLOR[3] = GUI::ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Support_Blocker]);
|
|
GLVolume::UNPRINTABLE_COLOR = GUI::ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Model_Unprintable]);
|
|
|
|
}
|
|
|
|
void GLVolume::load_render_colors()
|
|
{
|
|
RenderColor::colors[RenderCol_Model_Disable] = GUI::ImGuiWrapper::to_ImVec4(GLVolume::DISABLED_COLOR);
|
|
RenderColor::colors[RenderCol_Model_Neutral] = GUI::ImGuiWrapper::to_ImVec4(GLVolume::NEUTRAL_COLOR);
|
|
RenderColor::colors[RenderCol_Modifier] = GUI::ImGuiWrapper::to_ImVec4(GLVolume::MODEL_COLOR[0]);
|
|
RenderColor::colors[RenderCol_Negtive_Volume] = GUI::ImGuiWrapper::to_ImVec4(GLVolume::MODEL_COLOR[1]);
|
|
RenderColor::colors[RenderCol_Support_Enforcer] = GUI::ImGuiWrapper::to_ImVec4(GLVolume::MODEL_COLOR[2]);
|
|
RenderColor::colors[RenderCol_Support_Blocker] = GUI::ImGuiWrapper::to_ImVec4(GLVolume::MODEL_COLOR[3]);
|
|
RenderColor::colors[RenderCol_Model_Unprintable] = GUI::ImGuiWrapper::to_ImVec4(GLVolume::UNPRINTABLE_COLOR);
|
|
}
|
|
|
|
GLVolume::GLVolume(float r, float g, float b, float a)
|
|
: m_sla_shift_z(0.0)
|
|
, m_sinking_contours(*this)
|
|
// geometry_id == 0 -> invalid
|
|
, geometry_id(std::pair<size_t, size_t>(0, 0))
|
|
, extruder_id(0)
|
|
, selected(false)
|
|
, disabled(false)
|
|
, printable(true)
|
|
, visible(true)
|
|
, is_active(true)
|
|
, zoom_to_volumes(true)
|
|
, shader_outside_printer_detection_enabled(false)
|
|
, is_outside(false)
|
|
, partly_inside(false)
|
|
, hover(HS_None)
|
|
, is_modifier(false)
|
|
, is_wipe_tower(false)
|
|
, is_extrusion_path(false)
|
|
, force_transparent(false)
|
|
, force_native_color(false)
|
|
, force_neutral_color(false)
|
|
, force_sinking_contours(false)
|
|
, picking(false)
|
|
, tverts_range(0, size_t(-1))
|
|
{
|
|
color = { r, g, b, a };
|
|
set_render_color(color);
|
|
mmuseg_ts = 0;
|
|
}
|
|
|
|
|
|
// BBS
|
|
float GLVolume::explosion_ratio = 1.0;
|
|
float GLVolume::last_explosion_ratio = 1.0;
|
|
|
|
void GLVolume::set_render_color()
|
|
{
|
|
bool outside = is_outside || is_below_printbed();
|
|
|
|
if (force_native_color || force_neutral_color) {
|
|
#ifdef ENABBLE_OUTSIDE_COLOR
|
|
if (outside && shader_outside_printer_detection_enabled)
|
|
set_render_color(OUTSIDE_COLOR);
|
|
else {
|
|
#endif
|
|
if (force_native_color)
|
|
set_render_color(color);
|
|
else
|
|
set_render_color(NEUTRAL_COLOR);
|
|
#ifdef ENABLE_OUTSIDE_COLOR
|
|
}
|
|
#endif
|
|
}
|
|
else {
|
|
/* BBS
|
|
if (hover == HS_Select)
|
|
set_render_color(HOVER_SELECT_COLOR);
|
|
else if (hover == HS_Deselect)
|
|
set_render_color(HOVER_DESELECT_COLOR);
|
|
else if (selected)
|
|
set_render_color(outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR);
|
|
else if (disabled)
|
|
*/
|
|
if (disabled)
|
|
set_render_color(DISABLED_COLOR);
|
|
#ifdef ENABLE_OUTSIDE_COLOR
|
|
else if (is_outside && shader_outside_printer_detection_enabled)
|
|
set_render_color(OUTSIDE_COLOR);
|
|
#endif
|
|
else {
|
|
//to make black not too hard too see
|
|
ColorRGBA new_color = adjust_color_for_rendering(color);
|
|
set_render_color(new_color);
|
|
}
|
|
}
|
|
|
|
if (force_transparent) {
|
|
if (color.a() < FullyTransparentMaterialThreshold) {
|
|
render_color.a(FullTransparentModdifiedToFixAlpha);
|
|
} else {
|
|
render_color.a(color.a());
|
|
}
|
|
}
|
|
|
|
//BBS set unprintable color
|
|
if (!printable) {
|
|
render_color = UNPRINTABLE_COLOR;
|
|
}
|
|
|
|
//BBS set invisible color
|
|
if (!visible) {
|
|
render_color = MODEL_HIDDEN_COL;
|
|
}
|
|
}
|
|
|
|
ColorRGBA color_from_model_volume(const ModelVolume& model_volume)
|
|
{
|
|
ColorRGBA color;
|
|
if (model_volume.is_negative_volume())
|
|
return GLVolume::MODEL_NEGTIVE_COL;
|
|
else if (model_volume.is_modifier())
|
|
#if ENABLE_MODIFIERS_ALWAYS_TRANSPARENT
|
|
return GLVolume::MODEL_MIDIFIER_COL;
|
|
#else
|
|
color = { 0.2f, 1.0f, 0.2f, 1.0f };
|
|
#endif // ENABLE_MODIFIERS_ALWAYS_TRANSPARENT
|
|
else if (model_volume.is_support_blocker())
|
|
return GLVolume::SUPPORT_BLOCKER_COL;
|
|
else if (model_volume.is_support_enforcer())
|
|
return GLVolume::SUPPORT_ENFORCER_COL;
|
|
return color;
|
|
}
|
|
|
|
Transform3d GLVolume::world_matrix() const
|
|
{
|
|
Transform3d m = m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix();
|
|
Vec3d ofs2ass = m_offset_to_assembly * (GLVolume::explosion_ratio - 1.0);
|
|
Vec3d volofs2obj = m_volume_transformation.get_offset() * (GLVolume::explosion_ratio - 1.0);
|
|
|
|
m.translation()(2) += m_sla_shift_z;
|
|
m.translate(ofs2ass + volofs2obj);
|
|
return m;
|
|
}
|
|
|
|
bool GLVolume::is_left_handed() const
|
|
{
|
|
const Vec3d &m1 = m_instance_transformation.get_mirror();
|
|
const Vec3d &m2 = m_volume_transformation.get_mirror();
|
|
return m1.x() * m1.y() * m1.z() * m2.x() * m2.y() * m2.z() < 0.;
|
|
}
|
|
|
|
const BoundingBoxf3& GLVolume::transformed_bounding_box() const
|
|
{
|
|
if (!m_transformed_bounding_box.has_value() || last_explosion_ratio != explosion_ratio) {
|
|
const BoundingBoxf3& box = bounding_box();
|
|
assert(box.defined || box.min.x() >= box.max.x() || box.min.y() >= box.max.y() || box.min.z() >= box.max.z());
|
|
std::optional<BoundingBoxf3>* trans_box = const_cast<std::optional<BoundingBoxf3>*>(&m_transformed_bounding_box);
|
|
*trans_box = box.transformed(world_matrix());
|
|
last_explosion_ratio = explosion_ratio;
|
|
}
|
|
return *m_transformed_bounding_box;
|
|
}
|
|
|
|
const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const
|
|
{
|
|
if (!m_transformed_convex_hull_bounding_box.has_value()) {
|
|
std::optional<BoundingBoxf3>* trans_box = const_cast<std::optional<BoundingBoxf3>*>(&m_transformed_convex_hull_bounding_box);
|
|
*trans_box = transformed_convex_hull_bounding_box(world_matrix());
|
|
}
|
|
return *m_transformed_convex_hull_bounding_box;
|
|
}
|
|
|
|
BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const
|
|
{
|
|
return (m_convex_hull && ! m_convex_hull->empty()) ?
|
|
m_convex_hull->transformed_bounding_box(trafo) :
|
|
bounding_box().transformed(trafo);
|
|
}
|
|
|
|
BoundingBoxf3 GLVolume::transformed_non_sinking_bounding_box(const Transform3d& trafo) const
|
|
{
|
|
return GUI::wxGetApp().plater()->model().objects[object_idx()]->volumes[volume_idx()]->mesh().transformed_bounding_box(trafo, 0.0);
|
|
}
|
|
|
|
const BoundingBoxf3& GLVolume::transformed_non_sinking_bounding_box() const
|
|
{
|
|
if (!m_transformed_non_sinking_bounding_box.has_value()) {
|
|
std::optional<BoundingBoxf3>* trans_box = const_cast<std::optional<BoundingBoxf3>*>(&m_transformed_non_sinking_bounding_box);
|
|
const Transform3d& trafo = world_matrix();
|
|
*trans_box = transformed_non_sinking_bounding_box(trafo);
|
|
}
|
|
return *m_transformed_non_sinking_bounding_box;
|
|
}
|
|
|
|
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];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GLVolume::render()
|
|
{
|
|
if (!is_active)
|
|
return;
|
|
|
|
GLShaderProgram *shader = GUI::wxGetApp().get_current_shader();
|
|
if (shader == nullptr)
|
|
return;
|
|
|
|
ModelObjectPtrs &model_objects = GUI::wxGetApp().model().objects;
|
|
std::vector<ColorRGBA> colors = get_extruders_colors();
|
|
|
|
simple_render(shader, model_objects, colors);
|
|
}
|
|
|
|
//BBS: add outline related logic
|
|
void GLVolume::render_with_outline(const GUI::Size& cnv_size)
|
|
{
|
|
if (!is_active)
|
|
return;
|
|
|
|
GLShaderProgram *shader = GUI::wxGetApp().get_current_shader();
|
|
if (shader == nullptr)
|
|
return;
|
|
|
|
ModelObjectPtrs &model_objects = GUI::wxGetApp().model().objects;
|
|
std::vector<ColorRGBA> colors = get_extruders_colors();
|
|
|
|
const GUI::OpenGLManager::EFramebufferType framebuffers_type = GUI::OpenGLManager::get_framebuffers_type();
|
|
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Unknown) {
|
|
// No supported, degrade to normal rendering
|
|
simple_render(shader, model_objects, colors);
|
|
return;
|
|
}
|
|
|
|
// 1st. render pass, render the model into a separate render target that has only depth buffer
|
|
GLuint depth_fbo = 0;
|
|
GLuint depth_tex = 0;
|
|
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) {
|
|
glsafe(::glGenFramebuffers(1, &depth_fbo));
|
|
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo));
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glsafe(::glGenTextures(1, &depth_tex));
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
|
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
|
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
|
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
|
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr));
|
|
|
|
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_tex, 0));
|
|
} else {
|
|
glsafe(::glGenFramebuffersEXT(1, &depth_fbo));
|
|
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, depth_fbo));
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glsafe(::glGenTextures(1, &depth_tex));
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
|
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
|
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
|
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
|
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr));
|
|
|
|
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth_tex, 0));
|
|
}
|
|
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
|
if (tverts_range == std::make_pair<size_t, size_t>(0, -1))
|
|
model.render();
|
|
else
|
|
model.render(this->tverts_range);
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
|
|
|
// 2nd. render pass, just a normal render with the depth buffer passed as a texture
|
|
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) {
|
|
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0));
|
|
} else if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Ext) {
|
|
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
|
|
}
|
|
shader->set_uniform("is_outline", true);
|
|
shader->set_uniform("screen_size", Vec2f{cnv_size.get_width(), cnv_size.get_height()});
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
|
|
shader->set_uniform("depth_tex", 0);
|
|
simple_render(shader, model_objects, colors);
|
|
|
|
// Some clean up to do
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
|
shader->set_uniform("is_outline", false);
|
|
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) {
|
|
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0));
|
|
if (depth_fbo != 0)
|
|
glsafe(::glDeleteFramebuffers(1, &depth_fbo));
|
|
} else if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Ext) {
|
|
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
|
|
if (depth_fbo != 0)
|
|
glsafe(::glDeleteFramebuffersEXT(1, &depth_fbo));
|
|
}
|
|
if (depth_tex != 0)
|
|
glsafe(::glDeleteTextures(1, &depth_tex));
|
|
}
|
|
|
|
//BBS add render for simple case
|
|
void GLVolume::simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector<ColorRGBA>& extruder_colors, bool ban_light)
|
|
{
|
|
if (this->is_left_handed())
|
|
glFrontFace(GL_CW);
|
|
glsafe(::glCullFace(GL_BACK));
|
|
|
|
bool color_volume = false;
|
|
ModelObject* model_object = nullptr;
|
|
ModelVolume* model_volume = nullptr;
|
|
do {
|
|
if ((!printable) || object_idx() >= model_objects.size())
|
|
break;
|
|
model_object = model_objects[object_idx()];
|
|
|
|
if (volume_idx() >= model_object->volumes.size())
|
|
break;
|
|
model_volume = model_object->volumes[volume_idx()];
|
|
if (model_volume->mmu_segmentation_facets.empty())
|
|
break;
|
|
|
|
color_volume = true;
|
|
if (model_volume->mmu_segmentation_facets.timestamp() != mmuseg_ts) {
|
|
mmuseg_models.clear();
|
|
std::vector<indexed_triangle_set> its_per_color;
|
|
model_volume->mmu_segmentation_facets.get_facets(*model_volume, its_per_color);
|
|
mmuseg_models.resize(its_per_color.size());
|
|
for (int idx = 0; idx < its_per_color.size(); idx++) {
|
|
mmuseg_models[idx].init_from(its_per_color[idx]);
|
|
}
|
|
|
|
mmuseg_ts = model_volume->mmu_segmentation_facets.timestamp();
|
|
}
|
|
} while (0);
|
|
|
|
if (color_volume && !picking) {
|
|
// when force_transparent, we need to keep the alpha
|
|
if (force_native_color && render_color.is_transparent()) {
|
|
for (auto &extruder_color : extruder_colors)
|
|
extruder_color.a(render_color.a());
|
|
}
|
|
|
|
for (int idx = 0; idx < mmuseg_models.size(); idx++) {
|
|
GUI::GLModel &m = mmuseg_models[idx];
|
|
if (!m.is_initialized())
|
|
continue;
|
|
|
|
if (shader) {
|
|
if (idx == 0) {
|
|
int extruder_id = model_volume->extruder_id();
|
|
//to make black not too hard too see
|
|
ColorRGBA new_color = adjust_color_for_rendering(extruder_colors[extruder_id - 1]);
|
|
if (ban_light) {
|
|
new_color[3] = (255 - (extruder_id - 1))/255.0f;
|
|
}
|
|
m.set_color(new_color);
|
|
// shader->set_uniform("uniform_color", new_color);
|
|
}
|
|
else {
|
|
if (idx <= extruder_colors.size()) {
|
|
//to make black not too hard too see
|
|
ColorRGBA new_color = adjust_color_for_rendering(extruder_colors[idx - 1]);
|
|
if (ban_light) {
|
|
new_color[3] = (255 - (idx - 1))/255.0f;
|
|
}
|
|
m.set_color(new_color);
|
|
// shader->set_uniform("uniform_color", new_color);
|
|
}
|
|
else {
|
|
//to make black not too hard too see
|
|
ColorRGBA new_color = adjust_color_for_rendering(extruder_colors[0]);
|
|
if (ban_light) {
|
|
new_color[3] = (255 - 0) / 255.0f;
|
|
}
|
|
m.set_color(new_color);
|
|
// shader->set_uniform("uniform_color", new_color);
|
|
}
|
|
}
|
|
}
|
|
if (tverts_range == std::make_pair<size_t, size_t>(0, -1))
|
|
m.render();
|
|
else
|
|
m.render(this->tverts_range);
|
|
}
|
|
} else {
|
|
if (tverts_range == std::make_pair<size_t, size_t>(0, -1))
|
|
model.render();
|
|
else
|
|
model.render(this->tverts_range);
|
|
}
|
|
if (this->is_left_handed())
|
|
glFrontFace(GL_CCW);
|
|
}
|
|
|
|
bool GLVolume::is_sla_support() const { return this->composite_id.volume_id == -int(slaposSupportTree); }
|
|
bool GLVolume::is_sla_pad() const { return this->composite_id.volume_id == -int(slaposPad); }
|
|
|
|
bool GLVolume::is_sinking() const
|
|
{
|
|
if (is_modifier || GUI::wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA)
|
|
return false;
|
|
const BoundingBoxf3& box = transformed_convex_hull_bounding_box();
|
|
return box.min.z() < SINKING_Z_THRESHOLD && box.max.z() >= SINKING_Z_THRESHOLD;
|
|
}
|
|
|
|
bool GLVolume::is_below_printbed() const
|
|
{
|
|
return transformed_convex_hull_bounding_box().max.z() < 0.0;
|
|
}
|
|
|
|
void GLVolume::render_sinking_contours()
|
|
{
|
|
m_sinking_contours.render();
|
|
}
|
|
|
|
GLWipeTowerVolume::GLWipeTowerVolume(const std::vector<ColorRGBA>& colors)
|
|
: GLVolume()
|
|
{
|
|
m_colors = colors;
|
|
}
|
|
|
|
void GLWipeTowerVolume::render()
|
|
{
|
|
if (!is_active)
|
|
return;
|
|
|
|
if (m_colors.size() == 0 || m_colors.size() != model_per_colors.size())
|
|
return;
|
|
|
|
if (this->is_left_handed())
|
|
glFrontFace(GL_CW);
|
|
glsafe(::glCullFace(GL_BACK));
|
|
|
|
for (int i = 0; i < m_colors.size(); i++) {
|
|
if (!picking) {
|
|
ColorRGBA new_color = adjust_color_for_rendering(m_colors[i]);
|
|
this->model_per_colors[i].set_color(new_color);
|
|
} else {
|
|
this->model_per_colors[i].set_color(model.get_color());
|
|
}
|
|
this->model_per_colors[i].render();
|
|
}
|
|
|
|
if (this->is_left_handed())
|
|
glFrontFace(GL_CCW);
|
|
}
|
|
|
|
bool GLWipeTowerVolume::IsTransparent() {
|
|
for (size_t i = 0; i < m_colors.size(); i++) {
|
|
if (m_colors[i].is_transparent()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::vector<int> GLVolumeCollection::load_object(
|
|
const ModelObject *model_object,
|
|
int obj_idx,
|
|
const std::vector<int> &instance_idxs,
|
|
const std::string &color_by,
|
|
bool opengl_initialized,
|
|
bool need_raycaster)
|
|
{
|
|
std::vector<int> volumes_idx;
|
|
for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++volume_idx)
|
|
for (int instance_idx : instance_idxs)
|
|
volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by, opengl_initialized, false, false, need_raycaster));
|
|
return volumes_idx;
|
|
}
|
|
|
|
|
|
int GLVolumeCollection::load_object_volume(
|
|
const ModelObject *model_object,
|
|
int obj_idx,
|
|
int volume_idx,
|
|
int instance_idx,
|
|
const std::string &color_by,
|
|
bool opengl_initialized,
|
|
bool in_assemble_view,
|
|
bool use_loaded_id,
|
|
bool need_raycaster)
|
|
{
|
|
const ModelVolume *model_volume = model_object->volumes[volume_idx];
|
|
const int extruder_id = model_volume->extruder_id();
|
|
const ModelInstance *instance = model_object->instances[instance_idx];
|
|
auto color = GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4];
|
|
color.a(model_volume->is_model_part() ? 0.7f : 0.4f);
|
|
|
|
std::shared_ptr<const TriangleMesh> mesh = model_volume->mesh_ptr();
|
|
this->volumes.emplace_back(new GLVolume(color));
|
|
GLVolume& v = *this->volumes.back();
|
|
v.set_color(color_from_model_volume(*model_volume));
|
|
v.name = model_volume->name;
|
|
|
|
#if ENABLE_SMOOTH_NORMALS
|
|
v.model.init_from(mesh, true);
|
|
#else
|
|
v.model.init_from(*mesh);
|
|
if (need_raycaster) { v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh); }
|
|
#endif // ENABLE_SMOOTH_NORMALS
|
|
v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx);
|
|
|
|
if (model_volume->is_model_part())
|
|
{
|
|
// GLVolume will reference a convex hull from model_volume!
|
|
v.set_convex_hull(model_volume->get_convex_hull_shared_ptr());
|
|
if (extruder_id != -1)
|
|
v.extruder_id = extruder_id;
|
|
}
|
|
v.is_modifier = !model_volume->is_model_part();
|
|
v.shader_outside_printer_detection_enabled = model_volume->is_model_part();
|
|
if (in_assemble_view) {
|
|
v.set_instance_transformation(instance->get_assemble_transformation());
|
|
v.set_offset_to_assembly(instance->get_offset_to_assembly());
|
|
}
|
|
else
|
|
v.set_instance_transformation(instance->get_transformation());
|
|
v.set_volume_transformation(model_volume->get_transformation());
|
|
//use object's instance id
|
|
if (use_loaded_id && (instance->loaded_id > 0))
|
|
v.model_object_ID = instance->loaded_id;
|
|
else
|
|
v.model_object_ID = instance->id().id;
|
|
|
|
return int(this->volumes.size() - 1);
|
|
}
|
|
|
|
// Load SLA auxiliary GLVolumes (for support trees or pad).
|
|
// This function produces volumes for multiple instances in a single shot,
|
|
// as some object specific mesh conversions may be expensive.
|
|
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)
|
|
{
|
|
assert(print_object->is_step_done(milestone));
|
|
Transform3d mesh_trafo_inv = print_object->trafo().inverse();
|
|
// Get the support mesh.
|
|
TriangleMesh mesh = print_object->get_mesh(milestone);
|
|
mesh.transform(mesh_trafo_inv);
|
|
// Convex hull is required for out of print bed detection.
|
|
TriangleMesh convex_hull = mesh.convex_hull_3d();
|
|
for (const std::pair<size_t, size_t>& instance_idx : instances) {
|
|
const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first];
|
|
this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR));
|
|
GLVolume& v = *this->volumes.back();
|
|
#if ENABLE_SMOOTH_NORMALS
|
|
v.model.init_from(mesh, true);
|
|
#else
|
|
v.model.init_from(mesh);
|
|
v.model.set_color((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR);
|
|
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<const TriangleMesh>(mesh));
|
|
#endif // ENABLE_SMOOTH_NORMALS
|
|
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);
|
|
// Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
|
|
if (&instance_idx == &instances.back())
|
|
v.set_convex_hull(std::move(convex_hull));
|
|
else
|
|
v.set_convex_hull(convex_hull);
|
|
v.is_modifier = false;
|
|
v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree);
|
|
v.set_instance_transformation(model_instance.get_transformation());
|
|
// Leave the volume transformation at identity.
|
|
// v.set_volume_transformation(model_volume->get_transformation());
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
int plate_idx = obj_idx - 1000;
|
|
|
|
if (depth < 0.01f)
|
|
return int(this->volumes.size() - 1);
|
|
if (height == 0.0f)
|
|
height = 0.1f;
|
|
|
|
std::vector<ColorRGBA> extruder_colors = get_extruders_colors();
|
|
std::vector<ColorRGBA> colors;
|
|
GUI::PartPlateList& ppl = GUI::wxGetApp().plater()->get_partplate_list();
|
|
std::vector<int> plate_extruders = ppl.get_plate(plate_idx)->get_extruders(true);
|
|
TriangleMesh wipe_tower_shell = make_cube(width, depth, height);
|
|
for (int extruder_id : plate_extruders) {
|
|
if (extruder_id <= extruder_colors.size())
|
|
colors.push_back(extruder_colors[extruder_id - 1]);
|
|
else
|
|
colors.push_back(extruder_colors[0]);
|
|
}
|
|
|
|
// Orca: make it transparent
|
|
for(auto& color : colors)
|
|
color.a(0.66f);
|
|
volumes.emplace_back(new GLWipeTowerVolume(colors));
|
|
GLWipeTowerVolume& v = *dynamic_cast<GLWipeTowerVolume*>(volumes.back());
|
|
v.model_per_colors.resize(colors.size());
|
|
for (int i = 0; i < colors.size(); i++) {
|
|
TriangleMesh color_part = make_cube(width, depth / colors.size(), height);
|
|
color_part.translate({ 0.f, depth * i / colors.size(), 0. });
|
|
v.model_per_colors[i].init_from(color_part);
|
|
}
|
|
v.model.init_from(wipe_tower_shell);
|
|
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<const TriangleMesh>(wipe_tower_shell));
|
|
v.set_convex_hull(wipe_tower_shell);
|
|
v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
|
|
v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
|
|
v.composite_id = GLVolume::CompositeID(obj_idx, 0, 0);
|
|
v.geometry_id.first = 0;
|
|
v.geometry_id.second = wipe_tower_instance_id().id + (obj_idx - 1000);
|
|
v.is_wipe_tower = true;
|
|
v.shader_outside_printer_detection_enabled = !size_unknown;
|
|
return int(volumes.size() - 1);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func)
|
|
{
|
|
GLVolumeWithIdAndZList list;
|
|
list.reserve(volumes.size());
|
|
|
|
for (unsigned int i = 0; i < (unsigned int)volumes.size(); ++i) {
|
|
GLVolume* volume = volumes[i];
|
|
bool is_transparent = volume->render_color.is_transparent();
|
|
auto tempGlwipeTowerVolume = dynamic_cast<GLWipeTowerVolume *>(volume);
|
|
if (tempGlwipeTowerVolume) {
|
|
is_transparent = tempGlwipeTowerVolume->IsTransparent();
|
|
}
|
|
if (((type == GLVolumeCollection::ERenderType::Opaque && !is_transparent) ||
|
|
(type == GLVolumeCollection::ERenderType::Transparent && is_transparent) ||
|
|
type == GLVolumeCollection::ERenderType::All) &&
|
|
(! filter_func || filter_func(*volume)))
|
|
list.emplace_back(std::make_pair(volume, std::make_pair(i, 0.0)));
|
|
}
|
|
|
|
if (type == GLVolumeCollection::ERenderType::Transparent && list.size() > 1) {
|
|
for (GLVolumeWithIdAndZ& volume : list) {
|
|
volume.second.second = volume.first->bounding_box().transformed(view_matrix * volume.first->world_matrix()).max(2);
|
|
}
|
|
|
|
std::sort(list.begin(), list.end(),
|
|
[](const GLVolumeWithIdAndZ& v1, const GLVolumeWithIdAndZ& v2) -> bool { return v1.second.second < v2.second.second; }
|
|
);
|
|
}
|
|
else if (type == GLVolumeCollection::ERenderType::Opaque && list.size() > 1) {
|
|
std::sort(list.begin(), list.end(),
|
|
[](const GLVolumeWithIdAndZ& v1, const GLVolumeWithIdAndZ& v2) -> bool { return v1.first->selected && !v2.first->selected; }
|
|
);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
int GLVolumeCollection::get_selection_support_threshold_angle(bool &enable_support) const
|
|
{
|
|
const DynamicPrintConfig& glb_cfg = GUI::wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
|
enable_support = glb_cfg.opt_bool("enable_support");
|
|
int support_threshold_angle = glb_cfg.opt_int("support_threshold_angle");
|
|
return support_threshold_angle ;
|
|
}
|
|
|
|
//BBS: add outline drawing logic
|
|
void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
|
|
bool disable_cullface,
|
|
const Transform3d & view_matrix,
|
|
const Transform3d& projection_matrix,
|
|
const GUI::Size& cnv_size,
|
|
std::function<bool(const GLVolume &)> filter_func,
|
|
bool partly_inside_enable) const
|
|
{
|
|
GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func);
|
|
if (to_render.empty())
|
|
return;
|
|
|
|
GLShaderProgram* shader = GUI::wxGetApp().get_current_shader();
|
|
if (shader == nullptr)
|
|
return;
|
|
|
|
GLShaderProgram* sink_shader = GUI::wxGetApp().get_shader("flat");
|
|
GLShaderProgram* edges_shader = GUI::wxGetApp().get_shader("flat");
|
|
|
|
if (type == ERenderType::Transparent) {
|
|
glsafe(::glEnable(GL_BLEND));
|
|
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
|
}
|
|
|
|
glsafe(::glCullFace(GL_BACK));
|
|
if (disable_cullface)
|
|
glsafe(::glDisable(GL_CULL_FACE));
|
|
|
|
for (GLVolumeWithIdAndZ& volume : to_render) {
|
|
#if ENABLE_MODIFIERS_ALWAYS_TRANSPARENT
|
|
if (type == ERenderType::Transparent) {
|
|
volume.first->force_transparent = true;
|
|
//BOOST_LOG_TRIVIAL(info) << boost::format("transparent rendering...");
|
|
}
|
|
//else
|
|
// BOOST_LOG_TRIVIAL(info) << boost::format("opaque rendering...");
|
|
#endif // ENABLE_MODIFIERS_ALWAYS_TRANSPARENT
|
|
volume.first->set_render_color();
|
|
#if ENABLE_MODIFIERS_ALWAYS_TRANSPARENT
|
|
if (type == ERenderType::Transparent)
|
|
volume.first->force_transparent = false;
|
|
#endif // ENABLE_MODIFIERS_ALWAYS_TRANSPARENT
|
|
|
|
// render sinking contours of non-hovered volumes
|
|
shader->stop_using();
|
|
if (sink_shader != nullptr) {
|
|
sink_shader->start_using();
|
|
if (m_show_sinking_contours) {
|
|
if (volume.first->is_sinking() && !volume.first->is_below_printbed() &&
|
|
volume.first->hover == GLVolume::HS_None && !volume.first->force_sinking_contours) {
|
|
volume.first->render_sinking_contours();
|
|
}
|
|
}
|
|
sink_shader->stop_using();
|
|
}
|
|
shader->start_using();
|
|
|
|
if (!volume.first->model.is_initialized())
|
|
shader->set_uniform("uniform_color", volume.first->render_color);
|
|
shader->set_uniform("z_range", m_z_range);
|
|
shader->set_uniform("clipping_plane", m_clipping_plane);
|
|
shader->set_uniform("use_color_clip_plane", m_use_color_clip_plane);
|
|
shader->set_uniform("color_clip_plane", m_color_clip_plane);
|
|
shader->set_uniform("uniform_color_clip_plane_1", m_color_clip_plane_colors[0]);
|
|
shader->set_uniform("uniform_color_clip_plane_2", m_color_clip_plane_colors[1]);
|
|
//BOOST_LOG_TRIVIAL(info) << boost::format("set uniform_color to {%1%, %2%, %3%, %4%}, with_outline=%5%, selected %6%")
|
|
// %volume.first->render_color[0]%volume.first->render_color[1]%volume.first->render_color[2]%volume.first->render_color[3]
|
|
// %with_outline%volume.first->selected;
|
|
|
|
//BBS set print_volume to render volume
|
|
//shader->set_uniform("print_volume.type", static_cast<int>(m_render_volume.type));
|
|
//shader->set_uniform("print_volume.xy_data", m_render_volume.data);
|
|
//shader->set_uniform("print_volume.z_data", m_render_volume.zs);
|
|
|
|
if (volume.first->partly_inside && partly_inside_enable) {
|
|
//only partly inside volume need to be painted with boundary check
|
|
shader->set_uniform("print_volume.type", static_cast<int>(m_print_volume.type));
|
|
shader->set_uniform("print_volume.xy_data", m_print_volume.data);
|
|
shader->set_uniform("print_volume.z_data", m_print_volume.zs);
|
|
}
|
|
else {
|
|
//use -1 ad a invalid type
|
|
shader->set_uniform("print_volume.type", -1);
|
|
}
|
|
|
|
bool enable_support;
|
|
int support_threshold_angle = get_selection_support_threshold_angle(enable_support);
|
|
|
|
float normal_z = -::cos(Geometry::deg2rad((float) support_threshold_angle));
|
|
|
|
shader->set_uniform("volume_world_matrix", volume.first->world_matrix());
|
|
shader->set_uniform("slope.actived", m_slope.isGlobalActive && !volume.first->is_modifier && !volume.first->is_wipe_tower);
|
|
shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
|
|
shader->set_uniform("slope.normal_z", normal_z);
|
|
|
|
#if ENABLE_ENVIRONMENT_MAP
|
|
unsigned int environment_texture_id = GUI::wxGetApp().plater()->get_environment_texture_id();
|
|
bool use_environment_texture = environment_texture_id > 0 && GUI::wxGetApp().app_config->get("use_environment_map") == "1";
|
|
shader->set_uniform("use_environment_tex", use_environment_texture);
|
|
if (use_environment_texture)
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, environment_texture_id));
|
|
#endif // ENABLE_ENVIRONMENT_MAP
|
|
glcheck();
|
|
|
|
volume.first->model.set_color(volume.first->render_color);
|
|
const Transform3d model_matrix = volume.first->world_matrix();
|
|
shader->set_uniform("view_model_matrix", view_matrix * model_matrix);
|
|
shader->set_uniform("projection_matrix", projection_matrix);
|
|
const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
|
|
shader->set_uniform("view_normal_matrix", view_normal_matrix);
|
|
//BBS: add outline related logic
|
|
if (volume.first->selected && GUI::wxGetApp().show_outline())
|
|
volume.first->render_with_outline(cnv_size);
|
|
else
|
|
volume.first->render();
|
|
|
|
#if ENABLE_ENVIRONMENT_MAP
|
|
if (use_environment_texture)
|
|
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
|
#endif // ENABLE_ENVIRONMENT_MAP
|
|
|
|
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
|
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
|
}
|
|
|
|
if (m_show_sinking_contours) {
|
|
shader->stop_using();
|
|
if (sink_shader != nullptr) {
|
|
sink_shader->start_using();
|
|
for (GLVolumeWithIdAndZ& volume : to_render) {
|
|
// render sinking contours of hovered/displaced volumes
|
|
if (volume.first->is_sinking() && !volume.first->is_below_printbed() &&
|
|
(volume.first->hover != GLVolume::HS_None || volume.first->force_sinking_contours)) {
|
|
glsafe(::glDepthFunc(GL_ALWAYS));
|
|
volume.first->render_sinking_contours();
|
|
glsafe(::glDepthFunc(GL_LESS));
|
|
}
|
|
}
|
|
sink_shader->start_using();
|
|
}
|
|
shader->start_using();
|
|
}
|
|
|
|
if (disable_cullface)
|
|
glsafe(::glEnable(GL_CULL_FACE));
|
|
|
|
if (type == ERenderType::Transparent)
|
|
glsafe(::glDisable(GL_BLEND));
|
|
}
|
|
|
|
bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, ModelInstanceEPrintVolumeState *out_state) const
|
|
{
|
|
if (GUI::wxGetApp().plater() == NULL)
|
|
{
|
|
if (out_state != nullptr)
|
|
*out_state = ModelInstancePVS_Inside;
|
|
return false;
|
|
}
|
|
|
|
const Model& model = GUI::wxGetApp().plater()->model();
|
|
auto volume_below = [](GLVolume& volume) -> bool
|
|
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_below_printbed(); };
|
|
// Volume is partially below the print bed, thus a pre-calculated convex hull cannot be used.
|
|
auto volume_sinking = [](GLVolume& volume) -> bool
|
|
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_sinking(); };
|
|
// Cached bounding box of a volume above the print bed.
|
|
auto volume_bbox = [volume_sinking](GLVolume& volume) -> BoundingBoxf3
|
|
{ return volume_sinking(volume) ? volume.transformed_non_sinking_bounding_box() : volume.transformed_convex_hull_bounding_box(); };
|
|
// Cached 3D convex hull of a volume above the print bed.
|
|
auto volume_convex_mesh = [volume_sinking, &model](GLVolume& volume) -> const TriangleMesh&
|
|
{ return volume_sinking(volume) ? model.objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : *volume.convex_hull(); };
|
|
|
|
ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside;
|
|
bool contained_min_one = false;
|
|
|
|
//BBS: add instance judge logic, besides to original volume judge logic
|
|
std::map<int64_t, ModelInstanceEPrintVolumeState> model_state;
|
|
|
|
GUI::PartPlate* curr_plate = GUI::wxGetApp().plater()->get_partplate_list().get_selected_plate();
|
|
const Pointfs& pp_bed_shape = curr_plate->get_shape();
|
|
BuildVolume plate_build_volume(pp_bed_shape, build_volume.printable_height());
|
|
const std::vector<BoundingBoxf3>& exclude_areas = curr_plate->get_exclude_areas();
|
|
|
|
for (GLVolume* volume : this->volumes)
|
|
{
|
|
if (! volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (! volume->is_wipe_tower && volume->composite_id.volume_id >= 0))) {
|
|
BuildVolume::ObjectState state;
|
|
if (volume_below(*volume))
|
|
state = BuildVolume::ObjectState::Below;
|
|
else {
|
|
switch (plate_build_volume.type()) {
|
|
case BuildVolume_Type::Rectangle: {
|
|
//FIXME this test does not evaluate collision of a build volume bounding box with non-convex objects.
|
|
const BoundingBoxf3& bb = volume_bbox(*volume);
|
|
state = plate_build_volume.volume_state_bbox(bb);
|
|
}
|
|
break;
|
|
case BuildVolume_Type::Circle:
|
|
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:
|
|
state = plate_build_volume.object_state(volume_convex_mesh(*volume).its, volume->world_matrix().cast<float>(), volume_sinking(*volume));
|
|
break;
|
|
default:
|
|
// Ignore, don't produce any collision.
|
|
state = BuildVolume::ObjectState::Inside;
|
|
break;
|
|
}
|
|
assert(state != BuildVolume::ObjectState::Below);
|
|
}
|
|
|
|
int64_t comp_id = ((int64_t)volume->composite_id.object_id << 32) | ((int64_t)volume->composite_id.instance_id);
|
|
volume->is_outside = state != BuildVolume::ObjectState::Inside;
|
|
//volume->partly_inside = (state == BuildVolume::ObjectState::Colliding);
|
|
if (volume->printable) {
|
|
if (overall_state == ModelInstancePVS_Inside && volume->is_outside) {
|
|
overall_state = ModelInstancePVS_Fully_Outside;
|
|
}
|
|
|
|
if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && (state == BuildVolume::ObjectState::Colliding))
|
|
{
|
|
overall_state = ModelInstancePVS_Partly_Outside;
|
|
}
|
|
contained_min_one |= !volume->is_outside;
|
|
}
|
|
|
|
ModelInstanceEPrintVolumeState volume_state;
|
|
//if (volume->is_outside && (plate_build_volume.bounding_volume().intersects(volume->bounding_box())))
|
|
if (volume->is_outside && (state == BuildVolume::ObjectState::Colliding))
|
|
volume_state = ModelInstancePVS_Partly_Outside;
|
|
else if (volume->is_outside)
|
|
volume_state = ModelInstancePVS_Fully_Outside;
|
|
else
|
|
volume_state = ModelInstancePVS_Inside;
|
|
|
|
if (model_state.find(comp_id) != model_state.end())
|
|
{
|
|
if (model_state[comp_id] != ModelInstancePVS_Partly_Outside)
|
|
{
|
|
if (volume_state == ModelInstancePVS_Partly_Outside)
|
|
model_state[comp_id] = ModelInstancePVS_Partly_Outside;
|
|
else if (model_state[comp_id] != volume_state)
|
|
{
|
|
model_state[comp_id] = ModelInstancePVS_Partly_Outside;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
model_state[comp_id] = volume_state;
|
|
}
|
|
|
|
if (model_state[comp_id] == ModelInstancePVS_Partly_Outside) {
|
|
overall_state = ModelInstancePVS_Partly_Outside;
|
|
BOOST_LOG_TRIVIAL(debug) << "instance includes " << volume->name << " is partially outside of bed";
|
|
}
|
|
}
|
|
}
|
|
|
|
for (GLVolume* volume : this->volumes)
|
|
{
|
|
if (! volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (! volume->is_wipe_tower && volume->composite_id.volume_id >= 0)))
|
|
{
|
|
int64_t comp_id = ((int64_t)volume->composite_id.object_id << 32) | ((int64_t)volume->composite_id.instance_id);
|
|
if (model_state.find(comp_id) != model_state.end())
|
|
{
|
|
if (model_state[comp_id] == ModelInstancePVS_Partly_Outside) {
|
|
volume->partly_inside = true;
|
|
}
|
|
else
|
|
volume->partly_inside = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (out_state != nullptr)
|
|
*out_state = overall_state;
|
|
|
|
return contained_min_one;
|
|
}
|
|
|
|
void GLVolumeCollection::reset_outside_state()
|
|
{
|
|
for (GLVolume* volume : this->volumes)
|
|
{
|
|
if (volume != nullptr) {
|
|
volume->is_outside = false;
|
|
volume->partly_inside = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig *config, bool is_update_alpha)
|
|
{
|
|
|
|
using ColorItem = std::pair<std::string, ColorRGBA>;
|
|
std::vector<ColorItem> colors;
|
|
|
|
if (static_cast<PrinterTechnology>(config->opt_int("printer_technology")) == ptSLA) {
|
|
const std::string& txt_color = config->opt_string("material_colour").empty() ?
|
|
print_config_def.get("material_colour")->get_default_value<ConfigOptionString>()->value :
|
|
config->opt_string("material_colour");
|
|
ColorRGBA rgba;
|
|
if (decode_color(txt_color, rgba))
|
|
colors.push_back({ txt_color, rgba });
|
|
}
|
|
else {
|
|
const ConfigOptionStrings* filamemts_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("filament_colour"));
|
|
if (filamemts_opt == nullptr)
|
|
return;
|
|
|
|
size_t colors_count = (size_t)filamemts_opt->values.size();
|
|
if (colors_count == 0)
|
|
return;
|
|
colors.resize(colors_count);
|
|
|
|
for (unsigned int i = 0; i < colors_count; ++i) {
|
|
ColorRGBA rgba;
|
|
const std::string& fil_color = config->opt_string("filament_colour", i);
|
|
if (decode_color(fil_color, rgba))
|
|
colors[i] = { fil_color, rgba };
|
|
}
|
|
}
|
|
|
|
for (GLVolume* volume : volumes) {
|
|
if (volume == nullptr || volume->is_modifier || volume->is_wipe_tower || volume->volume_idx() < 0)
|
|
continue;
|
|
|
|
int extruder_id = volume->extruder_id - 1;
|
|
if (extruder_id < 0 || (int)colors.size() <= extruder_id)
|
|
extruder_id = 0;
|
|
|
|
const ColorItem& color = colors[extruder_id];
|
|
if (!color.first.empty()) {
|
|
if (!is_update_alpha) {
|
|
float old_a = volume->color.a();
|
|
volume->color = color.second;
|
|
volume->color.a(old_a);
|
|
} else {
|
|
volume->color = color.second;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GLVolumeCollection::set_transparency(float alpha)
|
|
{
|
|
for (GLVolume *volume : volumes) {
|
|
if (volume == nullptr || volume->is_modifier || volume->is_wipe_tower || (volume->volume_idx() < 0))
|
|
continue;
|
|
|
|
volume->color.a(alpha);
|
|
}
|
|
}
|
|
|
|
std::vector<double> GLVolumeCollection::get_current_print_zs(bool active_only) const
|
|
{
|
|
// Collect layer top positions of all volumes.
|
|
std::vector<double> print_zs;
|
|
for (GLVolume *vol : this->volumes)
|
|
{
|
|
if (!active_only || vol->is_active)
|
|
append(print_zs, vol->print_zs);
|
|
}
|
|
std::sort(print_zs.begin(), print_zs.end());
|
|
|
|
// Replace intervals of layers with similar top positions with their average value.
|
|
int n = int(print_zs.size());
|
|
int k = 0;
|
|
for (int i = 0; i < n;) {
|
|
int j = i + 1;
|
|
coordf_t zmax = print_zs[i] + EPSILON;
|
|
for (; j < n && print_zs[j] <= zmax; ++ j) ;
|
|
print_zs[k ++] = (j > i + 1) ? (0.5 * (print_zs[i] + print_zs[j - 1])) : print_zs[i];
|
|
i = j;
|
|
}
|
|
if (k < n)
|
|
print_zs.erase(print_zs.begin() + k, print_zs.end());
|
|
|
|
return print_zs;
|
|
}
|
|
|
|
size_t GLVolumeCollection::cpu_memory_used() const
|
|
{
|
|
size_t memsize = sizeof(*this) + this->volumes.capacity() * sizeof(GLVolume);
|
|
for (const GLVolume *volume : this->volumes)
|
|
memsize += volume->cpu_memory_used();
|
|
return memsize;
|
|
}
|
|
|
|
size_t GLVolumeCollection::gpu_memory_used() const
|
|
{
|
|
size_t memsize = 0;
|
|
for (const GLVolume *volume : this->volumes)
|
|
memsize += volume->gpu_memory_used();
|
|
return memsize;
|
|
}
|
|
|
|
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()) + ")";
|
|
}
|
|
|
|
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_triangle(idx_b[Bottom], idx_b[Left], idx_b[Top]);
|
|
geometry.add_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_triangle(idx_prev[Right], idx_a[Right], idx_prev[Top]);
|
|
geometry.add_triangle(idx_prev[Right], idx_prev[Bottom], idx_a[Right]);
|
|
}
|
|
else {
|
|
// Left turn. Fill in the left turn wedge.
|
|
geometry.add_triangle(idx_prev[Left], idx_prev[Top], idx_a[Left]);
|
|
geometry.add_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_index(u);
|
|
if (id == (unsigned int)idx_prev[Left])
|
|
geometry.set_index(u, (unsigned int)idx_initial[Left]);
|
|
else if (id == (unsigned int)idx_prev[Right])
|
|
geometry.set_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_triangle(idx_a[Bottom], idx_a[Right], idx_a[Top]);
|
|
geometry.add_triangle(idx_a[Bottom], idx_a[Top], idx_a[Left]);
|
|
}
|
|
|
|
if (!closed) {
|
|
// Terminate open paths with caps.
|
|
if (is_first) {
|
|
geometry.add_triangle(idx_a[Bottom], idx_a[Right], idx_a[Top]);
|
|
geometry.add_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_triangle(idx_b[Bottom], idx_b[Left], idx_b[Top]);
|
|
geometry.add_triangle(idx_b[Bottom], idx_b[Top], idx_b[Right]);
|
|
}
|
|
}
|
|
|
|
// Add quads for a straight hollow tube-like segment.
|
|
// bottom-right face
|
|
geometry.add_triangle(idx_a[Bottom], idx_b[Bottom], idx_b[Right]);
|
|
geometry.add_triangle(idx_a[Bottom], idx_b[Right], idx_a[Right]);
|
|
// top-right face
|
|
geometry.add_triangle(idx_a[Right], idx_b[Right], idx_b[Top]);
|
|
geometry.add_triangle(idx_a[Right], idx_b[Top], idx_a[Top]);
|
|
// top-left face
|
|
geometry.add_triangle(idx_a[Top], idx_b[Top], idx_b[Left]);
|
|
geometry.add_triangle(idx_a[Top], idx_b[Left], idx_a[Left]);
|
|
// bottom-left face
|
|
geometry.add_triangle(idx_a[Left], idx_b[Left], idx_b[Bottom]);
|
|
geometry.add_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_triangle(idx_prev[Right], idx_a[Right], idx_prev[Top]);
|
|
geometry.add_triangle(idx_prev[Right], idx_prev[Bottom], idx_a[Right]);
|
|
}
|
|
else {
|
|
// Left turn. Fill in the left turn wedge.
|
|
geometry.add_triangle(idx_prev[Left], idx_prev[Top], idx_a[Left]);
|
|
geometry.add_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_index(u);
|
|
if (id == (unsigned int)idx_prev[Left])
|
|
geometry.set_index(u, (unsigned int)idx_initial[Left]);
|
|
else if (id == (unsigned int)idx_prev[Right])
|
|
geometry.set_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_triangle(idx_a[Bottom], idx_a[Right], idx_a[Top]);
|
|
geometry.add_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_triangle(idx_b[Bottom], idx_b[Left], idx_b[Top]);
|
|
geometry.add_triangle(idx_b[Bottom], idx_b[Top], idx_b[Right]);
|
|
}
|
|
}
|
|
|
|
// Add quads for a straight hollow tube-like segment.
|
|
// bottom-right face
|
|
geometry.add_triangle(idx_a[Bottom], idx_b[Bottom], idx_b[Right]);
|
|
geometry.add_triangle(idx_a[Bottom], idx_b[Right], idx_a[Right]);
|
|
// top-right face
|
|
geometry.add_triangle(idx_a[Right], idx_b[Right], idx_b[Top]);
|
|
geometry.add_triangle(idx_a[Right], idx_b[Top], idx_a[Top]);
|
|
// top-left face
|
|
geometry.add_triangle(idx_a[Top], idx_b[Top], idx_b[Left]);
|
|
geometry.add_triangle(idx_a[Top], idx_b[Left], idx_a[Left]);
|
|
// bottom-left face
|
|
geometry.add_triangle(idx_a[Left], idx_b[Left], idx_b[Bottom]);
|
|
geometry.add_triangle(idx_a[Left], idx_b[Bottom], idx_a[Bottom]);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// Fill in the qverts and tverts with quads and triangles for the extrusion_path.
|
|
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);
|
|
}
|
|
|
|
// Fill in the qverts and tverts with quads and triangles for the extrusion_loop.
|
|
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);
|
|
}
|
|
|
|
// Fill in the qverts and tverts with quads and triangles for the extrusion_multi_path.
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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()");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace Slic3r
|