diff --git a/src/imgui/imgui.cpp b/src/imgui/imgui.cpp index fd32e4cf5..d589e1f3b 100644 --- a/src/imgui/imgui.cpp +++ b/src/imgui/imgui.cpp @@ -10970,7 +10970,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Basic info Text("Dear ImGui %s", GetVersion()); - Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + //Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows); Text("%d active allocations", io.MetricsActiveAllocations); diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 900e50fb6..3634f0b0f 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -172,6 +172,8 @@ void AppConfig::set_defaults() if (get("zoom_to_mouse").empty()) set_bool("zoom_to_mouse", false); + if (get("enable_lod").empty()) + set_bool("enable_lod", true); if (get("user_bed_type").empty()) set_bool("user_bed_type", true); if (get("grabber_size_factor").empty()) @@ -306,7 +308,7 @@ void AppConfig::set_defaults() if (get("mouse_wheel").empty()) { set("mouse_wheel", "0"); } - + if (get("max_recent_count").empty()) { set("max_recent_count", "18"); } diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 2cdc5255b..72f30f268 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -108,6 +108,8 @@ set(lisbslic3r_sources Fill/FillRectilinear.hpp Flow.cpp Flow.hpp + Frustum.cpp + Frustum.hpp FlushVolCalc.cpp FlushVolCalc.hpp format.hpp diff --git a/src/libslic3r/Frustum.cpp b/src/libslic3r/Frustum.cpp new file mode 100644 index 000000000..b01de8c20 --- /dev/null +++ b/src/libslic3r/Frustum.cpp @@ -0,0 +1,94 @@ +#include "Frustum.hpp" +#include +namespace Slic3r { +Frustum::Plane::PlaneIntersects Frustum::Plane::intersects(const BoundingBoxf3 &box) const +{ + Vec3f center = ((box.min + box.max) * 0.5f).cast(); + Vec3f extent = ((box.max - box.min) * 0.5f).cast(); + float d = distance(center); + float r = fabsf(extent.x() * normal_.x()) + fabsf(extent.y() * normal_.y()) + fabsf(extent.z() * normal_.z()); + if (d == r) { + return Plane::Intersects_Tangent; + } else if (std::abs(d) < r) { + return Plane::Intersects_Cross; + } + return (d > 0.0f) ? Plane::Intersects_Front : Plane::Intersects_Back; +} +Frustum::Plane::PlaneIntersects Frustum::Plane::intersects(const Vec3f &p0) const +{ + float d = distance(p0); + if (d == 0) { + return Plane::Intersects_Tangent; + } + return (d > 0.0f) ? Plane::Intersects_Front : Plane::Intersects_Back; +} +Frustum::Plane::PlaneIntersects Frustum::Plane::intersects(const Vec3f &p0, const Vec3f &p1) const +{ + Plane::PlaneIntersects state0 = intersects(p0); + Plane::PlaneIntersects state1 = intersects(p1); + if (state0 == state1) { + return state0; + } + if (state0 == Plane::Intersects_Tangent || state1 == Plane::Intersects_Tangent) { + return Plane::Intersects_Tangent; + } + + return Plane::Intersects_Cross; +} +Frustum::Plane::PlaneIntersects Frustum::Plane::intersects(const Vec3f &p0, const Vec3f &p1, const Vec3f &p2) const +{ + Plane::PlaneIntersects state0 = intersects(p0, p1); + Plane::PlaneIntersects state1 = intersects(p0, p2); + Plane::PlaneIntersects state2 = intersects(p1, p2); + + if (state0 == state1 && state0 == state2) { + return state0; } + + if (state0 == Plane::Intersects_Cross || state1 == Plane::Intersects_Cross || state2 == Plane::Intersects_Cross) { + return Plane::Intersects_Cross; + } + + return Plane::Intersects_Tangent; +} + +bool Frustum::intersects(const BoundingBoxf3 &box) const { + for (auto &plane : planes) { + if (plane.intersects(box) == Plane::Intersects_Back) { + return false; + } + } + // check box intersects + if (!bbox.intersects(box)) { + return false; + } + return true; +} + +bool Frustum::intersects(const Vec3f &p0) const { + for (auto &plane : planes) { + if (plane.intersects(p0) == Plane::Intersects_Back) { return false; } + } + return true; +} + +bool Frustum::intersects(const Vec3f &p0, const Vec3f &p1) const +{ + for (auto &plane : planes) { + if (plane.intersects(p0, p1) == Plane::Intersects_Back) { + return false; + } + } + return true; +} + +bool Frustum::intersects(const Vec3f &p0, const Vec3f &p1, const Vec3f &p2) const +{ + for (auto &plane : planes) { + if (plane.intersects(p0, p1, p2) == Plane::Intersects_Back) { + return false; + } + } + return true; +} + +} // namespace Slic3r diff --git a/src/libslic3r/Frustum.hpp b/src/libslic3r/Frustum.hpp new file mode 100644 index 000000000..444c9f858 --- /dev/null +++ b/src/libslic3r/Frustum.hpp @@ -0,0 +1,79 @@ +#ifndef slic3r_Frustum_hpp_ +#define slic3r_Frustum_hpp_ + +#include "Point.hpp" +#include "BoundingBox.hpp" +namespace Slic3r { +class Frustum +{ +public: + Frustum()=default; + ~Frustum() = default; + + class Plane { + public: + enum PlaneIntersects { Intersects_Cross = 0, Intersects_Tangent = 1, Intersects_Front = 2, Intersects_Back = 3 }; + void set(const Vec3f &n, const Vec3f &pt) + { + normal_ = n.normalized(); + center_ = pt; + d_ = -normal_.dot(pt); + } + + float distance(const Vec3f &pt) const { return normal_.dot(pt) + d_; } + + inline const Vec3f &getNormal() const { return normal_; } + + Plane::PlaneIntersects intersects(const BoundingBoxf3 &box) const; + //// check intersect with point (world space) + Plane::PlaneIntersects intersects(const Vec3f &p0) const; + // check intersect with line segment (world space) + Plane::PlaneIntersects intersects(const Vec3f &p0, const Vec3f &p1) const; + // check intersect with triangle (world space) + Plane::PlaneIntersects intersects(const Vec3f &p0, const Vec3f &p1, const Vec3f &p2) const; + private: + Vec3f normal_; + Vec3f center_; + float d_ = 0; + }; + + bool intersects(const BoundingBoxf3 &box) const; + // check intersect with point (world space) + bool intersects(const Vec3f &p0) const; + // check intersect with line segment (world space) + bool intersects(const Vec3f &p0, const Vec3f &p1) const; + // check intersect with triangle (world space) + bool intersects(const Vec3f &p0, const Vec3f &p1, const Vec3f &p2) const; + + Plane planes[6]; + /* corners[0]: nearTopLeft; + * corners[1]: nearTopRight; + * corners[2]: nearBottomLeft; + * corners[3]: nearBottomRight; + * corners[4]: farTopLeft; + * corners[5]: farTopRight; + * corners[6]: farBottomLeft; + * corners[7]: farBottomRight; + */ + Vec3f corners[8]; + + BoundingBoxf3 bbox; +}; + +enum FrustumClipMask { + POSITIVE_X = 1 << 0, + NEGATIVE_X = 1 << 1, + POSITIVE_Y = 1 << 2, + NEGATIVE_Y = 1 << 3, + POSITIVE_Z = 1 << 4, + NEGATIVE_Z = 1 << 5, +}; + +const int FrustumClipMaskArray[6] = { + FrustumClipMask::POSITIVE_X, FrustumClipMask::NEGATIVE_X, FrustumClipMask::POSITIVE_Y, FrustumClipMask::NEGATIVE_Y, FrustumClipMask::POSITIVE_Z, FrustumClipMask::NEGATIVE_Z, +}; + +const Vec4f FrustumClipPlane[6] = {{-1, 0, 0, 1}, {1, 0, 0, 1}, {0, -1, 0, 1}, {0, 1, 0, 1}, {0, 0, -1, 1}, {0, 0, 1, 1}}; +} + +#endif diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index fdd43e88e..8c86e4096 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -10,6 +10,7 @@ #include "GLShader.hpp" #include "GUI_App.hpp" #include "GUI_Colors.hpp" +//#include "Camera.hpp" #include "Plater.hpp" #include "BitmapCache.hpp" @@ -27,6 +28,7 @@ #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/Tesselate.hpp" #include "libslic3r/PrintConfig.hpp" +#include "libslic3r/QuadricEdgeCollapse.hpp" #include #include @@ -415,6 +417,45 @@ std::array, 5> GLVolume::MODEL_COLOR = { { { 1.0f, 1.0f, 0.0f, 1.f } } }; +float GLVolume::LOD_HIGH_ZOOM = 3.5f; +float GLVolume::LOD_MIDDLE_ZOOM = 2.8f; +float GLVolume::LOD_SMALL_ZOOM = 1.4f; +float GLVolume::LAST_CAMERA_ZOOM_VALUE = 0.0f; +const float ZOOM_THRESHOLD = 0.3f; +const unsigned char LOD_UPDATE_FREQUENCY = 20; +const Vec2i LOD_SCREEN_MIN = Vec2i(45, 35); +const Vec2i LOD_SCREEN_MAX = Vec2i(70, 55); + +Vec2f calc_pt_in_screen(const Vec3d &pt, const Transform3d &world_tran, const Matrix4d &view_proj_mat, int window_width, int window_height) +{ + auto tran = (view_proj_mat * world_tran); + Vec4d temp_center(pt.x(), pt.y(), pt.z(), 1.0); + Vec4d temp_ndc = tran * temp_center; + Vec3d screen_box_center = Vec3d(temp_ndc.x(), temp_ndc.y(), temp_ndc.z()) / temp_ndc.w(); + + float x = 0.5f * (1 + screen_box_center(0)) * window_width; + float y = 0.5f * (1 - screen_box_center(1)) * window_height; + return Vec2f(x, y); +} + +LOD_LEVEL calc_volume_box_in_screen_bigger_than_threshold( + const BoundingBoxf3 &v_box, const Transform3d &world_tran, const Matrix4d &view_proj_mat, + int window_width, int window_height) +{ + auto s_min = calc_pt_in_screen(v_box.min, world_tran, view_proj_mat, window_width, window_height); + auto s_max = calc_pt_in_screen(v_box.max, world_tran, view_proj_mat, window_width, window_height); + auto size_x = abs(s_max.x() - s_min.x()); + auto size_y = abs(s_max.y() - s_min.y()); + if (size_x >= LOD_SCREEN_MAX.x() || size_y >= LOD_SCREEN_MAX.y()) { + return LOD_LEVEL::HIGH; + } + if (size_x <= LOD_SCREEN_MIN.x() && size_y <= LOD_SCREEN_MIN.y()) { + return LOD_LEVEL::SMALL; + } else { + return LOD_LEVEL::MIDDLE; + } +} + void GLVolume::update_render_colors() { GLVolume::DISABLED_COLOR = GLColor(RenderColor::colors[RenderCol_Model_Disable]); @@ -463,6 +504,8 @@ GLVolume::GLVolume(float r, float g, float b, float a, bool create_index_data) , force_sinking_contours(false) , tverts_range(0, size_t(-1)) , qverts_range(0, size_t(-1)) + , tverts_range_lod(0, size_t(-1)) + , qverts_range_lod(0, size_t(-1)) { color = { r, g, b, a }; set_render_color(color); @@ -580,6 +623,91 @@ std::array color_from_model_volume(const ModelVolume& model_volume) return color; } +bool GLVolume::simplify_mesh(const TriangleMesh &mesh, GLIndexedVertexArray &va, LOD_LEVEL lod) const +{ + return simplify_mesh(mesh.its,va,lod); +} +#define SUPER_LARGE_FACES 500000 +#define LARGE_FACES 100000 +bool GLVolume::simplify_mesh(const indexed_triangle_set &_its, GLIndexedVertexArray &va, LOD_LEVEL lod) const +{ + if (_its.indices.size() == 0 || _its.vertices.size() == 0) { return false; } + bool enable_lod = GUI::wxGetApp().app_config->get("enable_lod") == "true"; + if (!enable_lod) { + return false; + } + auto its = std::make_unique(_its); + auto m_state = std::make_unique(); + if (lod == LOD_LEVEL::MIDDLE) { + m_state->config.max_error = 0.5f; + if (_its.indices.size() > SUPER_LARGE_FACES) { + m_state->config.max_error = 0.4f; + } else if (_its.indices.size() > LARGE_FACES) { + m_state->config.max_error = 0.3f; + } + } + if (lod == LOD_LEVEL::SMALL) { + m_state->config.max_error = 0.1f; + if (_its.indices.size() > SUPER_LARGE_FACES) { + m_state->config.max_error = 0.08f; + } else if (_its.indices.size() > LARGE_FACES) { + m_state->config.max_error = 0.05f; + } + } + + //std::mutex m_state_mutex; + std::thread m_worker = std::thread( + [&va](std::unique_ptr its, std::unique_ptr state) { + // Checks that the UI thread did not request cancellation, throws if so. + std::function throw_on_cancel = []() { + }; + std::function statusfn = [&state](int percent) { + state->progress = percent; + }; + // Initialize. + uint32_t triangle_count = 0; + float max_error = std::numeric_limits::max(); + { + if (state->config.use_count) + triangle_count = state->config.wanted_count; + if (!state->config.use_count) + max_error = state->config.max_error; + state->progress = 0; + state->result.reset(); + state->status = State::Status::running; + } + TriangleMesh origin_mesh(*its); + try { // Start the actual calculation. + its_quadric_edge_collapse(*its, triangle_count, &max_error, throw_on_cancel, statusfn); + } catch (std::exception&) { + state->status = State::idle; + } + if (state->status == State::Status::running) { + // We were not cancelled, the result is valid. + state->status = State::Status::idle; + state->result = std::move(its); + } + if (state->result) { + TriangleMesh mesh(*state->result); + float eps = 1.0f; + Vec3f origin_min = origin_mesh.stats().min - Vec3f(eps, eps, eps); + Vec3f origin_max = origin_mesh.stats().max + Vec3f(eps, eps, eps); + if (origin_min.x() < mesh.stats().min.x() && origin_min.y() < mesh.stats().min.y() && origin_min.z() < mesh.stats().min.z()&& + origin_max.x() > mesh.stats().max.x() && origin_max.y() > mesh.stats().max.y() && origin_max.z() > mesh.stats().max.z()) { + va.load_mesh(mesh); + } + else { + state->status = State::cancelling; + } + } + }, + std::move(its),std::move(m_state)); + if (m_worker.joinable()) { + m_worker.detach(); + } + return true; +} + Transform3d GLVolume::world_matrix() const { Transform3d m = m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); @@ -687,7 +815,11 @@ void GLVolume::render(bool with_outline, const std::array& body_color) glFrontFace(GL_CW); glsafe(::glCullFace(GL_BACK)); glsafe(::glPushMatrix()); - + auto camera = GUI::wxGetApp().plater()->get_camera(); + auto zoom = camera.get_zoom(); + Transform3d vier_mat = camera.get_view_matrix(); + Matrix4d vier_proj_mat = camera.get_projection_matrix().matrix() * vier_mat.matrix(); + const std::array &viewport = camera.get_viewport(); // BBS: add logic for mmu segmentation rendering auto render_body = [&]() { bool color_volume = false; @@ -716,7 +848,6 @@ void GLVolume::render(bool with_outline, const std::array& body_color) mmuseg_ivas[idx].load_its_flat_shading(its_per_color[idx]); mmuseg_ivas[idx].finalize_geometry(true); } - mmuseg_ts = mv->mmu_segmentation_facets.timestamp(); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__<< boost::format(", this %1%, name %2%, new mmuseg_ts %3%, new color size %4%") %this %this->name %mmuseg_ts %mmuseg_ivas.size(); @@ -734,8 +865,8 @@ void GLVolume::render(bool with_outline, const std::array& body_color) } glsafe(::glMultMatrixd(world_matrix().data())); for (int idx = 0; idx < mmuseg_ivas.size(); idx++) { - GLIndexedVertexArray& iva = mmuseg_ivas[idx]; - if (iva.triangle_indices_size == 0 && iva.quad_indices_size == 0) + GLIndexedVertexArray* iva = &mmuseg_ivas[idx]; + if (iva->triangle_indices_size == 0 && iva->quad_indices_size == 0) continue; if (shader) { @@ -763,7 +894,7 @@ void GLVolume::render(bool with_outline, const std::array& body_color) } } } - iva.render(this->tverts_range, this->qverts_range); + iva->render(this->tverts_range, this->qverts_range); /*if (force_native_color && (render_color[3] < 1.0)) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__<< boost::format(", this %1%, name %2%, tverts_range {%3,%4}, qverts_range{%5%, %6%}") %this %this->name %this->tverts_range.first %this->tverts_range.second @@ -773,7 +904,32 @@ void GLVolume::render(bool with_outline, const std::array& body_color) } else { glsafe(::glMultMatrixd(world_matrix().data())); - this->indexed_vertex_array->render(this->tverts_range, this->qverts_range); + auto render_which = [this](std::shared_ptr cur) { + if (cur->vertices_and_normals_interleaved_VBO_id > 0) { + cur->render(tverts_range_lod, qverts_range_lod); + } else {// if (cur->vertices_and_normals_interleaved_VBO_id == 0) + if (cur->triangle_indices.size() > 0) { + cur->finalize_geometry(true); + cur->render(tverts_range_lod, qverts_range_lod); + } else { + indexed_vertex_array->render(this->tverts_range, this->qverts_range); + } + } + }; + Transform3d world_tran = world_matrix(); + m_lod_update_index++; + if (abs(zoom - LAST_CAMERA_ZOOM_VALUE) > ZOOM_THRESHOLD || m_lod_update_index >= LOD_UPDATE_FREQUENCY){ + m_lod_update_index = 0; + LAST_CAMERA_ZOOM_VALUE = zoom; + m_cur_lod_level = calc_volume_box_in_screen_bigger_than_threshold(bounding_box(), world_tran, vier_proj_mat, viewport[2], viewport[3]); + } + if (m_cur_lod_level == LOD_LEVEL::SMALL && indexed_vertex_array_small) { + render_which(indexed_vertex_array_small); + } else if (m_cur_lod_level == LOD_LEVEL::MIDDLE && indexed_vertex_array_middle) { + render_which(indexed_vertex_array_middle); + } else { + this->indexed_vertex_array->render(this->tverts_range, this->qverts_range); + } } }; @@ -887,9 +1043,22 @@ void GLVolume::render(bool with_outline, const std::array& body_color) glsafe(::glPushMatrix()); Transform3d matrix = world_matrix(); + Transform3d world_tran = matrix; matrix.scale(scale); glsafe(::glMultMatrixd(matrix.data())); - this->indexed_vertex_array->render(this->tverts_range, this->qverts_range); + m_lod_update_index++; + if (abs(zoom - LAST_CAMERA_ZOOM_VALUE) > ZOOM_THRESHOLD || m_lod_update_index >= LOD_UPDATE_FREQUENCY) { + m_lod_update_index = 0; + LAST_CAMERA_ZOOM_VALUE = zoom; + m_cur_lod_level = calc_volume_box_in_screen_bigger_than_threshold(bounding_box(), world_tran, vier_proj_mat, viewport[2], viewport[3]); + } + if (m_cur_lod_level == LOD_LEVEL::SMALL && indexed_vertex_array_small && indexed_vertex_array_small->vertices_and_normals_interleaved_VBO_id > 0) { + this->indexed_vertex_array_small->render(this->tverts_range_lod, this->qverts_range_lod); + } else if (m_cur_lod_level == LOD_LEVEL::MIDDLE && indexed_vertex_array_middle && indexed_vertex_array_middle->vertices_and_normals_interleaved_VBO_id > 0) { + this->indexed_vertex_array_middle->render(this->tverts_range_lod, this->qverts_range_lod); + } else { + this->indexed_vertex_array->render(this->tverts_range, this->qverts_range); + } //BOOST_LOG_TRIVIAL(info) << boost::format(": %1%, outline render for body, shader name %2%")%__LINE__ %shader->get_name(); shader->set_uniform("is_outline", false); @@ -998,6 +1167,14 @@ void GLVolume::simple_render(GLShaderProgram *shader, ModelObjectPtrs &model_obj glFrontFace(GL_CCW); } +void GLVolume::set_bounding_boxes_as_dirty() +{ + m_lod_update_index = LOD_UPDATE_FREQUENCY; + m_transformed_bounding_box.reset(); + m_transformed_convex_hull_bounding_box.reset(); + m_transformed_non_sinking_bounding_box.reset(); +} + 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); } @@ -1112,6 +1289,8 @@ int GLVolumeCollection::load_object_volume( else { GLVolume* first_volume = *(volume_set.begin()); new_volume->indexed_vertex_array = first_volume->indexed_vertex_array; + new_volume->indexed_vertex_array_middle = first_volume->indexed_vertex_array_middle; + new_volume->indexed_vertex_array_small = first_volume->indexed_vertex_array_small; need_create_mesh = false; } volume_set.emplace(new_volume); @@ -1127,6 +1306,10 @@ int GLVolumeCollection::load_object_volume( #if ENABLE_SMOOTH_NORMALS v.indexed_vertex_array->load_mesh(mesh, true); #else + if (v.indexed_vertex_array_middle == nullptr) { v.indexed_vertex_array_middle = std::make_shared(); } + v.simplify_mesh(mesh, *v.indexed_vertex_array_middle, LOD_LEVEL::MIDDLE); // include finalize_geometry + if (v.indexed_vertex_array_small == nullptr) { v.indexed_vertex_array_small = std::make_shared(); } + v.simplify_mesh(mesh, *v.indexed_vertex_array_small, LOD_LEVEL::SMALL); v.indexed_vertex_array->load_mesh(mesh); #endif // ENABLE_SMOOTH_NORMALS v.indexed_vertex_array->finalize_geometry(opengl_initialized); @@ -1369,7 +1552,12 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, if (disable_cullface) glsafe(::glDisable(GL_CULL_FACE)); + auto camera = GUI::wxGetApp().plater()->get_camera(); for (GLVolumeWithIdAndZ& volume : to_render) { + auto world_box = volume.first->transformed_bounding_box(); + if (!camera.getFrustum().intersects(world_box)) { + continue; + } #if ENABLE_MODIFIERS_ALWAYS_TRANSPARENT if (type == ERenderType::Transparent) { volume.first->force_transparent = true; diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index d3a2cc1f3..500240e9e 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -256,8 +256,20 @@ public: private: BoundingBox m_bounding_box; }; +enum LOD_LEVEL { + HIGH, // Origin data + MIDDLE, + SMALL, +}; class GLVolume { + static float LOD_HIGH_ZOOM; + static float LOD_MIDDLE_ZOOM; + static float LOD_SMALL_ZOOM; + static float LAST_CAMERA_ZOOM_VALUE; + mutable LOD_LEVEL m_cur_lod_level = LOD_LEVEL::HIGH; + mutable unsigned char m_lod_update_index = 0; + public: std::string name; bool is_text_shape{false}; @@ -292,6 +304,9 @@ public: virtual ~GLVolume() = default; // BBS + bool simplify_mesh(const TriangleMesh &mesh, GLIndexedVertexArray &va, LOD_LEVEL lod) const; + bool simplify_mesh(const indexed_triangle_set &_its, GLIndexedVertexArray &va, LOD_LEVEL lod) const; + protected: Geometry::Transformation m_instance_transformation; Geometry::Transformation m_volume_transformation; @@ -327,6 +342,30 @@ protected: SinkingContours m_sinking_contours; + // guards m_state + struct Configuration + { + bool use_count = false;//diff with glgizmoSimplify + float decimate_ratio = 50.f; // in percent + uint32_t wanted_count = 0; // initialize by percents + float max_error = 1.; // maximal quadric error + + bool operator==(const Configuration &rhs) + { + return (use_count == rhs.use_count && decimate_ratio == rhs.decimate_ratio && wanted_count == rhs.wanted_count && max_error == rhs.max_error); + } + bool operator!=(const Configuration &rhs) { return !(*this == rhs); } + }; + struct State + { + enum Status { idle, running, cancelling }; + + Status status = idle; + int progress = 0; // percent of done work + Configuration config; // Configuration we started with. + const ModelVolume * mv = nullptr; + std::unique_ptr result; + }; public: // Color of the triangles / quads held by this volume. std::array color; @@ -400,7 +439,9 @@ public: EHoverState hover; // Interleaved triangles & normals with indexed triangles & quads. - std::shared_ptr indexed_vertex_array; + std::shared_ptr indexed_vertex_array; + std::shared_ptr indexed_vertex_array_middle; + std::shared_ptr indexed_vertex_array_small; const TriangleMesh * ori_mesh{nullptr}; // BBS mutable std::vector mmuseg_ivas; @@ -409,7 +450,8 @@ public: // Ranges of triangle and quad indices to be rendered. std::pair tverts_range; std::pair qverts_range; - + std::pair tverts_range_lod; + std::pair qverts_range_lod; // If the qverts or tverts contain thick extrusions, then offsets keeps pointers of the starts // of the extrusions per layer. std::vector print_zs; @@ -533,11 +575,7 @@ public: void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array->finalize_geometry(opengl_initialized); } void release_geometry() { this->indexed_vertex_array->release_geometry(); } - void set_bounding_boxes_as_dirty() { - m_transformed_bounding_box.reset(); - m_transformed_convex_hull_bounding_box.reset(); - m_transformed_non_sinking_bounding_box.reset(); - } + void set_bounding_boxes_as_dirty(); bool is_sla_support() const; bool is_sla_pad() const; @@ -573,6 +611,7 @@ typedef std::vector GLVolumePtrs; typedef std::pair> GLVolumeWithIdAndZ; typedef std::vector GLVolumeWithIdAndZList; + class GLVolumeCollection { public: diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index c540f03d8..75ebc4928 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -56,7 +56,7 @@ void Camera::select_next_type() void Camera::translate(const Vec3d& displacement) { if (!displacement.isApprox(Vec3d::Zero())) { m_view_matrix.translate(-displacement); - update_target(); + update_target(); } } @@ -106,6 +106,104 @@ void Camera::select_view(const std::string& direction) look_at(m_target - 0.707 * m_distance * Vec3d::UnitY() + 0.707 * m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY() + Vec3d::UnitZ()); } } +//how to use +//BoundingBox bbox = mesh.aabb.transform(transform); +//return camera_->getFrustum().intersects(bbox); +void Camera::update_frustum() +{ + auto eye_ = get_position().cast(); + auto center_ = get_target().cast(); + auto up_ = get_dir_up().cast(); + float near_ = m_frustrum_zs.first; + float far_ = m_frustrum_zs.second; + float aspect_ = m_viewport[2] / (double)m_viewport[3]; + float fov_ = (float) Geometry::deg2rad(get_fov()); + float eps = 0.01; + if (m_last_eye.isApprox(eye_) && m_last_center.isApprox(center_) && m_last_up.isApprox(up_) && + abs(m_last_near - near_) > eps && abs(m_last_far - far_) > eps && + abs(m_last_aspect - aspect_) > eps && abs(m_last_fov - fov_) > eps) { + return; + } + m_last_eye = eye_; + m_last_center = center_; + m_last_up = up_; + m_last_near = near_; + m_last_far = far_; + m_last_aspect = aspect_; + m_last_fov = fov_; + Vec3f forward((center_ - eye_).normalized()); + Vec3f side((forward.cross(up_)).normalized()); + Vec3f up((side.cross(forward)).normalized()); + + float nearHeightHalf = near_ * std::tan(fov_ / 2.f); + float farHeightHalf = far_ * std::tan(fov_ / 2.f); + float nearWidthHalf = nearHeightHalf * aspect_; + float farWidthHalf = farHeightHalf * aspect_; + + // near plane + Vec3f nearCenter = eye_ + forward * near_; + Vec3f nearNormal = forward; + m_frustum.planes[0].set(nearNormal, nearCenter); + + // far plane + Vec3f farCenter = eye_ + forward * far_; + Vec3f farNormal = -forward; + m_frustum.planes[1].set(farNormal, farCenter); + + // top plane + Vec3f topCenter = nearCenter + up * nearHeightHalf; + Vec3f topNormal = (topCenter - eye_).normalized().cross(side); + m_frustum.planes[2].set(topNormal, topCenter); + + // bottom plane + Vec3f bottomCenter = nearCenter - up * nearHeightHalf; + Vec3f bottomNormal = side.cross((bottomCenter - eye_).normalized()); + m_frustum.planes[3].set(bottomNormal, bottomCenter); + + // left plane + Vec3f leftCenter = nearCenter - side * nearWidthHalf; + Vec3f leftNormal = (leftCenter - eye_).normalized().cross(up); + m_frustum.planes[4].set(leftNormal, leftCenter); + + // right plane + Vec3f rightCenter = nearCenter + side * nearWidthHalf; + Vec3f rightNormal = up.cross((rightCenter - eye_).normalized()); + m_frustum.planes[5].set(rightNormal, rightCenter); + + //// 8 corners + Vec3f nearTopLeft = nearCenter + up * nearHeightHalf - side * nearWidthHalf; + Vec3f nearTopRight = nearCenter + up * nearHeightHalf + side * nearWidthHalf; + Vec3f nearBottomLeft = nearCenter - up * nearHeightHalf - side * nearWidthHalf; + Vec3f nearBottomRight = nearCenter - up * nearHeightHalf + side * nearWidthHalf; + + Vec3f farTopLeft = farCenter + up * farHeightHalf - side * farWidthHalf; + Vec3f farTopRight = farCenter + up * farHeightHalf + side * farWidthHalf; + Vec3f farBottomLeft = farCenter - up * farHeightHalf - side * farWidthHalf; + Vec3f farBottomRight = farCenter - up * farHeightHalf + side * farWidthHalf; + + m_frustum.corners[0] = nearTopLeft; + m_frustum.corners[1] = nearTopRight; + m_frustum.corners[2] = nearBottomLeft; + m_frustum.corners[3] = nearBottomRight; + m_frustum.corners[4] = farTopLeft; + m_frustum.corners[5] = farTopRight; + m_frustum.corners[6] = farBottomLeft; + m_frustum.corners[7] = farBottomRight; + // bounding box + auto double_min = std::numeric_limits::min(); + auto double_max = std::numeric_limits::max(); + m_frustum.bbox.min = Vec3d(double_max, double_max, double_max); + m_frustum.bbox.max = Vec3d(double_min, double_min, double_min); + for (auto &corner : m_frustum.corners) { + m_frustum.bbox.min[0] = std::min((float)m_frustum.bbox.min.x(), corner.x()); + m_frustum.bbox.min[1] = std::min((float)m_frustum.bbox.min.y(), corner.y()); + m_frustum.bbox.min[2] = std::min((float)m_frustum.bbox.min.z(), corner.z()); + + m_frustum.bbox.max[0] = std::max((float)m_frustum.bbox.max.x(), corner.x()); + m_frustum.bbox.max[1] = std::max((float)m_frustum.bbox.max.y(), corner.y()); + m_frustum.bbox.max[2] = std::max((float)m_frustum.bbox.max.z(), corner.z()); + } +} double Camera::get_fov() const { @@ -220,7 +318,7 @@ void Camera::zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor) } #if ENABLE_CAMERA_STATISTICS -void Camera::debug_render() const +void Camera::debug_render() { ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.begin(std::string("Camera statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); @@ -520,7 +618,7 @@ void Camera::set_distance(double distance) if (m_distance != distance) { m_view_matrix.translate((distance - m_distance) * get_dir_forward()); m_distance = distance; - + update_target(); } } @@ -626,7 +724,7 @@ void Camera::update_target() { Vec3d temptarget = get_position() + m_distance * get_dir_forward(); if (!(temptarget-m_target).isApprox(Vec3d::Zero())){ m_target = temptarget; - } + } } } // GUI diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index 64cecad42..9f16ce126 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -2,6 +2,7 @@ #define slic3r_Camera_hpp_ #include "libslic3r/BoundingBox.hpp" +#include "libslic3r/Frustum.hpp" #include "3DScene.hpp" #include @@ -55,6 +56,9 @@ private: std::pair m_frustrum_zs; BoundingBoxf3 m_scene_box; + Frustum m_frustum; + Vec3f m_last_eye, m_last_center, m_last_up; + float m_last_near, m_last_far, m_last_aspect, m_last_fov; public: Camera() { set_default_orientation(); } @@ -69,7 +73,7 @@ public: void enable_update_config_on_type_change(bool enable) { m_update_config_on_type_change_enabled = enable; } void translate(const Vec3d& displacement); - const Vec3d& get_target() { + const Vec3d& get_target() { update_target(); return m_target; } void set_target(const Vec3d& target); @@ -100,9 +104,9 @@ public: Vec3d get_dir_up() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(1); } Vec3d get_dir_forward() const { return -m_view_matrix.matrix().block(0, 0, 3, 3).row(2); } - Vec3d get_position() const { return m_view_matrix.matrix().inverse().block(0, 3, 3, 1); } - + const Frustum & getFrustum() const { return m_frustum; } + void update_frustum(); double get_near_z() const { return m_frustrum_zs.first; } double get_far_z() const { return m_frustrum_zs.second; } const std::pair& get_z_range() const { return m_frustrum_zs; } @@ -119,7 +123,7 @@ public: void zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor = DefaultZoomToVolumesMarginFactor); #if ENABLE_CAMERA_STATISTICS - void debug_render() const; + void debug_render(); #endif // ENABLE_CAMERA_STATISTICS // translate the camera in world space diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 75681da15..964cef175 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1991,7 +1991,7 @@ void GLCanvas3D::render(bool only_init) #if ENABLE_SHOW_CAMERA_TARGET _render_camera_target(); #endif // ENABLE_SHOW_CAMERA_TARGET - + camera.update_frustum(); if (m_picking_enabled && m_rectangle_selection.is_dragging()) m_rectangle_selection.render(*this); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 4595711ca..66f5deeb7 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -805,6 +805,28 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxWindow *pa } } + if (param == "enable_lod") { + if (wxGetApp().plater()->is_project_dirty()) { + auto result = MessageDialog(static_cast(this), _L("The current project has unsaved changes, save it before continuing?"), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Save"), wxYES_NO | wxYES_DEFAULT | wxCENTRE) + .ShowModal(); + if (result == wxID_YES) { + wxGetApp().plater()->save_project(); + } + } + MessageDialog msg_wingow(nullptr, _L("Please note that the model show will undergo certain changes at small pixels case.\nEnabled LOD requires application restart.") + "\n" + _L("Do you want to continue?"), _L("Enable LOD"), + wxYES| wxYES_DEFAULT | wxCANCEL | wxCENTRE); + if (msg_wingow.ShowModal() == wxID_YES) { + Close(); + GetParent()->RemoveChild(this); + wxGetApp().recreate_GUI(_L("Enable LOD")); + } else { + checkbox->SetValue(!checkbox->GetValue()); + app_config->set_bool(param, checkbox->GetValue()); + app_config->save(); + } + } + e.Skip(); }); @@ -1107,6 +1129,9 @@ wxWindow* PreferencesDialog::create_general_page() auto item_mouse_zoom_settings = create_item_checkbox(_L("Zoom to mouse position"), page, _L("Zoom in towards the mouse pointer's position in the 3D view, rather than the 2D window center."), 50, "zoom_to_mouse"); + auto enable_lod_settings = create_item_checkbox(_L("Improve rendering performance by lod"), page, + _L("Improved rendering performance under the scene of multiple plates and many models."), 50, + "enable_lod"); float range_min = 1.0, range_max = 2.5; auto item_grabber_size_settings = create_item_range_input(_L("Grabber scale"), page, _L("Set grabber size for move,rotate,scale tool.") + _L("Value range") + ":[" + std::to_string(range_min) + "," + @@ -1188,6 +1213,7 @@ wxWindow* PreferencesDialog::create_general_page() sizer_page->Add(item_multi_machine, 0, wxTOP, FromDIP(3)); sizer_page->Add(_3d_settings, 0, wxTOP | wxEXPAND, FromDIP(20)); sizer_page->Add(item_mouse_zoom_settings, 0, wxTOP, FromDIP(3)); + sizer_page->Add(enable_lod_settings, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_grabber_size_settings, 0, wxTOP, FromDIP(3)); sizer_page->Add(title_presets, 0, wxTOP | wxEXPAND, FromDIP(20)); sizer_page->Add(item_user_sync, 0, wxTOP, FromDIP(3)); diff --git a/src/slic3r/GUI/Widgets/ProgressDialog.hpp b/src/slic3r/GUI/Widgets/ProgressDialog.hpp index 597ec7f80..4b1146414 100644 --- a/src/slic3r/GUI/Widgets/ProgressDialog.hpp +++ b/src/slic3r/GUI/Widgets/ProgressDialog.hpp @@ -33,7 +33,7 @@ public: void OnPaint(wxPaintEvent &evt); virtual ~ProgressDialog(); - virtual void DoSetSize(int x, int y, int width, int height, int sizeFlags = wxSIZE_AUTO); + virtual void DoSetSize(int x, int y, int width, int height, int sizeFlags = wxSIZE_AUTO)override; bool Create(const wxString &title, const wxString &message, int maximum = 100, wxWindow *parent = NULL, int style = wxPD_APP_MODAL | wxPD_AUTO_HIDE); virtual bool Update(int value, const wxString &newmsg = wxEmptyString, bool *skip = NULL);