diff --git a/resources/shaders/gouraud_light_instanced.fs b/resources/shaders/gouraud_light_instanced.fs new file mode 100644 index 000000000..970185a00 --- /dev/null +++ b/resources/shaders/gouraud_light_instanced.fs @@ -0,0 +1,12 @@ +#version 110 + +uniform vec4 uniform_color; +uniform float emission_factor; + +// x = tainted, y = specular; +varying vec2 intensity; + +void main() +{ + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); +} diff --git a/resources/shaders/gouraud_light_instanced.vs b/resources/shaders/gouraud_light_instanced.vs new file mode 100644 index 000000000..69f58d6e1 --- /dev/null +++ b/resources/shaders/gouraud_light_instanced.vs @@ -0,0 +1,46 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +// vertex attributes +in vec3 v_position; +in vec3 v_normal; +// instance attributes +in vec3 i_offset; +in vec2 i_scales; + +// x = tainted, y = specular; +varying vec2 intensity; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 eye_normal = normalize(gl_NormalMatrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 world_position = vec4(v_position * vec3(vec2(i_scales.x), i_scales.y) + i_offset, 1.0); + vec3 eye_position = (gl_ModelViewMatrix * world_position).xyz; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_Position = gl_ProjectionMatrix * vec4(eye_position, 1.0); +} diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 01e314dfe..6afd2c043 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -51,6 +51,8 @@ #define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA0) // Enable the fix for exporting and importing to/from 3mf file of mirrored volumes #define ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT (1 && ENABLE_2_4_0_ALPHA0) +// Enable rendering seams (and other options) in preview using models +#define ENABLE_SEAMS_USING_MODELS (1 && ENABLE_2_4_0_ALPHA0) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index d7fb937d0..7b08b5f79 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -119,7 +119,7 @@ void Bed3D::Axes::render() const glsafe(::glEnable(GL_DEPTH_TEST)); shader->start_using(); - shader->set_uniform("emission_factor", 0.0); + shader->set_uniform("emission_factor", 0.0f); // x axis const_cast(&m_arrow)->set_color(-1, { 0.75f, 0.0f, 0.0f, 1.0f }); @@ -498,7 +498,7 @@ void Bed3D::render_model() const GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader != nullptr) { shader->start_using(); - shader->set_uniform("emission_factor", 0.0); + shader->set_uniform("emission_factor", 0.0f); glsafe(::glPushMatrix()); glsafe(::glTranslated(m_model_offset.x(), m_model_offset.y(), m_model_offset.z())); model->render(); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 090accb6a..9ac0536a4 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -44,10 +44,10 @@ static EMoveType buffer_type(unsigned char id) { return static_cast(static_cast(EMoveType::Retract) + id); } -static std::array decode_color(const std::string& color) { +static std::array decode_color(const std::string& color) { static const float INV_255 = 1.0f / 255.0f; - std::array ret = { 0.0f, 0.0f, 0.0f }; + std::array ret = { 0.0f, 0.0f, 0.0f, 1.0f }; const char* c = color.data() + 1; if (color.size() == 7 && color.front() == '#') { for (size_t j = 0; j < 3; ++j) { @@ -62,8 +62,8 @@ static std::array decode_color(const std::string& color) { return ret; } -static std::vector> decode_colors(const std::vector& colors) { - std::vector> output(colors.size(), { 0.0f, 0.0f, 0.0f }); +static std::vector> decode_colors(const std::vector& colors) { + std::vector> output(colors.size(), { 0.0f, 0.0f, 0.0f, 1.0f }); for (size_t i = 0; i < colors.size(); ++i) { output[i] = decode_color(colors[i]); } @@ -95,6 +95,26 @@ void GCodeViewer::VBuffer::reset() count = 0; } +#if ENABLE_SEAMS_USING_MODELS +void GCodeViewer::InstanceVBuffer::Ranges::reset() +{ + for (Range& range : ranges) { + // release gpu memory + if (range.vbo > 0) + glsafe(::glDeleteBuffers(1, &range.vbo)); + } + + ranges.clear(); +} + +void GCodeViewer::InstanceVBuffer::reset() +{ + s_ids.clear(); + buffer.clear(); + render_ranges.reset(); +} +#endif // ENABLE_SEAMS_USING_MODELS + void GCodeViewer::IBuffer::reset() { // release gpu memory @@ -125,7 +145,7 @@ bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const case EMoveType::Extrude: { // use rounding to reduce the number of generated paths return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role && - move.position[2] <= sub_paths.front().first.position[2] && feedrate == move.feedrate && fan_speed == move.fan_speed && + move.position.z() <= sub_paths.front().first.position.z() && feedrate == move.feedrate && fan_speed == move.fan_speed && height == round_to_nearest(move.height, 2) && width == round_to_nearest(move.width, 2) && matches_percent(volumetric_rate, move.volumetric_rate(), 0.05f); } @@ -136,18 +156,26 @@ bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const } } +#if ENABLE_SEAMS_USING_MODELS +void GCodeViewer::TBuffer::Model::reset() +{ + instances.reset(); +} +#endif // ENABLE_SEAMS_USING_MODELS + void GCodeViewer::TBuffer::reset() { - // release gpu memory vertices.reset(); for (IBuffer& buffer : indices) { buffer.reset(); } - // release cpu memory indices.clear(); paths.clear(); render_paths.clear(); +#if ENABLE_SEAMS_USING_MODELS + model.reset(); +#endif // ENABLE_SEAMS_USING_MODELS } void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id) @@ -176,7 +204,7 @@ GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) con const float local_t = std::clamp(global_t - static_cast(color_low_idx), 0.0f, 1.0f); // Interpolate between the low and high colors to find exactly which color the input value should get - Color ret; + Color ret = { 0.0f, 0.0f, 0.0f, 1.0f }; for (unsigned int i = 0; i < 3; ++i) { ret[i] = lerp(Range_Colors[color_low_idx][i], Range_Colors[color_high_idx][i], local_t); } @@ -195,7 +223,7 @@ void GCodeViewer::SequentialRangeCap::reset() { buffer = nullptr; ibo = 0; vbo = 0; - color = { 0.0f, 0.0f, 0.0f }; + color = { 0.0f, 0.0f, 0.0f, 1.0f }; } void GCodeViewer::SequentialView::Marker::init() @@ -207,7 +235,7 @@ void GCodeViewer::SequentialView::Marker::init() void GCodeViewer::SequentialView::Marker::set_world_position(const Vec3f& position) { m_world_position = position; - m_world_transform = (Geometry::assemble_transform((position + m_z_offset * Vec3f::UnitZ()).cast()) * Geometry::assemble_transform(m_model.get_bounding_box().size()[2] * Vec3d::UnitZ(), { M_PI, 0.0, 0.0 })).cast(); + m_world_transform = (Geometry::assemble_transform((position + m_z_offset * Vec3f::UnitZ()).cast()) * Geometry::assemble_transform(m_model.get_bounding_box().size().z() * Vec3d::UnitZ(), { M_PI, 0.0, 0.0 })).cast(); } void GCodeViewer::SequentialView::Marker::render() const @@ -223,7 +251,7 @@ void GCodeViewer::SequentialView::Marker::render() const glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); shader->start_using(); - shader->set_uniform("emission_factor", 0.0); + shader->set_uniform("emission_factor", 0.0f); glsafe(::glPushMatrix()); glsafe(::glMultMatrixf(m_world_transform.data())); @@ -484,58 +512,60 @@ void GCodeViewer::SequentialView::render(float legend_height) const } const std::vector GCodeViewer::Extrusion_Role_Colors {{ - { 0.75f, 0.75f, 0.75f }, // erNone - { 1.00f, 0.90f, 0.30f }, // erPerimeter - { 1.00f, 0.49f, 0.22f }, // erExternalPerimeter - { 0.12f, 0.12f, 1.00f }, // erOverhangPerimeter - { 0.69f, 0.19f, 0.16f }, // erInternalInfill - { 0.59f, 0.33f, 0.80f }, // erSolidInfill - { 0.94f, 0.25f, 0.25f }, // erTopSolidInfill - { 1.00f, 0.55f, 0.41f }, // erIroning - { 0.30f, 0.50f, 0.73f }, // erBridgeInfill - { 1.00f, 1.00f, 1.00f }, // erGapFill - { 0.00f, 0.53f, 0.43f }, // erSkirt - { 0.00f, 1.00f, 0.00f }, // erSupportMaterial - { 0.00f, 0.50f, 0.00f }, // erSupportMaterialInterface - { 0.70f, 0.89f, 0.67f }, // erWipeTower - { 0.37f, 0.82f, 0.58f }, // erCustom - { 0.00f, 0.00f, 0.00f } // erMixed + { 0.75f, 0.75f, 0.75f, 1.0f }, // erNone + { 1.00f, 0.90f, 0.30f, 1.0f }, // erPerimeter + { 1.00f, 0.49f, 0.22f, 1.0f }, // erExternalPerimeter + { 0.12f, 0.12f, 1.00f, 1.0f }, // erOverhangPerimeter + { 0.69f, 0.19f, 0.16f, 1.0f }, // erInternalInfill + { 0.59f, 0.33f, 0.80f, 1.0f }, // erSolidInfill + { 0.94f, 0.25f, 0.25f, 1.0f }, // erTopSolidInfill + { 1.00f, 0.55f, 0.41f, 1.0f }, // erIroning + { 0.30f, 0.50f, 0.73f, 1.0f }, // erBridgeInfill + { 1.00f, 1.00f, 1.00f, 1.0f }, // erGapFill + { 0.00f, 0.53f, 0.43f, 1.0f }, // erSkirt + { 0.00f, 1.00f, 0.00f, 1.0f }, // erSupportMaterial + { 0.00f, 0.50f, 0.00f, 1.0f }, // erSupportMaterialInterface + { 0.70f, 0.89f, 0.67f, 1.0f }, // erWipeTower + { 0.37f, 0.82f, 0.58f, 1.0f }, // erCustom + { 0.00f, 0.00f, 0.00f, 1.0f } // erMixed }}; const std::vector GCodeViewer::Options_Colors {{ - { 0.803f, 0.135f, 0.839f }, // Retractions - { 0.287f, 0.679f, 0.810f }, // Unretractions - { 0.900f, 0.900f, 0.900f }, // Seams - { 0.758f, 0.744f, 0.389f }, // ToolChanges - { 0.856f, 0.582f, 0.546f }, // ColorChanges - { 0.322f, 0.942f, 0.512f }, // PausePrints - { 0.886f, 0.825f, 0.262f } // CustomGCodes + { 0.803f, 0.135f, 0.839f, 1.0f }, // Retractions + { 0.287f, 0.679f, 0.810f, 1.0f }, // Unretractions + { 0.900f, 0.900f, 0.900f, 1.0f }, // Seams + { 0.758f, 0.744f, 0.389f, 1.0f }, // ToolChanges + { 0.856f, 0.582f, 0.546f, 1.0f }, // ColorChanges + { 0.322f, 0.942f, 0.512f, 1.0f }, // PausePrints + { 0.886f, 0.825f, 0.262f, 1.0f } // CustomGCodes }}; const std::vector GCodeViewer::Travel_Colors {{ - { 0.219f, 0.282f, 0.609f }, // Move - { 0.112f, 0.422f, 0.103f }, // Extrude - { 0.505f, 0.064f, 0.028f } // Retract + { 0.219f, 0.282f, 0.609f, 1.0f }, // Move + { 0.112f, 0.422f, 0.103f, 1.0f }, // Extrude + { 0.505f, 0.064f, 0.028f, 1.0f } // Retract }}; -const GCodeViewer::Color GCodeViewer::Wipe_Color = { 1.0f, 1.0f, 0.0f }; - const std::vector GCodeViewer::Range_Colors {{ - { 0.043f, 0.173f, 0.478f }, // bluish - { 0.075f, 0.349f, 0.522f }, - { 0.110f, 0.533f, 0.569f }, - { 0.016f, 0.839f, 0.059f }, - { 0.667f, 0.949f, 0.000f }, - { 0.988f, 0.975f, 0.012f }, - { 0.961f, 0.808f, 0.039f }, - { 0.890f, 0.533f, 0.125f }, - { 0.820f, 0.408f, 0.188f }, - { 0.761f, 0.322f, 0.235f }, - { 0.581f, 0.149f, 0.087f } // reddish + { 0.043f, 0.173f, 0.478f, 1.0f }, // bluish + { 0.075f, 0.349f, 0.522f, 1.0f }, + { 0.110f, 0.533f, 0.569f, 1.0f }, + { 0.016f, 0.839f, 0.059f, 1.0f }, + { 0.667f, 0.949f, 0.000f, 1.0f }, + { 0.988f, 0.975f, 0.012f, 1.0f }, + { 0.961f, 0.808f, 0.039f, 1.0f }, + { 0.890f, 0.533f, 0.125f, 1.0f }, + { 0.820f, 0.408f, 0.188f, 1.0f }, + { 0.761f, 0.322f, 0.235f, 1.0f }, + { 0.581f, 0.149f, 0.087f, 1.0f } // reddish }}; +const GCodeViewer::Color GCodeViewer::Wipe_Color = { 1.0f, 1.0f, 0.0f, 1.0f }; +const GCodeViewer::Color GCodeViewer::Neutral_Color = { 0.25f, 0.25f, 0.25f, 1.0f }; + GCodeViewer::GCodeViewer() { +#if !ENABLE_SEAMS_USING_MODELS // initializes non OpenGL data of TBuffers // OpenGL data are initialized into render().init_gl_data() for (size_t i = 0; i < m_buffers.size(); ++i) { @@ -569,6 +599,8 @@ GCodeViewer::GCodeViewer() } set_toolpath_move_type_visible(EMoveType::Extrude, true); +#endif // !ENABLE_SEAMS_USING_MODELS + // m_sequential_view.skip_invisible_moves = true; } @@ -610,7 +642,7 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& if (!gcode_result.bed_shape.empty()) { // bed shape detected in the gcode bed_shape = gcode_result.bed_shape; - auto bundle = wxGetApp().preset_bundle; + const auto bundle = wxGetApp().preset_bundle; if (bundle != nullptr && !m_settings_ids.printer.empty()) { const Preset* preset = bundle->printers.find_preset(m_settings_ids.printer); if (preset != nullptr) { @@ -622,23 +654,23 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& else { // adjust printbed size in dependence of toolpaths bbox const double margin = 10.0; - Vec2d min(m_paths_bounding_box.min(0) - margin, m_paths_bounding_box.min(1) - margin); - Vec2d max(m_paths_bounding_box.max(0) + margin, m_paths_bounding_box.max(1) + margin); + const Vec2d min(m_paths_bounding_box.min.x() - margin, m_paths_bounding_box.min.y() - margin); + const Vec2d max(m_paths_bounding_box.max.x() + margin, m_paths_bounding_box.max.y() + margin); - Vec2d size = max - min; + const Vec2d size = max - min; bed_shape = { - { min(0), min(1) }, - { max(0), min(1) }, - { max(0), min(1) + 0.442265 * size[1]}, - { max(0) - 10.0, min(1) + 0.4711325 * size[1]}, - { max(0) + 10.0, min(1) + 0.5288675 * size[1]}, - { max(0), min(1) + 0.557735 * size[1]}, - { max(0), max(1) }, - { min(0) + 0.557735 * size[0], max(1)}, - { min(0) + 0.5288675 * size[0], max(1) - 10.0}, - { min(0) + 0.4711325 * size[0], max(1) + 10.0}, - { min(0) + 0.442265 * size[0], max(1)}, - { min(0), max(1) } }; + { min.x(), min.y() }, + { max.x(), min.y() }, + { max.x(), min.y() + 0.442265 * size.y()}, + { max.x() - 10.0, min.y() + 0.4711325 * size.y()}, + { max.x() + 10.0, min.y() + 0.5288675 * size.y()}, + { max.x(), min.y() + 0.557735 * size.y()}, + { max.x(), max.y() }, + { min.x() + 0.557735 * size.x(), max.y()}, + { min.x() + 0.5288675 * size.x(), max.y() - 10.0}, + { min.x() + 0.4711325 * size.x(), max.y() + 10.0}, + { min.x() + 0.442265 * size.x(), max.y()}, + { min.x(), max.y() } }; } wxGetApp().plater()->set_bed_shape(bed_shape, texture, model, gcode_result.bed_shape.empty()); @@ -647,7 +679,7 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& m_print_statistics = gcode_result.print_statistics; if (m_time_estimate_mode != PrintEstimatedStatistics::ETimeMode::Normal) { - float time = m_print_statistics.modes[static_cast(m_time_estimate_mode)].time; + const float time = m_print_statistics.modes[static_cast(m_time_estimate_mode)].time; if (time == 0.0f || short_time(get_time_dhms(time)) == short_time(get_time_dhms(m_print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].time))) m_time_estimate_mode = PrintEstimatedStatistics::ETimeMode::Normal; @@ -757,13 +789,14 @@ void GCodeViewer::reset() #endif // ENABLE_GCODE_VIEWER_STATISTICS } -void GCodeViewer::render() const +void GCodeViewer::render() { auto init_gl_data = [this]() { // initializes opengl data of TBuffers for (size_t i = 0; i < m_buffers.size(); ++i) { - TBuffer& buffer = const_cast(m_buffers[i]); - switch (buffer_type(i)) + TBuffer& buffer = m_buffers[i]; + EMoveType type = buffer_type(i); + switch (type) { default: { break; } case EMoveType::Tool_change: @@ -773,33 +806,65 @@ void GCodeViewer::render() const case EMoveType::Retract: case EMoveType::Unretract: case EMoveType::Seam: { +#if ENABLE_SEAMS_USING_MODELS + if (wxGetApp().is_gl_version_greater_or_equal_to(3, 1)) { + buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Model; + buffer.shader = "gouraud_light_instanced"; + buffer.model.model.init_from(diamond(16)); + buffer.model.color = option_color(type); + } + else { + buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point; + buffer.vertices.format = VBuffer::EFormat::Position; + buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; + } + break; +#else + buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point; + buffer.vertices.format = VBuffer::EFormat::Position; buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; break; +#endif // ENABLE_SEAMS_USING_MODELS } case EMoveType::Wipe: case EMoveType::Extrude: { +#if ENABLE_SEAMS_USING_MODELS + buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Triangle; + buffer.vertices.format = VBuffer::EFormat::PositionNormal3; +#endif // ENABLE_SEAMS_USING_MODELS buffer.shader = "gouraud_light"; break; } case EMoveType::Travel: { +#if ENABLE_SEAMS_USING_MODELS + buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line; + buffer.vertices.format = VBuffer::EFormat::PositionNormal1; +#endif // ENABLE_SEAMS_USING_MODELS buffer.shader = "toolpaths_lines"; break; } } + +#if ENABLE_SEAMS_USING_MODELS + set_toolpath_move_type_visible(EMoveType::Extrude, true); +#endif // ENABLE_SEAMS_USING_MODELS } // initializes tool marker - const_cast(&m_sequential_view)->marker.init(); + m_sequential_view.marker.init(); // initializes point sizes std::array point_sizes; ::glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, point_sizes.data()); - *const_cast*>(&m_detected_point_sizes) = { static_cast(point_sizes[0]), static_cast(point_sizes[1]) }; - *const_cast(&m_gl_data_initialized) = true; + m_detected_point_sizes = { static_cast(point_sizes[0]), static_cast(point_sizes[1]) }; + m_gl_data_initialized = true; }; #if ENABLE_GCODE_VIEWER_STATISTICS - const_cast(&m_statistics)->reset_opengl(); + m_statistics.reset_opengl(); +#if ENABLE_SEAMS_USING_MODELS + m_statistics.total_instances_gpu_size = 0; +#endif // ENABLE_SEAMS_USING_MODELS #endif // ENABLE_GCODE_VIEWER_STATISTICS // OpenGL data must be initialized after the glContext has been created. @@ -815,10 +880,9 @@ void GCodeViewer::render() const render_shells(); float legend_height = 0.0f; render_legend(legend_height); - SequentialView* sequential_view = const_cast(&m_sequential_view); - if (sequential_view->current.last != sequential_view->endpoints.last) { - sequential_view->marker.set_world_position(sequential_view->current_position); - sequential_view->render(legend_height); + if (m_sequential_view.current.last != m_sequential_view.endpoints.last) { + m_sequential_view.marker.set_world_position(m_sequential_view.current_position); + m_sequential_view.render(legend_height); } #if ENABLE_GCODE_VIEWER_STATISTICS render_statistics(); @@ -844,8 +908,8 @@ void GCodeViewer::update_sequential_view_current(unsigned int first, unsigned in return false; }; - int first_diff = static_cast(first) - static_cast(m_sequential_view.last_current.first); - int last_diff = static_cast(last) - static_cast(m_sequential_view.last_current.last); + const int first_diff = static_cast(first) - static_cast(m_sequential_view.last_current.first); + const int last_diff = static_cast(last) - static_cast(m_sequential_view.last_current.last); unsigned int new_first = first; unsigned int new_last = last; @@ -1036,13 +1100,13 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const // save vertices to file fprintf(fp, "\n# vertices\n"); for (const Vec3f& v : out_vertices) { - fprintf(fp, "v %g %g %g\n", v[0], v[1], v[2]); + fprintf(fp, "v %g %g %g\n", v.x(), v.y(), v.x()); } // save normals to file fprintf(fp, "\n# normals\n"); for (const Vec3f& n : out_normals) { - fprintf(fp, "vn %g %g %g\n", n[0], n[1], n[2]); + fprintf(fp, "vn %g %g %g\n", n.x(), n.y(), n.z()); } size_t i = 0; @@ -1124,9 +1188,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // format data into the buffers to be rendered as points auto add_vertices_as_point = [](const GCodeProcessor::MoveVertex& curr, VertexBuffer& vertices) { - vertices.push_back(curr.position[0]); - vertices.push_back(curr.position[1]); - vertices.push_back(curr.position[2]); + vertices.push_back(curr.position.x()); + vertices.push_back(curr.position.y()); + vertices.push_back(curr.position.z()); }; auto add_indices_as_point = [](const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) { @@ -1137,13 +1201,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // format data into the buffers to be rendered as lines auto add_vertices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, VertexBuffer& vertices) { // x component of the normal to the current segment (the normal is parallel to the XY plane) - float normal_x = (curr.position - prev.position).normalized()[1]; + const float normal_x = (curr.position - prev.position).normalized().y(); auto add_vertex = [&vertices, normal_x](const GCodeProcessor::MoveVertex& vertex) { // add position - vertices.push_back(vertex.position[0]); - vertices.push_back(vertex.position[1]); - vertices.push_back(vertex.position[2]); + vertices.push_back(vertex.position.x()); + vertices.push_back(vertex.position.y()); + vertices.push_back(vertex.position.z()); // add normal x component vertices.push_back(normal_x); }; @@ -1177,13 +1241,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) auto add_vertices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, unsigned int vbuffer_id, VertexBuffer& vertices, size_t move_id) { auto store_vertex = [](VertexBuffer& vertices, const Vec3f& position, const Vec3f& normal) { // append position - vertices.push_back(position[0]); - vertices.push_back(position[1]); - vertices.push_back(position[2]); + vertices.push_back(position.x()); + vertices.push_back(position.y()); + vertices.push_back(position.z()); // append normal - vertices.push_back(normal[0]); - vertices.push_back(normal[1]); - vertices.push_back(normal[2]); + vertices.push_back(normal.x()); + vertices.push_back(normal.y()); + vertices.push_back(normal.z()); }; if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { @@ -1193,19 +1257,19 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) Path& last_path = buffer.paths.back(); - Vec3f dir = (curr.position - prev.position).normalized(); - Vec3f right = Vec3f(dir[1], -dir[0], 0.0f).normalized(); - Vec3f left = -right; - Vec3f up = right.cross(dir); - Vec3f down = -up; - float half_width = 0.5f * last_path.width; - float half_height = 0.5f * last_path.height; - Vec3f prev_pos = prev.position - half_height * up; - Vec3f curr_pos = curr.position - half_height * up; - Vec3f d_up = half_height * up; - Vec3f d_down = -half_height * up; - Vec3f d_right = half_width * right; - Vec3f d_left = -half_width * right; + const Vec3f dir = (curr.position - prev.position).normalized(); + const Vec3f right = Vec3f(dir.y(), -dir.x(), 0.0f).normalized(); + const Vec3f left = -right; + const Vec3f up = right.cross(dir); + const Vec3f down = -up; + const float half_width = 0.5f * last_path.width; + const float half_height = 0.5f * last_path.height; + const Vec3f prev_pos = prev.position - half_height * up; + const Vec3f curr_pos = curr.position - half_height * up; + const Vec3f d_up = half_height * up; + const Vec3f d_down = -half_height * up; + const Vec3f d_right = half_width * right; + const Vec3f d_left = -half_width * right; // vertices 1st endpoint if (last_path.vertices_count() == 1 || vertices.empty()) { @@ -1284,14 +1348,14 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) Path& last_path = buffer.paths.back(); - Vec3f dir = (curr.position - prev.position).normalized(); - Vec3f right = Vec3f(dir[1], -dir[0], 0.0f).normalized(); - Vec3f up = right.cross(dir); - float sq_length = (curr.position - prev.position).squaredNorm(); + const Vec3f dir = (curr.position - prev.position).normalized(); + const Vec3f right = Vec3f(dir.y(), -dir.x(), 0.0f).normalized(); + const Vec3f up = right.cross(dir); + const float sq_length = (curr.position - prev.position).squaredNorm(); const std::array first_seg_v_offsets = convert_vertices_offset(vbuffer_size, { 0, 1, 2, 3, 4, 5, 6, 7 }); const std::array non_first_seg_v_offsets = convert_vertices_offset(vbuffer_size, { -4, 0, -2, 1, 2, 3, 4, 5 }); - bool is_first_segment = (last_path.vertices_count() == 1); + const bool is_first_segment = (last_path.vertices_count() == 1); if (is_first_segment || vbuffer_size == 0) { // 1st segment or restart into a new vertex buffer // =============================================== @@ -1310,20 +1374,20 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // any other segment // ================= float displacement = 0.0f; - float cos_dir = prev_dir.dot(dir); + const float cos_dir = prev_dir.dot(dir); if (cos_dir > -0.9998477f) { // if the angle between adjacent segments is smaller than 179 degrees - Vec3f med_dir = (prev_dir + dir).normalized(); - float half_width = 0.5f * last_path.width; + const Vec3f med_dir = (prev_dir + dir).normalized(); + const float half_width = 0.5f * last_path.width; displacement = half_width * ::tan(::acos(std::clamp(dir.dot(med_dir), -1.0f, 1.0f))); } - float sq_displacement = sqr(displacement); - bool can_displace = displacement > 0.0f && sq_displacement < sq_prev_length && sq_displacement < sq_length; + const float sq_displacement = sqr(displacement); + const bool can_displace = displacement > 0.0f && sq_displacement < sq_prev_length && sq_displacement < sq_length; - bool is_right_turn = prev_up.dot(prev_dir.cross(dir)) <= 0.0f; + const bool is_right_turn = prev_up.dot(prev_dir.cross(dir)) <= 0.0f; // whether the angle between adjacent segments is greater than 45 degrees - bool is_sharp = cos_dir < 0.7071068f; + const bool is_sharp = cos_dir < 0.7071068f; bool right_displaced = false; bool left_displaced = false; @@ -1371,6 +1435,23 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) sq_prev_length = sq_length; }; +#if ENABLE_SEAMS_USING_MODELS + // format data into the buffers to be rendered as model + auto add_model_instance = [](const GCodeProcessor::MoveVertex& curr, InstanceBuffer& instances, InstanceIdBuffer& instances_ids, size_t move_id) { + // append position + instances.push_back(curr.position.x()); + instances.push_back(curr.position.y()); + instances.push_back(curr.position.z()); + // append width + instances.push_back(1.2f * curr.width); + // append height + instances.push_back(1.2f * curr.height); + + // append id + instances_ids.push_back(move_id); + }; +#endif // ENABLE_SEAMS_USING_MODELS + #if ENABLE_GCODE_VIEWER_STATISTICS auto start_time = std::chrono::high_resolution_clock::now(); m_statistics.results_size = SLIC3R_STDVEC_MEMSIZE(gcode_result.moves, GCodeProcessor::MoveVertex); @@ -1413,6 +1494,10 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) std::vector vertices(m_buffers.size()); std::vector indices(m_buffers.size()); +#if ENABLE_SEAMS_USING_MODELS + std::vector instances(m_buffers.size()); + std::vector instances_ids(m_buffers.size()); +#endif // ENABLE_SEAMS_USING_MODELS std::vector options_zs; // toolpaths data -> extract vertices from result @@ -1434,9 +1519,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) progress_count = 0; } - unsigned char id = buffer_id(curr.type); + const unsigned char id = buffer_id(curr.type); TBuffer& t_buffer = m_buffers[id]; MultiVertexBuffer& v_multibuffer = vertices[id]; +#if ENABLE_SEAMS_USING_MODELS + InstanceBuffer& inst_buffer = instances[id]; + InstanceIdBuffer& inst_id_buffer = instances_ids[id]; +#endif // ENABLE_SEAMS_USING_MODELS // ensure there is at least one vertex buffer if (v_multibuffer.empty()) @@ -1460,6 +1549,16 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) case TBuffer::ERenderPrimitiveType::Point: { add_vertices_as_point(curr, v_buffer); break; } case TBuffer::ERenderPrimitiveType::Line: { add_vertices_as_line(prev, curr, v_buffer); break; } case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast(v_multibuffer.size()) - 1, v_buffer, i); break; } +#if ENABLE_SEAMS_USING_MODELS + case TBuffer::ERenderPrimitiveType::Model: + { + add_model_instance(curr, inst_buffer, inst_id_buffer, i); +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.instances_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + break; + } +#endif // ENABLE_SEAMS_USING_MODELS } // collect options zs for later use @@ -1476,24 +1575,24 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) return Vec3f(vertices[offset + 0], vertices[offset + 1], vertices[offset + 2]); }; auto update_position_at = [](VertexBuffer& vertices, size_t offset, const Vec3f& position) { - vertices[offset + 0] = position[0]; - vertices[offset + 1] = position[1]; - vertices[offset + 2] = position[2]; + vertices[offset + 0] = position.x(); + vertices[offset + 1] = position.y(); + vertices[offset + 2] = position.z(); }; auto match_right_vertices = [&](const Path::Sub_Path& prev_sub_path, const Path::Sub_Path& next_sub_path, size_t curr_s_id, size_t vertex_size_floats, const Vec3f& displacement_vec) { if (&prev_sub_path == &next_sub_path) { // previous and next segment are both contained into to the same vertex buffer VertexBuffer& vbuffer = v_multibuffer[prev_sub_path.first.b_id]; // offset into the vertex buffer of the next segment 1st vertex - size_t next_1st_offset = (prev_sub_path.last.s_id - curr_s_id) * 6 * vertex_size_floats; + const size_t next_1st_offset = (prev_sub_path.last.s_id - curr_s_id) * 6 * vertex_size_floats; // offset into the vertex buffer of the right vertex of the previous segment - size_t prev_right_offset = prev_sub_path.last.i_id - next_1st_offset - 3 * vertex_size_floats; + const size_t prev_right_offset = prev_sub_path.last.i_id - next_1st_offset - 3 * vertex_size_floats; // new position of the right vertices - Vec3f shared_vertex = extract_position_at(vbuffer, prev_right_offset) + displacement_vec; + const Vec3f shared_vertex = extract_position_at(vbuffer, prev_right_offset) + displacement_vec; // update previous segment update_position_at(vbuffer, prev_right_offset, shared_vertex); // offset into the vertex buffer of the right vertex of the next segment - size_t next_right_offset = next_sub_path.last.i_id - next_1st_offset; + const size_t next_right_offset = next_sub_path.last.i_id - next_1st_offset; // update next segment update_position_at(vbuffer, next_right_offset, shared_vertex); } @@ -1501,13 +1600,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) VertexBuffer& prev_vbuffer = v_multibuffer[prev_sub_path.first.b_id]; VertexBuffer& next_vbuffer = v_multibuffer[next_sub_path.first.b_id]; // offset into the previous vertex buffer of the right vertex of the previous segment - size_t prev_right_offset = prev_sub_path.last.i_id - 3 * vertex_size_floats; + const size_t prev_right_offset = prev_sub_path.last.i_id - 3 * vertex_size_floats; // new position of the right vertices - Vec3f shared_vertex = extract_position_at(prev_vbuffer, prev_right_offset) + displacement_vec; + const Vec3f shared_vertex = extract_position_at(prev_vbuffer, prev_right_offset) + displacement_vec; // update previous segment update_position_at(prev_vbuffer, prev_right_offset, shared_vertex); // offset into the next vertex buffer of the right vertex of the next segment - size_t next_right_offset = next_sub_path.first.i_id + 1 * vertex_size_floats; + const size_t next_right_offset = next_sub_path.first.i_id + 1 * vertex_size_floats; // update next segment update_position_at(next_vbuffer, next_right_offset, shared_vertex); } @@ -1517,15 +1616,15 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) if (&prev_sub_path == &next_sub_path) { // previous and next segment are both contained into to the same vertex buffer VertexBuffer& vbuffer = v_multibuffer[prev_sub_path.first.b_id]; // offset into the vertex buffer of the next segment 1st vertex - size_t next_1st_offset = (prev_sub_path.last.s_id - curr_s_id) * 6 * vertex_size_floats; + const size_t next_1st_offset = (prev_sub_path.last.s_id - curr_s_id) * 6 * vertex_size_floats; // offset into the vertex buffer of the left vertex of the previous segment - size_t prev_left_offset = prev_sub_path.last.i_id - next_1st_offset - 1 * vertex_size_floats; + const size_t prev_left_offset = prev_sub_path.last.i_id - next_1st_offset - 1 * vertex_size_floats; // new position of the left vertices - Vec3f shared_vertex = extract_position_at(vbuffer, prev_left_offset) + displacement_vec; + const Vec3f shared_vertex = extract_position_at(vbuffer, prev_left_offset) + displacement_vec; // update previous segment update_position_at(vbuffer, prev_left_offset, shared_vertex); // offset into the vertex buffer of the left vertex of the next segment - size_t next_left_offset = next_sub_path.last.i_id - next_1st_offset + 1 * vertex_size_floats; + const size_t next_left_offset = next_sub_path.last.i_id - next_1st_offset + 1 * vertex_size_floats; // update next segment update_position_at(vbuffer, next_left_offset, shared_vertex); } @@ -1533,13 +1632,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) VertexBuffer& prev_vbuffer = v_multibuffer[prev_sub_path.first.b_id]; VertexBuffer& next_vbuffer = v_multibuffer[next_sub_path.first.b_id]; // offset into the previous vertex buffer of the left vertex of the previous segment - size_t prev_left_offset = prev_sub_path.last.i_id - 1 * vertex_size_floats; + const size_t prev_left_offset = prev_sub_path.last.i_id - 1 * vertex_size_floats; // new position of the left vertices - Vec3f shared_vertex = extract_position_at(prev_vbuffer, prev_left_offset) + displacement_vec; + const Vec3f shared_vertex = extract_position_at(prev_vbuffer, prev_left_offset) + displacement_vec; // update previous segment update_position_at(prev_vbuffer, prev_left_offset, shared_vertex); // offset into the next vertex buffer of the left vertex of the next segment - size_t next_left_offset = next_sub_path.first.i_id + 3 * vertex_size_floats; + const size_t next_left_offset = next_sub_path.first.i_id + 3 * vertex_size_floats; // update next segment update_position_at(next_vbuffer, next_left_offset, shared_vertex); } @@ -1551,8 +1650,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // to two different vertex buffers size_t prev_sub_path_id = 0; size_t next_sub_path_id = 0; - size_t path_vertices_count = path.vertices_count(); - float half_width = 0.5f * path.width; + const size_t path_vertices_count = path.vertices_count(); + const float half_width = 0.5f * path.width; for (size_t j = 1; j < path_vertices_count - 1; ++j) { size_t curr_s_id = path.sub_paths.front().first.s_id + j; const Vec3f& prev = gcode_result.moves[curr_s_id - 1].position; @@ -1567,16 +1666,16 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) const Path::Sub_Path& prev_sub_path = path.sub_paths[prev_sub_path_id]; const Path::Sub_Path& next_sub_path = path.sub_paths[next_sub_path_id]; - Vec3f prev_dir = (curr - prev).normalized(); - Vec3f prev_right = Vec3f(prev_dir[1], -prev_dir[0], 0.0f).normalized(); - Vec3f prev_up = prev_right.cross(prev_dir); + const Vec3f prev_dir = (curr - prev).normalized(); + const Vec3f prev_right = Vec3f(prev_dir.y(), -prev_dir.x(), 0.0f).normalized(); + const Vec3f prev_up = prev_right.cross(prev_dir); - Vec3f next_dir = (next - curr).normalized(); + const Vec3f next_dir = (next - curr).normalized(); - bool is_right_turn = prev_up.dot(prev_dir.cross(next_dir)) <= 0.0f; - float cos_dir = prev_dir.dot(next_dir); + const bool is_right_turn = prev_up.dot(prev_dir.cross(next_dir)) <= 0.0f; + const float cos_dir = prev_dir.dot(next_dir); // whether the angle between adjacent segments is greater than 45 degrees - bool is_sharp = cos_dir < 0.7071068f; + const bool is_sharp = cos_dir < 0.7071068f; float displacement = 0.0f; if (cos_dir > -0.9998477f) { @@ -1585,10 +1684,10 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) displacement = half_width * ::tan(::acos(std::clamp(next_dir.dot(med_dir), -1.0f, 1.0f))); } - float sq_prev_length = (curr - prev).squaredNorm(); - float sq_next_length = (next - curr).squaredNorm(); - float sq_displacement = sqr(displacement); - bool can_displace = displacement > 0.0f && sq_displacement < sq_prev_length && sq_displacement < sq_next_length; + const float sq_prev_length = (curr - prev).squaredNorm(); + const float sq_next_length = (next - curr).squaredNorm(); + const float sq_displacement = sqr(displacement); + const bool can_displace = displacement > 0.0f && sq_displacement < sq_prev_length && sq_displacement < sq_next_length; if (can_displace) { // displacement to apply to the vertices to match @@ -1638,31 +1737,44 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) } } - // send vertices data to gpu + // send vertices data to gpu, where needed for (size_t i = 0; i < m_buffers.size(); ++i) { TBuffer& t_buffer = m_buffers[i]; - - const MultiVertexBuffer& v_multibuffer = vertices[i]; - for (const VertexBuffer& v_buffer : v_multibuffer) { - size_t size_elements = v_buffer.size(); - size_t size_bytes = size_elements * sizeof(float); - size_t vertices_count = size_elements / t_buffer.vertices.vertex_size_floats(); - t_buffer.vertices.count += vertices_count; +#if ENABLE_SEAMS_USING_MODELS + if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Model) { + const InstanceBuffer& inst_buffer = instances[i]; + if (!inst_buffer.empty()) { + t_buffer.model.instances.buffer = inst_buffer; + t_buffer.model.instances.s_ids = instances_ids[i]; + } + } + else { +#endif // ENABLE_SEAMS_USING_MODELS + const MultiVertexBuffer& v_multibuffer = vertices[i]; + for (const VertexBuffer& v_buffer : v_multibuffer) { + const size_t size_elements = v_buffer.size(); + const size_t size_bytes = size_elements * sizeof(float); + const size_t vertices_count = size_elements / t_buffer.vertices.vertex_size_floats(); + t_buffer.vertices.count += vertices_count; #if ENABLE_GCODE_VIEWER_STATISTICS - m_statistics.total_vertices_gpu_size += static_cast(size_bytes); - m_statistics.max_vbuffer_gpu_size = std::max(m_statistics.max_vbuffer_gpu_size, static_cast(size_bytes)); - ++m_statistics.vbuffers_count; + m_statistics.total_vertices_gpu_size += static_cast(size_bytes); + m_statistics.max_vbuffer_gpu_size = std::max(m_statistics.max_vbuffer_gpu_size, static_cast(size_bytes)); + ++m_statistics.vbuffers_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS - GLuint id = 0; - glsafe(::glGenBuffers(1, &id)); - t_buffer.vertices.vbos.push_back(static_cast(id)); - t_buffer.vertices.sizes.push_back(size_bytes); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, size_bytes, v_buffer.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + GLuint id = 0; + glsafe(::glGenBuffers(1, &id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, size_bytes, v_buffer.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + + t_buffer.vertices.vbos.push_back(static_cast(id)); + t_buffer.vertices.sizes.push_back(size_bytes); + } +#if ENABLE_SEAMS_USING_MODELS } +#endif // ENABLE_SEAMS_USING_MODELS } #if ENABLE_GCODE_VIEWER_STATISTICS @@ -1673,6 +1785,10 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // dismiss vertices data, no more needed std::vector().swap(vertices); +#if ENABLE_SEAMS_USING_MODELS + std::vector().swap(instances); + std::vector().swap(instances_ids); +#endif // ENABLE_SEAMS_USING_MODELS // toolpaths data -> extract indices from result // paths may have been filled while extracting vertices, @@ -1709,7 +1825,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) progress_count = 0; } - unsigned char id = buffer_id(curr.type); + const unsigned char id = buffer_id(curr.type); TBuffer& t_buffer = m_buffers[id]; MultiIndexBuffer& i_multibuffer = indices[id]; CurrVertexBuffer& curr_vertex_buffer = curr_vertex_buffers[id]; @@ -1718,7 +1834,10 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // ensure there is at least one index buffer if (i_multibuffer.empty()) { i_multibuffer.push_back(IndexBuffer()); - vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]); +#if ENABLE_SEAMS_USING_MODELS + if (!t_buffer.vertices.vbos.empty()) +#endif // ENABLE_SEAMS_USING_MODELS + vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]); } // if adding the indices for the current segment exceeds the threshold size of the current index buffer @@ -1765,6 +1884,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast(i_multibuffer.size()) - 1, i_buffer, i); break; } + default: { break; } } } @@ -1777,28 +1897,34 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // toolpaths data -> send indices data to gpu for (size_t i = 0; i < m_buffers.size(); ++i) { TBuffer& t_buffer = m_buffers[i]; - const MultiIndexBuffer& i_multibuffer = indices[i]; - for (const IndexBuffer& i_buffer : i_multibuffer) { - size_t size_elements = i_buffer.size(); - size_t size_bytes = size_elements * sizeof(IBufferType); +#if ENABLE_SEAMS_USING_MODELS + if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Model) { +#endif // ENABLE_SEAMS_USING_MODELS + const MultiIndexBuffer& i_multibuffer = indices[i]; + for (const IndexBuffer& i_buffer : i_multibuffer) { + const size_t size_elements = i_buffer.size(); + const size_t size_bytes = size_elements * sizeof(IBufferType); - // stores index buffer informations into TBuffer - t_buffer.indices.push_back(IBuffer()); - IBuffer& ibuf = t_buffer.indices.back(); - ibuf.count = size_elements; - ibuf.vbo = vbo_indices[i][t_buffer.indices.size() - 1]; + // stores index buffer informations into TBuffer + t_buffer.indices.push_back(IBuffer()); + IBuffer& ibuf = t_buffer.indices.back(); + ibuf.count = size_elements; + ibuf.vbo = vbo_indices[i][t_buffer.indices.size() - 1]; #if ENABLE_GCODE_VIEWER_STATISTICS - m_statistics.total_indices_gpu_size += static_cast(size_bytes); - m_statistics.max_ibuffer_gpu_size = std::max(m_statistics.max_ibuffer_gpu_size, static_cast(size_bytes)); - ++m_statistics.ibuffers_count; + m_statistics.total_indices_gpu_size += static_cast(size_bytes); + m_statistics.max_ibuffer_gpu_size = std::max(m_statistics.max_ibuffer_gpu_size, static_cast(size_bytes)); + ++m_statistics.ibuffers_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS - glsafe(::glGenBuffers(1, &ibuf.ibo)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf.ibo)); - glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, size_bytes, i_buffer.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + glsafe(::glGenBuffers(1, &ibuf.ibo)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf.ibo)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, size_bytes, i_buffer.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } +#if ENABLE_SEAMS_USING_MODELS } +#endif // ENABLE_SEAMS_USING_MODELS } if (progress_dialog != nullptr) { @@ -1844,7 +1970,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) if (move.type == EMoveType::Extrude) { // layers zs const double* const last_z = m_layers.empty() ? nullptr : &m_layers.get_zs().back(); - double z = static_cast(move.position[2]); + const double z = static_cast(move.position.z()); if (last_z == nullptr || z < *last_z - EPSILON || *last_z + EPSILON < z) m_layers.append(z, { last_travel_s_id, i }); else @@ -1881,7 +2007,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) if (!options_zs.empty()) { TBuffer& extrude_buffer = m_buffers[buffer_id(EMoveType::Extrude)]; for (Path& path : extrude_buffer.paths) { - float z = path.sub_paths.front().first.position[2]; + const float z = path.sub_paths.front().first.position.z(); if (std::find_if(options_zs.begin(), options_zs.end(), [z](float f) { return f - EPSILON <= z && z <= f + EPSILON; }) != options_zs.end()) path.cp_color_id = 255 - path.cp_color_id; } @@ -1918,12 +2044,12 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF) { // adds wipe tower's volume - double max_z = print.objects()[0]->model_object()->get_model()->bounding_box().max(2); + const double max_z = print.objects()[0]->model_object()->get_model()->bounding_box().max(2); const PrintConfig& config = print.config(); - size_t extruders_count = config.nozzle_diameter.size(); - if ((extruders_count > 1) && config.wipe_tower && !config.complete_objects) { - float depth = print.wipe_tower_data(extruders_count).depth; - float brim_width = print.wipe_tower_data(extruders_count).brim_width; + const size_t extruders_count = config.nozzle_diameter.size(); + if (extruders_count > 1 && config.wipe_tower && !config.complete_objects) { + const float depth = print.wipe_tower_data(extruders_count).depth; + const float brim_width = print.wipe_tower_data(extruders_count).brim_width; m_shells.volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, !print.is_step_done(psWipeTower), brim_width, initialized); @@ -1968,18 +2094,14 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } case EViewType::Tool: { color = m_tool_colors[path.extruder_id]; break; } case EViewType::ColorPrint: { - if (path.cp_color_id >= static_cast(m_tool_colors.size())) { - color = { 0.5f, 0.5f, 0.5f }; -// // complementary color -// color = m_tool_colors[255 - path.cp_color_id]; -// color = { 1.0f - color[0], 1.0f - color[1], 1.0f - color[2] }; - } + if (path.cp_color_id >= static_cast(m_tool_colors.size())) + color = { 0.5f, 0.5f, 0.5f, 1.0f }; else color = m_tool_colors[path.cp_color_id]; break; } - default: { color = { 1.0f, 1.0f, 1.0f }; break; } + default: { color = { 1.0f, 1.0f, 1.0f, 1.0f }; break; } } return color; @@ -2018,8 +2140,8 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool path.sub_paths.back().last = buffer.paths[last].sub_paths.back().last; } - size_t min_s_id = m_layers.get_endpoints_at(min_id).first; - size_t max_s_id = m_layers.get_endpoints_at(max_id).last; + const size_t min_s_id = m_layers.get_endpoints_at(min_id).first; + const size_t max_s_id = m_layers.get_endpoints_at(max_id).last; return (min_s_id <= path.sub_paths.front().first.s_id && path.sub_paths.front().first.s_id <= max_s_id) || (min_s_id <= path.sub_paths.back().last.s_id && path.sub_paths.back().last.s_id <= max_s_id); @@ -2028,9 +2150,12 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool #if ENABLE_GCODE_VIEWER_STATISTICS Statistics* statistics = const_cast(&m_statistics); statistics->render_paths_size = 0; +#if ENABLE_SEAMS_USING_MODELS + statistics->models_instances_size = 0; +#endif // ENABLE_SEAMS_USING_MODELS #endif // ENABLE_GCODE_VIEWER_STATISTICS - bool top_layer_only = get_app_config()->get("seq_top_layer_only") == "1"; + const bool top_layer_only = get_app_config()->get("seq_top_layer_only") == "1"; SequentialView::Endpoints global_endpoints = { m_moves_count , 0 }; SequentialView::Endpoints top_layer_endpoints = global_endpoints; @@ -2048,84 +2173,126 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool if (!buffer.visible) continue; - for (size_t i = 0; i < buffer.paths.size(); ++i) { - const Path& path = buffer.paths[i]; - if (path.type == EMoveType::Travel) { - if (!is_travel_in_layers_range(i, m_layers_z_range[0], m_layers_z_range[1])) +#if ENABLE_SEAMS_USING_MODELS + if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Model) { + for (size_t id : buffer.model.instances.s_ids) { + if (id < m_layers.get_endpoints_at(m_layers_z_range[0]).first || m_layers.get_endpoints_at(m_layers_z_range[1]).last < id) continue; + + global_endpoints.first = std::min(global_endpoints.first, id); + global_endpoints.last = std::max(global_endpoints.last, id); + + if (top_layer_only) { + if (id < m_layers.get_endpoints_at(m_layers_z_range[1]).first || m_layers.get_endpoints_at(m_layers_z_range[1]).last < id) + continue; + + top_layer_endpoints.first = std::min(top_layer_endpoints.first, id); + top_layer_endpoints.last = std::max(top_layer_endpoints.last, id); + } } - else if (!is_in_layers_range(path, m_layers_z_range[0], m_layers_z_range[1])) - continue; - - if (path.type == EMoveType::Extrude && !is_visible(path)) - continue; - - // store valid path - for (size_t j = 0; j < path.sub_paths.size(); ++j) { - paths.push_back({ static_cast(b), path.sub_paths[j].first.b_id, static_cast(i), static_cast(j) }); - } - - global_endpoints.first = std::min(global_endpoints.first, path.sub_paths.front().first.s_id); - global_endpoints.last = std::max(global_endpoints.last, path.sub_paths.back().last.s_id); - - if (top_layer_only) { + } + else { +#endif // ENABLE_SEAMS_USING_MODELS + for (size_t i = 0; i < buffer.paths.size(); ++i) { + const Path& path = buffer.paths[i]; if (path.type == EMoveType::Travel) { - if (is_travel_in_layers_range(i, m_layers_z_range[1], m_layers_z_range[1])) { + if (!is_travel_in_layers_range(i, m_layers_z_range[0], m_layers_z_range[1])) + continue; + } + else if (!is_in_layers_range(path, m_layers_z_range[0], m_layers_z_range[1])) + continue; + + if (path.type == EMoveType::Extrude && !is_visible(path)) + continue; + + // store valid path + for (size_t j = 0; j < path.sub_paths.size(); ++j) { + paths.push_back({ static_cast(b), path.sub_paths[j].first.b_id, static_cast(i), static_cast(j) }); + } + + global_endpoints.first = std::min(global_endpoints.first, path.sub_paths.front().first.s_id); + global_endpoints.last = std::max(global_endpoints.last, path.sub_paths.back().last.s_id); + + if (top_layer_only) { + if (path.type == EMoveType::Travel) { + if (is_travel_in_layers_range(i, m_layers_z_range[1], m_layers_z_range[1])) { + top_layer_endpoints.first = std::min(top_layer_endpoints.first, path.sub_paths.front().first.s_id); + top_layer_endpoints.last = std::max(top_layer_endpoints.last, path.sub_paths.back().last.s_id); + } + } + else if (is_in_layers_range(path, m_layers_z_range[1], m_layers_z_range[1])) { top_layer_endpoints.first = std::min(top_layer_endpoints.first, path.sub_paths.front().first.s_id); top_layer_endpoints.last = std::max(top_layer_endpoints.last, path.sub_paths.back().last.s_id); } } - else if (is_in_layers_range(path, m_layers_z_range[1], m_layers_z_range[1])) { - top_layer_endpoints.first = std::min(top_layer_endpoints.first, path.sub_paths.front().first.s_id); - top_layer_endpoints.last = std::max(top_layer_endpoints.last, path.sub_paths.back().last.s_id); - } } +#if ENABLE_SEAMS_USING_MODELS } +#endif // ENABLE_SEAMS_USING_MODELS } // update current sequential position sequential_view->current.first = !top_layer_only && keep_sequential_current_first ? std::clamp(sequential_view->current.first, global_endpoints.first, global_endpoints.last) : global_endpoints.first; sequential_view->current.last = keep_sequential_current_last ? std::clamp(sequential_view->current.last, global_endpoints.first, global_endpoints.last) : global_endpoints.last; - // get the world position from gpu + // get the world position from the vertex buffer bool found = false; for (const TBuffer& buffer : m_buffers) { - // searches the path containing the current position - for (const Path& path : buffer.paths) { - if (path.contains(m_sequential_view.current.last)) { - int sub_path_id = path.get_id_of_sub_path_containing(m_sequential_view.current.last); - if (sub_path_id != -1) { - const Path::Sub_Path& sub_path = path.sub_paths[sub_path_id]; - unsigned int offset = static_cast(m_sequential_view.current.last - sub_path.first.s_id); - if (offset > 0) { - if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Line) - offset = 2 * offset - 1; - else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) { - unsigned int indices_count = buffer.indices_per_segment(); - offset = indices_count * (offset - 1) + (indices_count - 2); - if (sub_path_id == 0) - offset += 6; // add 2 triangles for starting cap - } - } - offset += static_cast(sub_path.first.i_id); - - // gets the vertex index from the index buffer on gpu - const IBuffer& i_buffer = buffer.indices[sub_path.first.b_id]; - unsigned int index = 0; - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo)); - glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast(offset * sizeof(IBufferType)), static_cast(sizeof(IBufferType)), static_cast(&index))); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - - // gets the position from the vertices buffer on gpu - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo)); - glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast(index * buffer.vertices.vertex_size_bytes()), static_cast(3 * sizeof(float)), static_cast(sequential_view->current_position.data()))); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); +#if ENABLE_SEAMS_USING_MODELS + if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Model) { + for (size_t i = 0; i < buffer.model.instances.s_ids.size(); ++i) { + if (buffer.model.instances.s_ids[i] == m_sequential_view.current.last) { + size_t offset = i * buffer.model.instances.instance_size_floats(); + sequential_view->current_position.x() = buffer.model.instances.buffer[offset + 0]; + sequential_view->current_position.y() = buffer.model.instances.buffer[offset + 1]; + sequential_view->current_position.z() = buffer.model.instances.buffer[offset + 2]; found = true; break; } } } + else { +#endif // ENABLE_SEAMS_USING_MODELS + // searches the path containing the current position + for (const Path& path : buffer.paths) { + if (path.contains(m_sequential_view.current.last)) { + const int sub_path_id = path.get_id_of_sub_path_containing(m_sequential_view.current.last); + if (sub_path_id != -1) { + const Path::Sub_Path& sub_path = path.sub_paths[sub_path_id]; + unsigned int offset = static_cast(m_sequential_view.current.last - sub_path.first.s_id); + if (offset > 0) { + if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Line) + offset = 2 * offset - 1; + else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) { + unsigned int indices_count = buffer.indices_per_segment(); + offset = indices_count * (offset - 1) + (indices_count - 2); + if (sub_path_id == 0) + offset += 6; // add 2 triangles for starting cap + } + } + offset += static_cast(sub_path.first.i_id); + + // gets the vertex index from the index buffer on gpu + const IBuffer& i_buffer = buffer.indices[sub_path.first.b_id]; + unsigned int index = 0; + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo)); + glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast(offset * sizeof(IBufferType)), static_cast(sizeof(IBufferType)), static_cast(&index))); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + // gets the position from the vertices buffer on gpu + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo)); + glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast(index * buffer.vertices.vertex_size_bytes()), static_cast(3 * sizeof(float)), static_cast(sequential_view->current_position.data()))); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + + found = true; + break; + } + } + } +#if ENABLE_SEAMS_USING_MODELS + } +#endif // ENABLE_SEAMS_USING_MODELS if (found) break; @@ -2143,20 +2310,20 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool Color color; switch (path.type) { - case EMoveType::Tool_change: { color = Options_Colors[static_cast(EOptionsColors::ToolChanges)]; break; } - case EMoveType::Color_change: { color = Options_Colors[static_cast(EOptionsColors::ColorChanges)]; break; } - case EMoveType::Pause_Print: { color = Options_Colors[static_cast(EOptionsColors::PausePrints)]; break; } - case EMoveType::Custom_GCode: { color = Options_Colors[static_cast(EOptionsColors::CustomGCodes)]; break; } - case EMoveType::Retract: { color = Options_Colors[static_cast(EOptionsColors::Retractions)]; break; } - case EMoveType::Unretract: { color = Options_Colors[static_cast(EOptionsColors::Unretractions)]; break; } - case EMoveType::Seam: { color = Options_Colors[static_cast(EOptionsColors::Seams)]; break; } + case EMoveType::Tool_change: + case EMoveType::Color_change: + case EMoveType::Pause_Print: + case EMoveType::Custom_GCode: + case EMoveType::Retract: + case EMoveType::Unretract: + case EMoveType::Seam: { color = option_color(path.type); break; } case EMoveType::Extrude: { if (!top_layer_only || m_sequential_view.current.last == global_endpoints.last || is_in_layers_range(path, m_layers_z_range[1], m_layers_z_range[1])) color = extrusion_color(path); else - color = { 0.25f, 0.25f, 0.25f }; + color = Neutral_Color; break; } @@ -2164,12 +2331,12 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool if (!top_layer_only || m_sequential_view.current.last == global_endpoints.last || is_travel_in_layers_range(path_id, m_layers_z_range[1], m_layers_z_range[1])) color = (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path); else - color = { 0.25f, 0.25f, 0.25f }; + color = Neutral_Color; break; } case EMoveType::Wipe: { color = Wipe_Color; break; } - default: { color = { 0.0f, 0.0f, 0.0f }; break; } + default: { color = { 0.0f, 0.0f, 0.0f, 1.0f }; break; } } RenderPath key{ tbuffer_id, color, static_cast(ibuffer_id), path_id }; @@ -2193,6 +2360,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool size_in_indices = buffer.indices_per_segment() * segments_count; break; } + default: { break; } } if (size_in_indices == 0) @@ -2231,9 +2399,57 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool #endif } +#if ENABLE_SEAMS_USING_MODELS + // second pass: for buffers using instanced models, update the instances render ranges + for (size_t b = 0; b < m_buffers.size(); ++b) { + TBuffer& buffer = const_cast(m_buffers[b]); + if (buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Model) + continue; + + buffer.model.instances.render_ranges.reset(); + + if (!buffer.visible) + continue; + + buffer.model.instances.render_ranges.ranges.push_back({ 0, 0, 0, buffer.model.color }); + bool has_second_range = top_layer_only && m_sequential_view.current.last != m_sequential_view.global.last; + if (has_second_range) + buffer.model.instances.render_ranges.ranges.push_back({ 0, 0, 0, Neutral_Color }); + + if (m_sequential_view.current.first <= buffer.model.instances.s_ids.back() && buffer.model.instances.s_ids.front() <= m_sequential_view.current.last) { + for (size_t id : buffer.model.instances.s_ids) { + if (has_second_range) { + if (id <= m_sequential_view.endpoints.first) { + ++buffer.model.instances.render_ranges.ranges.front().offset; + if (id <= m_sequential_view.current.first) + ++buffer.model.instances.render_ranges.ranges.back().offset; + else + ++buffer.model.instances.render_ranges.ranges.back().count; + } + else if (id <= m_sequential_view.current.last) + ++buffer.model.instances.render_ranges.ranges.front().count; + else + break; + } + else { + if (id <= m_sequential_view.current.first) + ++buffer.model.instances.render_ranges.ranges.front().offset; + else if (id <= m_sequential_view.current.last) + ++buffer.model.instances.render_ranges.ranges.front().count; + else + break; + } + } + } + } +#endif // ENABLE_SEAMS_USING_MODELS + // set sequential data to their final value sequential_view->endpoints = top_layer_only ? top_layer_endpoints : global_endpoints; sequential_view->current.first = !top_layer_only && keep_sequential_current_first ? std::clamp(sequential_view->current.first, sequential_view->endpoints.first, sequential_view->endpoints.last) : sequential_view->endpoints.first; +#if ENABLE_SEAMS_USING_MODELS + sequential_view->global = global_endpoints; +#endif // ENABLE_SEAMS_USING_MODELS // updates sequential range caps std::array* sequential_range_caps = const_cast*>(&m_sequential_range_caps); @@ -2355,12 +2571,17 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool statistics->render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.sizes, unsigned int); statistics->render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.offsets, size_t); } +#if ENABLE_SEAMS_USING_MODELS + statistics->models_instances_size += SLIC3R_STDVEC_MEMSIZE(buffer.model.instances.buffer, float); + statistics->models_instances_size += SLIC3R_STDVEC_MEMSIZE(buffer.model.instances.s_ids, size_t); + statistics->models_instances_size += SLIC3R_STDVEC_MEMSIZE(buffer.model.instances.render_ranges.ranges, InstanceVBuffer::Ranges::Range); +#endif // ENABLE_SEAMS_USING_MODELS } statistics->refresh_paths_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); #endif // ENABLE_GCODE_VIEWER_STATISTICS } -void GCodeViewer::render_toolpaths() const +void GCodeViewer::render_toolpaths() { #if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS float point_size = 20.0f; @@ -2374,15 +2595,10 @@ void GCodeViewer::render_toolpaths() const float near_plane_height = camera.get_type() == Camera::EType::Perspective ? static_cast(viewport[3]) / (2.0f * static_cast(2.0 * std::tan(0.5 * Geometry::deg2rad(camera.get_fov())))) : static_cast(viewport[3]) * 0.0005; - auto set_uniform_color = [](const std::array& color, GLShaderProgram& shader) { - std::array color4 = { color[0], color[1], color[2], 1.0f }; - shader.set_uniform("uniform_color", color4); - }; - #if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_points = [this, zoom, point_size, near_plane_height, set_uniform_color] + auto render_as_points = [this, zoom, point_size, near_plane_height] #else - auto render_as_points = [zoom, point_size, near_plane_height, set_uniform_color] + auto render_as_points = [zoom, point_size, near_plane_height] #endif // ENABLE_GCODE_VIEWER_STATISTICS (const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) { #if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS @@ -2401,10 +2617,10 @@ void GCodeViewer::render_toolpaths() const for (const RenderPath& path : buffer.render_paths) { if (path.ibuffer_id == ibuffer_id) { - set_uniform_color(path.color, shader); + shader.set_uniform("uniform_color", path.color); glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS - ++const_cast(&m_statistics)->gl_multi_points_calls_count; + ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } } @@ -2414,51 +2630,76 @@ void GCodeViewer::render_toolpaths() const }; #if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_lines = [this, light_intensity, set_uniform_color] + auto render_as_lines = [this, light_intensity] #else - auto render_as_lines = [light_intensity, set_uniform_color] + auto render_as_lines = [light_intensity] #endif // ENABLE_GCODE_VIEWER_STATISTICS (const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) { shader.set_uniform("light_intensity", light_intensity); for (const RenderPath& path : buffer.render_paths) { if (path.ibuffer_id == ibuffer_id) { - set_uniform_color(path.color, shader); + shader.set_uniform("uniform_color", path.color); glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS - ++const_cast(&m_statistics)->gl_multi_lines_calls_count; + ++m_statistics.gl_multi_lines_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } } }; #if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_triangles = [this, set_uniform_color] + auto render_as_triangles = [this] #else - auto render_as_triangles = [set_uniform_color] + auto render_as_triangles = [] #endif // ENABLE_GCODE_VIEWER_STATISTICS -(const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) { + (const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) { for (const RenderPath& path : buffer.render_paths) { if (path.ibuffer_id == ibuffer_id) { - set_uniform_color(path.color, shader); + shader.set_uniform("uniform_color", path.color); glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS - ++const_cast(&m_statistics)->gl_multi_triangles_calls_count; + ++m_statistics.gl_multi_triangles_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } } }; +#if ENABLE_SEAMS_USING_MODELS + auto render_as_instanced_model = [this] + (TBuffer& buffer, GLShaderProgram & shader) { + for (auto& range : buffer.model.instances.render_ranges.ranges) { + if (range.vbo == 0 && range.count > 0) { + glsafe(::glGenBuffers(1, &range.vbo)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, range.vbo)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, range.count * buffer.model.instances.instance_size_bytes(), (const void*)&buffer.model.instances.buffer[range.offset * buffer.model.instances.instance_size_floats()], GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + } + + if (range.vbo > 0) { + buffer.model.model.set_color(-1, range.color); + buffer.model.model.render_instanced(range.vbo, range.count); +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_instanced_models_calls_count; + m_statistics.total_instances_gpu_size += static_cast(range.count * buffer.model.instances.instance_size_bytes()); +#endif // ENABLE_GCODE_VIEWER_STATISTICS + } + } + }; +#endif // ENABLE_SEAMS_USING_MODELS + auto line_width = [](double zoom) { return (zoom < 5.0) ? 1.0 : (1.0 + 5.0 * (zoom - 5.0) / (100.0 - 5.0)); }; - glsafe(::glLineWidth(static_cast(line_width(zoom)))); - unsigned char begin_id = buffer_id(EMoveType::Retract); unsigned char end_id = buffer_id(EMoveType::Count); for (unsigned char i = begin_id; i < end_id; ++i) { +#if ENABLE_SEAMS_USING_MODELS + TBuffer& buffer = m_buffers[i]; +#else const TBuffer& buffer = m_buffers[i]; +#endif // ENABLE_SEAMS_USING_MODELS if (!buffer.visible || !buffer.has_data()) continue; @@ -2466,53 +2707,66 @@ void GCodeViewer::render_toolpaths() const if (shader != nullptr) { shader->start_using(); - for (size_t j = 0; j < buffer.indices.size(); ++j) { - const IBuffer& i_buffer = buffer.indices[j]; - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo)); - glsafe(::glVertexPointer(buffer.vertices.position_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes())); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - bool has_normals = buffer.vertices.normal_size_floats() > 0; - if (has_normals) { - glsafe(::glNormalPointer(GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_bytes())); - glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); - } - - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo)); - - switch (buffer.render_primitive_type) - { - case TBuffer::ERenderPrimitiveType::Point: { - render_as_points(buffer, static_cast(j), *shader); - break; - } - case TBuffer::ERenderPrimitiveType::Line: { - render_as_lines(buffer, static_cast(j), *shader); - break; - } - case TBuffer::ERenderPrimitiveType::Triangle: { - render_as_triangles(buffer, static_cast(j), *shader); - break; - } - } - - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - - if (has_normals) - glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); - - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); +#if ENABLE_SEAMS_USING_MODELS + if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Model) { + shader->set_uniform("emission_factor", 0.25f); + render_as_instanced_model(buffer, *shader); + shader->set_uniform("emission_factor", 0.0f); } + else { +#endif // ENABLE_SEAMS_USING_MODELS + for (size_t j = 0; j < buffer.indices.size(); ++j) { + const IBuffer& i_buffer = buffer.indices[j]; + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo)); + glsafe(::glVertexPointer(buffer.vertices.position_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes())); + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + bool has_normals = buffer.vertices.normal_size_floats() > 0; + if (has_normals) { + glsafe(::glNormalPointer(GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_bytes())); + glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + } + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo)); + + switch (buffer.render_primitive_type) + { + case TBuffer::ERenderPrimitiveType::Point: { + render_as_points(buffer, static_cast(j), *shader); + break; + } + case TBuffer::ERenderPrimitiveType::Line: { + glsafe(::glLineWidth(static_cast(line_width(zoom)))); + render_as_lines(buffer, static_cast(j), *shader); + break; + } + case TBuffer::ERenderPrimitiveType::Triangle: { + render_as_triangles(buffer, static_cast(j), *shader); + break; + } + default: { break; } + } + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + if (has_normals) + glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); + + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + } +#if ENABLE_SEAMS_USING_MODELS + } +#endif // ENABLE_SEAMS_USING_MODELS shader->stop_using(); } } #if ENABLE_GCODE_VIEWER_STATISTICS - auto render_sequential_range_cap = [this, set_uniform_color] + auto render_sequential_range_cap = [this] #else - auto render_sequential_range_cap = [set_uniform_color] + auto render_sequential_range_cap = [] #endif // ENABLE_GCODE_VIEWER_STATISTICS (const SequentialRangeCap& cap) { GLShaderProgram* shader = wxGetApp().get_shader(cap.buffer->shader.c_str()); @@ -2528,14 +2782,14 @@ void GCodeViewer::render_toolpaths() const glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); } - set_uniform_color(cap.color, *shader); + shader->set_uniform("uniform_color", cap.color); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cap.ibo)); glsafe(::glDrawElements(GL_TRIANGLES, (GLsizei)cap.indices_count(), GL_UNSIGNED_SHORT, nullptr)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); #if ENABLE_GCODE_VIEWER_STATISTICS - ++const_cast(&m_statistics)->gl_triangles_calls_count; + ++m_statistics.gl_triangles_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS if (has_normals) @@ -2554,7 +2808,7 @@ void GCodeViewer::render_toolpaths() const } } -void GCodeViewer::render_shells() const +void GCodeViewer::render_shells() { if (!m_shells.visible || m_shells.volumes.empty()) return; @@ -2572,7 +2826,7 @@ void GCodeViewer::render_shells() const // glsafe(::glDepthMask(GL_TRUE)); } -void GCodeViewer::render_legend(float& legend_height) const +void GCodeViewer::render_legend(float& legend_height) { if (!m_legend_enabled) return; @@ -2935,8 +3189,7 @@ void GCodeViewer::render_legend(float& legend_height) const const bool visible = is_visible(role); append_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], labels[i], visible, times[i], percents[i], max_percent, offsets, used_filaments_m[i], used_filaments_g[i], [this, role, visible]() { - Extrusions* extrusions = const_cast(&m_extrusions); - extrusions->role_visibility_flags = visible ? extrusions->role_visibility_flags & ~(1 << role) : extrusions->role_visibility_flags | (1 << role); + m_extrusions.role_visibility_flags = visible ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); // update buffers' render paths refresh_render_paths(false, false); wxGetApp().plater()->update_preview_moves_slider(); @@ -3408,7 +3661,7 @@ void GCodeViewer::render_legend(float& legend_height) const auto show_mode_button = [this, &imgui, can_show_mode_button](const wxString& label, PrintEstimatedStatistics::ETimeMode mode) { if (can_show_mode_button(mode)) { if (imgui.button(label)) { - *const_cast(&m_time_estimate_mode) = mode; + m_time_estimate_mode = mode; wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); } @@ -3435,7 +3688,7 @@ void GCodeViewer::render_legend(float& legend_height) const } #if ENABLE_GCODE_VIEWER_STATISTICS -void GCodeViewer::render_statistics() const +void GCodeViewer::render_statistics() { static const float offset = 275.0f; @@ -3498,6 +3751,9 @@ void GCodeViewer::render_statistics() const add_counter(std::string("Multi GL_LINES:"), m_statistics.gl_multi_lines_calls_count); add_counter(std::string("Multi GL_TRIANGLES:"), m_statistics.gl_multi_triangles_calls_count); add_counter(std::string("GL_TRIANGLES:"), m_statistics.gl_triangles_calls_count); +#if ENABLE_SEAMS_USING_MODELS + add_counter(std::string("Instanced models:"), m_statistics.gl_instanced_models_calls_count); +#endif // ENABLE_SEAMS_USING_MODELS } if (ImGui::CollapsingHeader("CPU memory")) { @@ -3506,11 +3762,17 @@ void GCodeViewer::render_statistics() const ImGui::Separator(); add_memory(std::string("Paths:"), m_statistics.paths_size); add_memory(std::string("Render paths:"), m_statistics.render_paths_size); +#if ENABLE_SEAMS_USING_MODELS + add_memory(std::string("Models instances:"), m_statistics.models_instances_size); +#endif // ENABLE_SEAMS_USING_MODELS } if (ImGui::CollapsingHeader("GPU memory")) { add_memory(std::string("Vertices:"), m_statistics.total_vertices_gpu_size); add_memory(std::string("Indices:"), m_statistics.total_indices_gpu_size); +#if ENABLE_SEAMS_USING_MODELS + add_memory(std::string("Instances:"), m_statistics.total_instances_gpu_size); +#endif // ENABLE_SEAMS_USING_MODELS ImGui::Separator(); add_memory(std::string("Max VBuffer:"), m_statistics.max_vbuffer_gpu_size); add_memory(std::string("Max IBuffer:"), m_statistics.max_ibuffer_gpu_size); @@ -3520,6 +3782,9 @@ void GCodeViewer::render_statistics() const add_counter(std::string("Travel segments count:"), m_statistics.travel_segments_count); add_counter(std::string("Wipe segments count:"), m_statistics.wipe_segments_count); add_counter(std::string("Extrude segments count:"), m_statistics.extrude_segments_count); +#if ENABLE_SEAMS_USING_MODELS + add_counter(std::string("Instances count:"), m_statistics.instances_count); +#endif // ENABLE_SEAMS_USING_MODELS ImGui::Separator(); add_counter(std::string("VBuffers count:"), m_statistics.vbuffers_count); add_counter(std::string("IBuffers count:"), m_statistics.ibuffers_count); @@ -3550,6 +3815,21 @@ void GCodeViewer::log_memory_used(const std::string& label, int64_t additional) } } +GCodeViewer::Color GCodeViewer::option_color(EMoveType move_type) const +{ + switch (move_type) + { + case EMoveType::Tool_change: { return Options_Colors[static_cast(EOptionsColors::ToolChanges)]; } + case EMoveType::Color_change: { return Options_Colors[static_cast(EOptionsColors::ColorChanges)]; } + case EMoveType::Pause_Print: { return Options_Colors[static_cast(EOptionsColors::PausePrints)]; } + case EMoveType::Custom_GCode: { return Options_Colors[static_cast(EOptionsColors::CustomGCodes)]; } + case EMoveType::Retract: { return Options_Colors[static_cast(EOptionsColors::Retractions)]; } + case EMoveType::Unretract: { return Options_Colors[static_cast(EOptionsColors::Unretractions)]; } + case EMoveType::Seam: { return Options_Colors[static_cast(EOptionsColors::Seams)]; } + default: { return { 0.0f, 0.0f, 0.0f, 1.0f }; } + } +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 41307bad9..429175fe6 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -22,17 +22,22 @@ namespace GUI { class GCodeViewer { using IBufferType = unsigned short; - using Color = std::array; + using Color = std::array; using VertexBuffer = std::vector; using MultiVertexBuffer = std::vector; using IndexBuffer = std::vector; using MultiIndexBuffer = std::vector; +#if ENABLE_SEAMS_USING_MODELS + using InstanceBuffer = std::vector; + using InstanceIdBuffer = std::vector; +#endif // ENABLE_SEAMS_USING_MODELS static const std::vector Extrusion_Role_Colors; static const std::vector Options_Colors; static const std::vector Travel_Colors; - static const Color Wipe_Color; static const std::vector Range_Colors; + static const Color Wipe_Color; + static const Color Neutral_Color; enum class EOptionsColors : unsigned char { @@ -80,7 +85,10 @@ class GCodeViewer size_t position_size_floats() const { return 3; } size_t position_size_bytes() const { return position_size_floats() * sizeof(float); } - size_t normal_offset_floats() const { return position_size_floats(); } + size_t normal_offset_floats() const { + assert(format == EFormat::PositionNormal1 || format == EFormat::PositionNormal3); + return position_size_floats(); + } size_t normal_offset_bytes() const { return normal_offset_floats() * sizeof(float); } size_t normal_size_floats() const { @@ -96,6 +104,47 @@ class GCodeViewer void reset(); }; +#if ENABLE_SEAMS_USING_MODELS + // buffer containing instances data used to render a toolpaths using instanced models + // instance record format: 5 floats -> position.x|position.y|position.z|width|height + // which is sent to the shader as -> vec3 (offset) + vec2 (scales) in GLModel::render_instanced() + struct InstanceVBuffer + { + // ranges used to render only subparts of the intances + struct Ranges + { + struct Range + { + // offset in bytes of the 1st instance to render + unsigned int offset; + // count of instances to render + unsigned int count; + // vbo id + unsigned int vbo{ 0 }; + // Color to apply to the instances + Color color; + }; + + std::vector ranges; + + void reset(); + }; + + // cpu-side buffer containing all instances data + InstanceBuffer buffer; + // indices of the moves for all instances + std::vector s_ids; + Ranges render_ranges; + + size_t data_size_bytes() const { return s_ids.size() * instance_size_bytes(); } + + size_t instance_size_floats() const { return 5; } + size_t instance_size_bytes() const { return instance_size_floats() * sizeof(float); } + + void reset(); + }; +#endif // ENABLE_SEAMS_USING_MODELS + // ibo buffer containing indices data (for lines/triangles) used to render a specific toolpath type struct IBuffer { @@ -229,13 +278,34 @@ class GCodeViewer { Point, Line, +#if ENABLE_SEAMS_USING_MODELS + Triangle, + Model +#else Triangle +#endif // ENABLE_SEAMS_USING_MODELS }; ERenderPrimitiveType render_primitive_type; + + // buffers for point, line and triangle primitive types VBuffer vertices; std::vector indices; +#if ENABLE_SEAMS_USING_MODELS + struct Model + { + GLModel model; + Color color; + InstanceVBuffer instances; + + void reset(); + }; + + // contain the buffer for model primitive types + Model model; +#endif // ENABLE_SEAMS_USING_MODELS + std::string shader; std::vector paths; // std::set seems to perform significantly better, at least on Windows. @@ -283,9 +353,24 @@ class GCodeViewer } size_t max_indices_per_segment_size_bytes() const { return max_indices_per_segment() * sizeof(IBufferType); } +#if ENABLE_SEAMS_USING_MODELS + bool has_data() const { + switch (render_primitive_type) + { + case ERenderPrimitiveType::Point: + case ERenderPrimitiveType::Line: + case ERenderPrimitiveType::Triangle: { + return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0; + } + case ERenderPrimitiveType::Model: { return model.model.is_initialized() && !model.instances.buffer.empty(); } + default: { return false; } + } + } +#else bool has_data() const { return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0; } +#endif // ENABLE_SEAMS_USING_MODELS }; // helper to render shells @@ -433,18 +518,30 @@ class GCodeViewer int64_t gl_multi_lines_calls_count{ 0 }; int64_t gl_multi_triangles_calls_count{ 0 }; int64_t gl_triangles_calls_count{ 0 }; +#if ENABLE_SEAMS_USING_MODELS + int64_t gl_instanced_models_calls_count{ 0 }; +#endif // ENABLE_SEAMS_USING_MODELS // memory int64_t results_size{ 0 }; int64_t total_vertices_gpu_size{ 0 }; int64_t total_indices_gpu_size{ 0 }; +#if ENABLE_SEAMS_USING_MODELS + int64_t total_instances_gpu_size{ 0 }; +#endif // ENABLE_SEAMS_USING_MODELS int64_t max_vbuffer_gpu_size{ 0 }; int64_t max_ibuffer_gpu_size{ 0 }; int64_t paths_size{ 0 }; int64_t render_paths_size{ 0 }; +#if ENABLE_SEAMS_USING_MODELS + int64_t models_instances_size{ 0 }; +#endif // ENABLE_SEAMS_USING_MODELS // other int64_t travel_segments_count{ 0 }; int64_t wipe_segments_count{ 0 }; int64_t extrude_segments_count{ 0 }; +#if ENABLE_SEAMS_USING_MODELS + int64_t instances_count{ 0 }; +#endif // ENABLE_SEAMS_USING_MODELS int64_t vbuffers_count{ 0 }; int64_t ibuffers_count{ 0 }; @@ -470,22 +567,34 @@ class GCodeViewer gl_multi_lines_calls_count = 0; gl_multi_triangles_calls_count = 0; gl_triangles_calls_count = 0; +#if ENABLE_SEAMS_USING_MODELS + gl_instanced_models_calls_count = 0; +#endif // ENABLE_SEAMS_USING_MODELS } void reset_sizes() { results_size = 0; total_vertices_gpu_size = 0; total_indices_gpu_size = 0; +#if ENABLE_SEAMS_USING_MODELS + total_instances_gpu_size = 0; +#endif // ENABLE_SEAMS_USING_MODELS max_vbuffer_gpu_size = 0; max_ibuffer_gpu_size = 0; paths_size = 0; render_paths_size = 0; +#if ENABLE_SEAMS_USING_MODELS + models_instances_size = 0; +#endif // ENABLE_SEAMS_USING_MODELS } void reset_others() { travel_segments_count = 0; wipe_segments_count = 0; extrude_segments_count = 0; +#if ENABLE_SEAMS_USING_MODELS + instances_count = 0; +#endif // ENABLE_SEAMS_USING_MODELS vbuffers_count = 0; ibuffers_count = 0; } @@ -563,6 +672,9 @@ public: Endpoints endpoints; Endpoints current; Endpoints last_current; +#if ENABLE_SEAMS_USING_MODELS + Endpoints global; +#endif // ENABLE_SEAMS_USING_MODELS Vec3f current_position{ Vec3f::Zero() }; Marker marker; GCodeWindow gcode_window; @@ -632,7 +744,7 @@ public: void update_shells_color_by_extruder(const DynamicPrintConfig* config); void reset(); - void render() const; + void render(); bool has_data() const { return !m_roles.empty(); } bool can_export_toolpaths() const; @@ -678,17 +790,18 @@ private: void load_toolpaths(const GCodeProcessor::Result& gcode_result); void load_shells(const Print& print, bool initialized); void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; - void render_toolpaths() const; - void render_shells() const; - void render_legend(float& legend_height) const; + void render_toolpaths(); + void render_shells(); + void render_legend(float& legend_height); #if ENABLE_GCODE_VIEWER_STATISTICS - void render_statistics() const; + void render_statistics(); #endif // ENABLE_GCODE_VIEWER_STATISTICS bool is_visible(ExtrusionRole role) const { return role < erCount && (m_extrusions.role_visibility_flags & (1 << role)) != 0; } bool is_visible(const Path& path) const { return is_visible(path.role); } void log_memory_used(const std::string& label, int64_t additional = 0) const; + Color option_color(EMoveType move_type) const; }; } // namespace GUI diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ee1d3b14a..06c6cdda5 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4148,7 +4148,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const glsafe(::glEnable(GL_DEPTH_TEST)); shader->start_using(); - shader->set_uniform("emission_factor", 0.0); + shader->set_uniform("emission_factor", 0.0f); for (GLVolume* vol : visible_volumes) { shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? orange : gray); @@ -5170,7 +5170,7 @@ void GLCanvas3D::_render_objects() m_camera_clipping_plane = ClippingPlane::ClipsNothing(); } -void GLCanvas3D::_render_gcode() const +void GLCanvas3D::_render_gcode() { m_gcode_viewer.render(); } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index ba8430c07..3750e3a3e 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -904,7 +904,7 @@ private: #else void _render_objects(); #endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING - void _render_gcode() const; + void _render_gcode(); void _render_selection() const; void _render_sequential_clearance(); #if ENABLE_RENDER_SELECTION_CENTER diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index a9550bc04..5cddd7fa3 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -208,6 +208,85 @@ void GLModel::render() const } } +#if ENABLE_SEAMS_USING_MODELS +void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count) const +{ + if (instances_vbo == 0) + return; + + GLShaderProgram* shader = wxGetApp().get_current_shader(); + assert(shader == nullptr || boost::algorithm::iends_with(shader->get_name(), "_instanced")); + + // vertex attributes + GLint position_id = (shader != nullptr) ? shader->get_attrib_location("v_position") : -1; + GLint normal_id = (shader != nullptr) ? shader->get_attrib_location("v_normal") : -1; + assert(position_id != -1 && normal_id != -1); + + // instance attributes + GLint offset_id = (shader != nullptr) ? shader->get_attrib_location("i_offset") : -1; + GLint scales_id = (shader != nullptr) ? shader->get_attrib_location("i_scales") : -1; + assert(offset_id != -1 && scales_id != -1); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, instances_vbo)); + if (offset_id != -1) { + glsafe(::glVertexAttribPointer(offset_id, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (GLvoid*)0)); + glsafe(::glEnableVertexAttribArray(offset_id)); + glsafe(::glVertexAttribDivisor(offset_id, 1)); + } + if (scales_id != -1) { + glsafe(::glVertexAttribPointer(scales_id, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (GLvoid*)(3 * sizeof(float)))); + glsafe(::glEnableVertexAttribArray(scales_id)); + glsafe(::glVertexAttribDivisor(scales_id, 1)); + } + + for (const RenderData& data : m_render_data) { + if (data.vbo_id == 0 || data.ibo_id == 0) + continue; + + GLenum mode; + switch (data.type) + { + default: + case PrimitiveType::Triangles: { mode = GL_TRIANGLES; break; } + case PrimitiveType::Lines: { mode = GL_LINES; break; } + case PrimitiveType::LineStrip: { mode = GL_LINE_STRIP; break; } + case PrimitiveType::LineLoop: { mode = GL_LINE_LOOP; break; } + } + + if (shader != nullptr) + shader->set_uniform("uniform_color", data.color); + else + glsafe(::glColor4fv(data.color.data())); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id)); + if (position_id != -1) { + glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)0)); + glsafe(::glEnableVertexAttribArray(position_id)); + } + if (normal_id != -1) { + glsafe(::glVertexAttribPointer(normal_id, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)(3 * sizeof(float)))); + glsafe(::glEnableVertexAttribArray(normal_id)); + } + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id)); + glsafe(::glDrawElementsInstanced(mode, static_cast(data.indices_count), GL_UNSIGNED_INT, (const void*)0, instances_count)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + if (normal_id != -1) + glsafe(::glDisableVertexAttribArray(normal_id)); + if (position_id != -1) + glsafe(::glDisableVertexAttribArray(position_id)); + } + + if (scales_id != -1) + glsafe(::glDisableVertexAttribArray(scales_id)); + if (offset_id != -1) + glsafe(::glDisableVertexAttribArray(offset_id)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); +} +#endif // ENABLE_SEAMS_USING_MODELS + void GLModel::send_to_gpu(RenderData& data, const std::vector& vertices, const std::vector& indices) { assert(data.vbo_id == 0); @@ -598,5 +677,53 @@ GLModel::InitializationData straight_arrow(float tip_width, float tip_height, fl return data; } +GLModel::InitializationData diamond(int resolution) +{ + resolution = std::max(4, resolution); + + GLModel::InitializationData data; + GLModel::InitializationData::Entity entity; + entity.type = GLModel::PrimitiveType::Triangles; + + const float step = 2.0f * float(PI) / float(resolution); + + // positions + for (int i = 0; i < resolution; ++i) { + float ii = float(i) * step; + entity.positions.emplace_back(0.5f * ::cos(ii), 0.5f * ::sin(ii), 0.0f); + } + entity.positions.emplace_back(0.0f, 0.0f, 0.5f); + entity.positions.emplace_back(0.0f, 0.0f, -0.5f); + + // normals + for (const Vec3f& v : entity.positions) { + entity.normals.emplace_back(v.normalized()); + } + + // triangles + // top + for (int i = 0; i < resolution; ++i) { + entity.indices.push_back(i + 0); + entity.indices.push_back(i + 1); + entity.indices.push_back(resolution); + } + entity.indices.push_back(resolution - 1); + entity.indices.push_back(0); + entity.indices.push_back(resolution); + + // bottom + for (int i = 0; i < resolution; ++i) { + entity.indices.push_back(i + 0); + entity.indices.push_back(resolution + 1); + entity.indices.push_back(i + 1); + } + entity.indices.push_back(resolution - 1); + entity.indices.push_back(resolution + 1); + entity.indices.push_back(0); + + data.entities.emplace_back(entity); + return data; +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index cab2fe220..8e0039b08 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -72,6 +72,9 @@ namespace GUI { void reset(); void render() const; +#if ENABLE_SEAMS_USING_MODELS + void render_instanced(unsigned int instances_vbo, unsigned int instances_count) const; +#endif // ENABLE_SEAMS_USING_MODELS bool is_initialized() const { return !m_render_data.empty(); } @@ -100,6 +103,11 @@ namespace GUI { // used to render sidebar hints for position and scale GLModel::InitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness); + // create a diamond with the given resolution + // the origin of the diamond is in its center + // the diamond is contained into a box with size [1, 1, 1] + GLModel::InitializationData diamond(int resolution); + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 33eec63e8..c93f22f46 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -38,9 +38,17 @@ std::pair GLShadersManager::init() // used to render printbed valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" }); // used to render options in gcode preview - valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" }); - if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20)) - valid &= append_shader("options_120", { "options_120.vs", "options_120.fs" }); +#if ENABLE_SEAMS_USING_MODELS + if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 1)) + valid &= append_shader("gouraud_light_instanced", { "gouraud_light_instanced.vs", "gouraud_light_instanced.fs" }); + else { +#endif // ENABLE_SEAMS_USING_MODELS + valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" }); + if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20)) + valid &= append_shader("options_120", { "options_120.vs", "options_120.fs" }); +#if ENABLE_SEAMS_USING_MODELS + } +#endif // ENABLE_SEAMS_USING_MODELS // 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 diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 9cda6d75b..cc089e26e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -191,7 +191,7 @@ void GLGizmoBase::render_grabbers(float size) const if (shader == nullptr) return; shader->start_using(); - shader->set_uniform("emission_factor", 0.1); + shader->set_uniform("emission_factor", 0.1f); for (int i = 0; i < (int)m_grabbers.size(); ++i) { if (m_grabbers[i].enabled) m_grabbers[i].render(m_hover_id == i, size); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index d7bdf9474..641258ca4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -136,7 +136,7 @@ void GLGizmoCut::on_render() if (shader == nullptr) return; shader->start_using(); - shader->set_uniform("emission_factor", 0.1); + shader->set_uniform("emission_factor", 0.1f); m_grabbers[0].color = GrabberColor; m_grabbers[0].render(m_hover_id == 0, (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 1211864eb..9a056adcb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -141,7 +141,7 @@ void GLGizmoMove3D::on_render() GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader != nullptr) { shader->start_using(); - shader->set_uniform("emission_factor", 0.1); + shader->set_uniform("emission_factor", 0.1f); // draw grabber float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0); m_grabbers[m_hover_id].render(true, mean_size); @@ -208,7 +208,7 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box const_cast(&m_vbo_cone)->set_color(-1, color); if (!picking) { shader->start_using(); - shader->set_uniform("emission_factor", 0.1); + shader->set_uniform("emission_factor", 0.1f); } glsafe(::glPushMatrix()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 04e08adc1..36759d2ec 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -339,7 +339,7 @@ void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool pick const_cast(&m_cone)->set_color(-1, color); if (!picking) { shader->start_using(); - shader->set_uniform("emission_factor", 0.1); + shader->set_uniform("emission_factor", 0.1f); } glsafe(::glPushMatrix()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 849046962..894d844d8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -236,7 +236,7 @@ void GLGizmoScale3D::on_render() GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader != nullptr) { shader->start_using(); - shader->set_uniform("emission_factor", 0.1); + shader->set_uniform("emission_factor", 0.1f); // draw grabbers m_grabbers[0].render(true, grabber_mean_size); m_grabbers[1].render(true, grabber_mean_size); @@ -251,7 +251,7 @@ void GLGizmoScale3D::on_render() GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader != nullptr) { shader->start_using(); - shader->set_uniform("emission_factor", 0.1); + shader->set_uniform("emission_factor", 0.1f); // draw grabbers m_grabbers[2].render(true, grabber_mean_size); m_grabbers[3].render(true, grabber_mean_size); @@ -266,7 +266,7 @@ void GLGizmoScale3D::on_render() GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader != nullptr) { shader->start_using(); - shader->set_uniform("emission_factor", 0.1); + shader->set_uniform("emission_factor", 0.1f); // draw grabbers m_grabbers[4].render(true, grabber_mean_size); m_grabbers[5].render(true, grabber_mean_size); @@ -284,7 +284,7 @@ void GLGizmoScale3D::on_render() GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader != nullptr) { shader->start_using(); - shader->set_uniform("emission_factor", 0.1); + shader->set_uniform("emission_factor", 0.1f); // draw grabbers for (int i = 6; i < 10; ++i) { m_grabbers[i].render(true, grabber_mean_size); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 6d8ae36ab..40b8d436d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -169,7 +169,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const_cast(&m_cone)->set_color(-1, render_color); const_cast(&m_sphere)->set_color(-1, render_color); if (shader && !picking) - shader->set_uniform("emission_factor", 0.5); + shader->set_uniform("emission_factor", 0.5f); // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. glsafe(::glPushMatrix()); @@ -224,7 +224,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) render_color[3] = 0.7f; const_cast(&m_cylinder)->set_color(-1, render_color); if (shader) - shader->set_uniform("emission_factor", 0.5); + shader->set_uniform("emission_factor", 0.5f); for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) { if (is_mesh_point_clipped(drain_hole.pos.cast())) continue; diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 3409c531d..92c758d4b 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1874,7 +1874,7 @@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) con const_cast(&m_arrow)->set_color(-1, uniform_scale ? UNIFORM_SCALE_COLOR : get_color(axis)); GLShaderProgram* shader = wxGetApp().get_current_shader(); if (shader != nullptr) - shader->set_uniform("emission_factor", 0.0); + shader->set_uniform("emission_factor", 0.0f); glsafe(::glTranslated(0.0, 5.0, 0.0)); m_arrow.render();