ENH: wireframe: refine the rendering logic under paint

1. remove wireframe in 3d view, only keep in paint;
2. pass barycentric_coordinates from outside
3. add shortcut hints

Change-Id: I911e5cdf3475926d9527dc0839fdce072ed54746
(cherry picked from commit 6e16d0ccfb71741e55daabd757be9f9e7613e695)
This commit is contained in:
lane.wei 2022-10-25 15:55:35 +08:00 committed by Lane.Wei
parent 79b5c94f4f
commit a9a228d071
9 changed files with 137 additions and 39 deletions

View file

@ -1,4 +1,4 @@
#version 130
#version 110
#define INTENSITY_CORRECTION 0.6
@ -45,6 +45,10 @@ float edgeFactor(float lineWidth) {
vec3 wireframe(vec3 fill, vec3 stroke, float lineWidth) {
return mix(stroke, fill, edgeFactor(lineWidth));
//if (any(lessThan(barycentric_coordinates, vec3(0.005, 0.005, 0.005))))
// return vec3(1.0, 0.0, 0.0);
//else
// return fill;
}
vec3 getWireframeColor(vec3 fill) {

View file

@ -1,7 +1,10 @@
#version 130
#version 110
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
attribute vec3 v_position;
attribute vec3 v_barycentric;
uniform mat4 volume_world_matrix;
// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane.
uniform vec2 z_range;
@ -22,15 +25,19 @@ struct SlopeDetection
uniform SlopeDetection slope;
void main()
{
model_pos = gl_Vertex;
//model_pos = gl_Vertex;
model_pos = vec4(v_position, 1.0);
// Point in homogenous coordinates.
vec4 world_pos = volume_world_matrix * gl_Vertex;
//vec4 world_pos = volume_world_matrix * gl_Vertex;
vec4 world_pos = volume_world_matrix * model_pos;
gl_Position = ftransform();
//gl_Position = ftransform();
gl_Position = gl_ModelViewProjectionMatrix * vec4(v_position.x, v_position.y, v_position.z, 1.0);
// Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
//compute the Barycentric Coordinates
int vertexMod3 = gl_VertexID % 3;
barycentric_coordinates = vec3(float(vertexMod3 == 0), float(vertexMod3 == 1), float(vertexMod3 == 2));
//int vertexMod3 = gl_VertexID % 3;
//barycentric_coordinates = vec3(float(vertexMod3 == 0), float(vertexMod3 == 1), float(vertexMod3 == 2));
barycentric_coordinates = v_barycentric;
}

View file

@ -731,8 +731,8 @@ bool GLCanvas3D::init()
Bed3D::load_render_colors();
#endif
if (wxGetApp().is_gl_version_greater_or_equal_to(3, 0))
wxGetApp().plater()->enable_wireframe(true);
//if (!wxGetApp().is_gl_version_greater_or_equal_to(3, 0))
// wxGetApp().plater()->enable_wireframe(false);
m_initialized = true;
return true;
@ -5762,12 +5762,12 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
// GLGizmosManager::EType type = gm.get_current_type();
if (dynamic_cast<GLGizmoPainterBase*>(gm.get_current()) == nullptr)
{
if (wxGetApp().plater()->is_wireframe_enabled()) {
/*if (wxGetApp().plater()->is_wireframe_enabled()) {
if (wxGetApp().plater()->is_show_wireframe())
shader->set_uniform("show_wireframe", true);
else
shader->set_uniform("show_wireframe", false);
}
}*/
//BBS:add assemble view related logic
// do not cull backfaces to show broken geometry, if any
m_volumes.render(type, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this, canvas_type](const GLVolume& volume) {
@ -5795,12 +5795,12 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
}
case GLVolumeCollection::ERenderType::Transparent:
{
if (wxGetApp().plater()->is_wireframe_enabled()) {
/*if (wxGetApp().plater()->is_wireframe_enabled()) {
if (wxGetApp().plater()->is_show_wireframe())
shader->set_uniform("show_wireframe", true);
else
shader->set_uniform("show_wireframe", false);
}
}*/
//BBS:add assemble view related logic
m_volumes.render(type, false, wxGetApp().plater()->get_camera().get_view_matrix(), [this, canvas_type](const GLVolume& volume) {
if (canvas_type == ECanvasType::CanvasAssembleView) {
@ -5820,9 +5820,9 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
}
}
if (wxGetApp().plater()->is_wireframe_enabled()) {
/*if (wxGetApp().plater()->is_wireframe_enabled()) {
shader->set_uniform("show_wireframe", false);
}
}*/
shader->stop_using();
}

View file

@ -45,7 +45,8 @@ std::pair<bool, std::string> GLShadersManager::init()
// used to render extrusion and travel paths as lines in gcode preview
valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" });
// used to render objects in 3d editor
if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 0)) {
//if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 0)) {
if (0) {
valid &= append_shader("gouraud", { "gouraud_130.vs", "gouraud_130.fs" }
#if ENABLE_ENVIRONMENT_MAP
, { "ENABLE_ENVIRONMENT_MAP"sv }
@ -70,16 +71,16 @@ std::pair<bool, std::string> GLShadersManager::init()
// Since macOS 12 (Monterey), this issue with the opposite direction on Apple's Arm CPU seems to be fixed, and computed
// triangle normals inside fragment shader have the right direction.
if (platform_flavor() == PlatformFlavor::OSXOnArm && wxPlatformInfo::Get().GetOSMajorVersion() < 12) {
if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 0))
//if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 0))
valid &= append_shader("mm_gouraud", {"mm_gouraud_130.vs", "mm_gouraud_130.fs"}, {"FLIP_TRIANGLE_NORMALS"sv});
else
valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"}, {"FLIP_TRIANGLE_NORMALS"sv});
//else
// valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"}, {"FLIP_TRIANGLE_NORMALS"sv});
}
else {
if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 0))
valid &= append_shader("mm_gouraud", {"mm_gouraud_130.vs", "mm_gouraud_130.fs"});
else
valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"});
//if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 0))
valid &= append_shader("mm_gouraud", {"mm_gouraud_wireframe.vs", "mm_gouraud_wireframe.fs"});
//else
// valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"});
}
//BBS: add shader for outline

View file

@ -131,6 +131,10 @@ bool GLGizmoMmuSegmentation::on_init()
m_desc["height_range_caption"] = _L("Ctrl + Mouse wheel");
m_desc["height_range"] = _L("Height range");
//add toggle wire frame hint
m_desc["toggle_wireframe_caption"] = _L("Ctrl + Shift + Enter");
m_desc["toggle_wireframe"] = _L("Toggle Wireframe");
init_extruders_data();
return true;
@ -315,16 +319,16 @@ void GLGizmoMmuSegmentation::show_tooltip_information(float caption_max, float x
std::vector<std::string> tip_items;
switch (m_tool_type) {
case ToolType::BRUSH:
tip_items = {"paint", "erase", "cursor_size", "clipping_of_view"};
tip_items = {"paint", "erase", "cursor_size", "clipping_of_view", "toggle_wireframe"};
break;
case ToolType::BUCKET_FILL:
tip_items = {"paint", "erase", "smart_fill_angle", "clipping_of_view"};
tip_items = {"paint", "erase", "smart_fill_angle", "clipping_of_view", "toggle_wireframe"};
break;
case ToolType::SMART_FILL:
// TODO:
break;
case ToolType::GAP_FILL:
tip_items = {"gap_area"};
tip_items = {"gap_area", "toggle_wireframe"};
break;
default:
break;
@ -727,6 +731,7 @@ void GLGizmoMmuSegmentation::init_model_triangle_selectors()
EnforcerBlockerType max_ebt = (EnforcerBlockerType)std::min(m_extruders_colors.size(), (size_t)EnforcerBlockerType::ExtruderMax);
m_triangle_selectors.back()->deserialize(mv->mmu_segmentation_facets.get_data(), false, max_ebt);
m_triangle_selectors.back()->request_update_render_data();
m_triangle_selectors.back()->set_wireframe_needed(true);
m_volumes_extruder_idxs.push_back(mv->extruder_id());
}
}

View file

@ -1076,12 +1076,20 @@ void TriangleSelectorPatch::render(ImGuiWrapper* imgui)
if (!shader)
return;
assert(shader->get_name() == "mm_gouraud");
GLint position_id = -1;
GLint barycentric_id = -1;
if (wxGetApp().plater()->is_wireframe_enabled()) {
if (wxGetApp().plater()->is_show_wireframe())
position_id = shader->get_attrib_location("v_position");
barycentric_id = shader->get_attrib_location("v_barycentric");
if (m_need_wireframe && wxGetApp().plater()->is_show_wireframe()) {
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", show_wireframe on");
shader->set_uniform("show_wireframe", true);
else
}
else {
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", show_wireframe off");
shader->set_uniform("show_wireframe", false);
}
}
for (size_t buffer_idx = 0; buffer_idx < m_triangle_patches.size(); ++buffer_idx) {
if (this->has_VBOs(buffer_idx)) {
@ -1100,11 +1108,13 @@ void TriangleSelectorPatch::render(ImGuiWrapper* imgui)
std::array<float, 4> new_color = adjust_color_for_rendering(color);
shader->set_uniform("uniform_color", new_color);
//shader->set_uniform("uniform_color", color);
this->render(buffer_idx);
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", buffer_idx %1%: new_color[%2%, %3%, %4%, %5%]")%buffer_idx%new_color[0]%new_color[1]%new_color[2]%new_color[3];
this->render(buffer_idx, (int)position_id, (int)barycentric_id);
}
}
if (m_paint_contour.has_VBO()) {
if (m_paint_contour.has_VBO())
{
ScopeGuard guard_mm_gouraud([shader]() { shader->start_using(); });
shader->stop_using();
@ -1123,6 +1133,7 @@ void TriangleSelectorPatch::render(ImGuiWrapper* imgui)
void TriangleSelectorPatch::update_triangles_per_type()
{
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", enter");
m_triangle_patches.resize((int)EnforcerBlockerType::ExtruderMax + 1);
for (int i = 0; i < m_triangle_patches.size(); i++) {
auto& patch = m_triangle_patches[i];
@ -1130,6 +1141,8 @@ void TriangleSelectorPatch::update_triangles_per_type()
patch.triangle_indices.reserve(m_triangles.size() / 3);
}
bool using_wireframe = (wxGetApp().plater()->is_wireframe_enabled())?true:false;
for (auto& triangle : m_triangles) {
if (!triangle.valid() || triangle.is_split())
continue;
@ -1139,13 +1152,33 @@ void TriangleSelectorPatch::update_triangles_per_type()
//patch.triangle_indices.insert(patch.triangle_indices.end(), triangle.verts_idxs.begin(), triangle.verts_idxs.end());
for (int i = 0; i < 3; ++i) {
int j = triangle.verts_idxs[i];
int index = int(patch.patch_vertices.size()/3);
int index = using_wireframe?int(patch.patch_vertices.size()/6) : int(patch.patch_vertices.size()/3);
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: i=%2%, j=%3%, index=%4%, v[%5%,%6%,%7%]")%__LINE__%i%j%index%m_vertices[j].v(0)%m_vertices[j].v(1)%m_vertices[j].v(2);
patch.patch_vertices.emplace_back(m_vertices[j].v(0));
patch.patch_vertices.emplace_back(m_vertices[j].v(1));
patch.patch_vertices.emplace_back(m_vertices[j].v(2));
if (using_wireframe) {
if (i == 0) {
patch.patch_vertices.emplace_back(1.0);
patch.patch_vertices.emplace_back(0.0);
patch.patch_vertices.emplace_back(0.0);
}
else if (i == 1) {
patch.patch_vertices.emplace_back(0.0);
patch.patch_vertices.emplace_back(1.0);
patch.patch_vertices.emplace_back(0.0);
}
else {
patch.patch_vertices.emplace_back(0.0);
patch.patch_vertices.emplace_back(0.0);
patch.patch_vertices.emplace_back(1.0);
}
}
patch.triangle_indices.emplace_back( index);
}
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: state=%2%, vertice size=%3%, triangle size %4%")%__LINE__%state%patch.patch_vertices.size()%patch.triangle_indices.size();
}
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("exit");
}
void TriangleSelectorPatch::update_selector_triangles()
@ -1166,6 +1199,8 @@ void TriangleSelectorPatch::update_triangles_per_patch()
auto [neighbors, neighbors_propagated] = this->precompute_all_neighbors();
std::vector<bool> visited(m_triangles.size(), false);
bool using_wireframe = (wxGetApp().plater()->is_wireframe_enabled())?true:false;
auto get_all_touching_triangles = [this](int facet_idx, const Vec3i& neighbors, const Vec3i& neighbors_propagated) -> std::vector<int> {
assert(facet_idx != -1 && facet_idx < int(m_triangles.size()));
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
@ -1218,10 +1253,27 @@ void TriangleSelectorPatch::update_triangles_per_patch()
Triangle& triangle = m_triangles[current_facet];
for (int i = 0; i < 3; ++i) {
int j = triangle.verts_idxs[i];
int index = int(patch.patch_vertices.size()/3);
int index = using_wireframe?int(patch.patch_vertices.size()/6) : int(patch.patch_vertices.size()/3);
patch.patch_vertices.emplace_back(m_vertices[j].v(0));
patch.patch_vertices.emplace_back(m_vertices[j].v(1));
patch.patch_vertices.emplace_back(m_vertices[j].v(2));
if (using_wireframe) {
if (i == 0) {
patch.patch_vertices.emplace_back(1.0);
patch.patch_vertices.emplace_back(0.0);
patch.patch_vertices.emplace_back(0.0);
}
else if (i == 1) {
patch.patch_vertices.emplace_back(0.0);
patch.patch_vertices.emplace_back(1.0);
patch.patch_vertices.emplace_back(0.0);
}
else {
patch.patch_vertices.emplace_back(0.0);
patch.patch_vertices.emplace_back(0.0);
patch.patch_vertices.emplace_back(1.0);
}
}
patch.triangle_indices.emplace_back( index);
}
//patch.triangle_indices.insert(patch.triangle_indices.end(), triangle.verts_idxs.begin(), triangle.verts_idxs.end());
@ -1268,6 +1320,7 @@ void TriangleSelectorPatch::set_filter_state(bool is_filter_state)
void TriangleSelectorPatch::update_render_data()
{
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", m_paint_changed=%1%, m_triangle_patches.size %2%")%m_paint_changed%m_triangle_patches.size();
if (m_paint_changed || (m_triangle_patches.size() == 0)) {
this->release_geometry();
@ -1288,6 +1341,8 @@ void TriangleSelectorPatch::update_render_data()
m_paint_changed = false;
}
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", before paint_contour");
m_paint_contour.release_geometry();
std::vector<Vec2i> contour_edges = this->get_seed_fill_contour();
m_paint_contour.contour_vertices.reserve(contour_edges.size() * 6);
@ -1306,9 +1361,10 @@ void TriangleSelectorPatch::update_render_data()
m_paint_contour.contour_indices_size = m_paint_contour.contour_indices.size();
m_paint_contour.finalize_geometry();
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", exit");
}
void TriangleSelectorPatch::render(int triangle_indices_idx)
void TriangleSelectorPatch::render(int triangle_indices_idx, int position_id, int barycentric_id)
{
assert(triangle_indices_idx < this->m_triangle_indices_VBO_ids.size());
assert(this->m_triangle_patches.size() == this->m_triangle_indices_VBO_ids.size());
@ -1321,9 +1377,22 @@ void TriangleSelectorPatch::render(int triangle_indices_idx)
//glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), (const void*)(0 * sizeof(float))));
if (this->m_triangle_indices_sizes[triangle_indices_idx] > 0) {
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->m_vertices_VBO_ids[triangle_indices_idx]));
if (position_id != -1) {
glsafe(::glEnableVertexAttribArray((GLint)position_id));
glsafe(::glVertexAttribPointer((GLint)position_id, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), nullptr));
}
else {
glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), nullptr));
}
if (barycentric_id != -1) {
glsafe(::glEnableVertexAttribArray((GLint)barycentric_id));
glsafe(::glVertexAttribPointer((GLint)barycentric_id, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (GLvoid*)(intptr_t)(3 * sizeof(float))));
}
//glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), nullptr));
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: triangle_indices_idx %2%, bind vertex vbo, buffer id %3%")%__LINE__%triangle_indices_idx%this->m_vertices_VBO_ids[triangle_indices_idx];
}
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
// Render using the Vertex Buffer Objects.
@ -1331,9 +1400,14 @@ void TriangleSelectorPatch::render(int triangle_indices_idx)
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->m_triangle_indices_VBO_ids[triangle_indices_idx]));
glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(this->m_triangle_indices_sizes[triangle_indices_idx]), GL_UNSIGNED_INT, nullptr));
glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: triangle_indices_idx %2%, bind indices vbo, buffer id %3%")%__LINE__%triangle_indices_idx%this->m_triangle_indices_VBO_ids[triangle_indices_idx];
}
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
if ((this->m_triangle_indices_sizes[triangle_indices_idx] > 0)&&(position_id != -1))
glsafe(::glDisableVertexAttribArray(position_id));
if ((this->m_triangle_indices_sizes[triangle_indices_idx] > 0)&&(barycentric_id != -1))
glsafe(::glDisableVertexAttribArray((GLint)barycentric_id));
if (this->m_triangle_indices_sizes[triangle_indices_idx] > 0)
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
@ -1354,6 +1428,8 @@ void TriangleSelectorPatch::release_geometry()
triangle_indices_VBO_id = 0;
}
this->clear();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: released geometry")%__LINE__;
}
void TriangleSelectorPatch::finalize_vertices()
@ -1382,6 +1458,7 @@ void TriangleSelectorPatch::finalize_triangle_indices()
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vertices_VBO_ids[buffer_idx]));
glsafe(::glBufferData(GL_ARRAY_BUFFER, patch_vertices.size() * sizeof(float), patch_vertices.data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: buffer_idx %2%, vertices size %3%, buffer id %4%")%__LINE__%buffer_idx%patch_vertices.size()%m_vertices_VBO_ids[buffer_idx];
patch_vertices.clear();
}
@ -1392,6 +1469,7 @@ void TriangleSelectorPatch::finalize_triangle_indices()
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_triangle_indices_VBO_ids[buffer_idx]));
glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, triangle_indices.size() * sizeof(int), triangle_indices.data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: buffer_idx %2%, vertices size %3%, buffer id %4%")%__LINE__%buffer_idx%triangle_indices.size()%m_triangle_indices_VBO_ids[buffer_idx];
triangle_indices.clear();
}
}

View file

@ -73,6 +73,8 @@ public:
// to be already set.
virtual void render(ImGuiWrapper *imgui);
void render() { this->render(nullptr); }
void set_wireframe_needed(bool need_wireframe) { m_need_wireframe = need_wireframe; }
bool get_wireframe_needed() { return m_need_wireframe; }
// BBS
void request_update_render_data(bool paint_changed = false)
@ -108,6 +110,7 @@ private:
protected:
GLPaintContour m_paint_contour;
bool m_need_wireframe {false};
};
// BBS
@ -198,7 +201,7 @@ protected:
private:
void update_render_data();
void render(int buffer_idx);
void render(int buffer_idx, int position_id = -1, int barycentric_id = -1);
};

View file

@ -2093,10 +2093,10 @@ void MainFrame::init_menubar_as_editor()
[this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); }, this,
[this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this);
viewMenu->AppendSeparator();
/*viewMenu->AppendSeparator();
append_menu_check_item(viewMenu, wxID_ANY, _L("Show &Wireframe") + "\tCtrl+Shift+Enter", _L("Show wireframes in 3D scene"),
[this](wxCommandEvent&) { m_plater->toggle_show_wireframe(); m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT)); }, this,
[this]() { return m_plater->is_wireframe_enabled(); }, [this]() { return m_plater->is_show_wireframe(); }, this);
[this]() { return m_plater->is_wireframe_enabled(); }, [this]() { return m_plater->is_show_wireframe(); }, this);*/
//viewMenu->AppendSeparator();
////BBS orthogonal view

View file

@ -1632,7 +1632,7 @@ struct Plater::priv
bool show_render_statistic_dialog{ false };
bool show_wireframe{ false };
bool wireframe_enabled{ false };
bool wireframe_enabled{ true };
static const std::regex pattern_bundle;
static const std::regex pattern_3mf;