diff --git a/resources/shaders/extrusions.fs b/resources/shaders/extrusions.fs new file mode 100644 index 000000000..046dade8a --- /dev/null +++ b/resources/shaders/extrusions.fs @@ -0,0 +1,45 @@ +#version 110 + +#define INTENSITY_AMBIENT 0.3 +#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.0, 0.0, 1.0); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +uniform vec4 uniform_color; + +varying vec3 eye_position; +varying vec3 eye_normal; +//varying float world_normal_z; + +// x = tainted, y = specular; +vec2 intensity; + +void main() +{ + vec3 normal = normalize(eye_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(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + +// // darkens fragments whose normal points downward +// if (world_normal_z < 0.0) +// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); + + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/extrusions.vs b/resources/shaders/extrusions.vs new file mode 100644 index 000000000..d97adbabe --- /dev/null +++ b/resources/shaders/extrusions.vs @@ -0,0 +1,15 @@ +#version 110 + +varying vec3 eye_position; +varying vec3 eye_normal; +//// world z component of the normal used to darken the lower side of the toolpaths +//varying float world_normal_z; + +void main() +{ + eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; + eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); +// eye_normal = gl_NormalMatrix * gl_Normal; +// world_normal_z = gl_Normal.z; + gl_Position = ftransform(); +} diff --git a/resources/shaders/retractions.fs b/resources/shaders/retractions.fs new file mode 100644 index 000000000..046dade8a --- /dev/null +++ b/resources/shaders/retractions.fs @@ -0,0 +1,45 @@ +#version 110 + +#define INTENSITY_AMBIENT 0.3 +#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.0, 0.0, 1.0); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +uniform vec4 uniform_color; + +varying vec3 eye_position; +varying vec3 eye_normal; +//varying float world_normal_z; + +// x = tainted, y = specular; +vec2 intensity; + +void main() +{ + vec3 normal = normalize(eye_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(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + +// // darkens fragments whose normal points downward +// if (world_normal_z < 0.0) +// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); + + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/retractions.vs b/resources/shaders/retractions.vs new file mode 100644 index 000000000..d97adbabe --- /dev/null +++ b/resources/shaders/retractions.vs @@ -0,0 +1,15 @@ +#version 110 + +varying vec3 eye_position; +varying vec3 eye_normal; +//// world z component of the normal used to darken the lower side of the toolpaths +//varying float world_normal_z; + +void main() +{ + eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; + eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); +// eye_normal = gl_NormalMatrix * gl_Normal; +// world_normal_z = gl_Normal.z; + gl_Position = ftransform(); +} diff --git a/resources/shaders/toolchanges.fs b/resources/shaders/toolchanges.fs new file mode 100644 index 000000000..046dade8a --- /dev/null +++ b/resources/shaders/toolchanges.fs @@ -0,0 +1,45 @@ +#version 110 + +#define INTENSITY_AMBIENT 0.3 +#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.0, 0.0, 1.0); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +uniform vec4 uniform_color; + +varying vec3 eye_position; +varying vec3 eye_normal; +//varying float world_normal_z; + +// x = tainted, y = specular; +vec2 intensity; + +void main() +{ + vec3 normal = normalize(eye_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(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + +// // darkens fragments whose normal points downward +// if (world_normal_z < 0.0) +// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); + + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/toolchanges.vs b/resources/shaders/toolchanges.vs new file mode 100644 index 000000000..d97adbabe --- /dev/null +++ b/resources/shaders/toolchanges.vs @@ -0,0 +1,15 @@ +#version 110 + +varying vec3 eye_position; +varying vec3 eye_normal; +//// world z component of the normal used to darken the lower side of the toolpaths +//varying float world_normal_z; + +void main() +{ + eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; + eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); +// eye_normal = gl_NormalMatrix * gl_Normal; +// world_normal_z = gl_Normal.z; + gl_Position = ftransform(); +} diff --git a/resources/shaders/travels.fs b/resources/shaders/travels.fs new file mode 100644 index 000000000..046dade8a --- /dev/null +++ b/resources/shaders/travels.fs @@ -0,0 +1,45 @@ +#version 110 + +#define INTENSITY_AMBIENT 0.3 +#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.0, 0.0, 1.0); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +uniform vec4 uniform_color; + +varying vec3 eye_position; +varying vec3 eye_normal; +//varying float world_normal_z; + +// x = tainted, y = specular; +vec2 intensity; + +void main() +{ + vec3 normal = normalize(eye_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(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + +// // darkens fragments whose normal points downward +// if (world_normal_z < 0.0) +// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); + + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/travels.vs b/resources/shaders/travels.vs new file mode 100644 index 000000000..d97adbabe --- /dev/null +++ b/resources/shaders/travels.vs @@ -0,0 +1,15 @@ +#version 110 + +varying vec3 eye_position; +varying vec3 eye_normal; +//// world z component of the normal used to darken the lower side of the toolpaths +//varying float world_normal_z; + +void main() +{ + eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; + eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); +// eye_normal = gl_NormalMatrix * gl_Normal; +// world_normal_z = gl_Normal.z; + gl_Position = ftransform(); +} diff --git a/resources/shaders/unretractions.fs b/resources/shaders/unretractions.fs new file mode 100644 index 000000000..046dade8a --- /dev/null +++ b/resources/shaders/unretractions.fs @@ -0,0 +1,45 @@ +#version 110 + +#define INTENSITY_AMBIENT 0.3 +#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.0, 0.0, 1.0); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +uniform vec4 uniform_color; + +varying vec3 eye_position; +varying vec3 eye_normal; +//varying float world_normal_z; + +// x = tainted, y = specular; +vec2 intensity; + +void main() +{ + vec3 normal = normalize(eye_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(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + +// // darkens fragments whose normal points downward +// if (world_normal_z < 0.0) +// intensity.x *= (1.0 + world_normal_z * (1.0 - INTENSITY_AMBIENT)); + + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/unretractions.vs b/resources/shaders/unretractions.vs new file mode 100644 index 000000000..d97adbabe --- /dev/null +++ b/resources/shaders/unretractions.vs @@ -0,0 +1,15 @@ +#version 110 + +varying vec3 eye_position; +varying vec3 eye_normal; +//// world z component of the normal used to darken the lower side of the toolpaths +//varying float world_normal_z; + +void main() +{ + eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; + eye_normal = gl_NormalMatrix * vec3(0.0, 0.0, 1.0); +// eye_normal = gl_NormalMatrix * gl_Normal; +// world_normal_z = gl_Normal.z; + gl_Position = ftransform(); +} diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index f5094553a..c75c24002 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -38,6 +38,8 @@ void GCodeProcessor::CpColor::reset() current = 0; } +unsigned int GCodeProcessor::Result::id = 0; + void GCodeProcessor::apply_config(const PrintConfig& config) { m_parser.apply_config(config); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 54ac546b3..1f7af9c29 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -39,17 +39,6 @@ namespace Slic3r { Relative }; - enum class EMoveType : unsigned char - { - Noop, - Retract, - Unretract, - Tool_change, - Travel, - Extrude, - Num_Types - }; - struct CachedPosition { AxisCoords position; // mm @@ -67,6 +56,17 @@ namespace Slic3r { }; public: + enum class EMoveType : unsigned char + { + Noop, + Retract, + Unretract, + Tool_change, + Travel, + Extrude, + Count + }; + struct MoveVertex { EMoveType type{ EMoveType::Noop }; @@ -98,8 +98,9 @@ namespace Slic3r { struct Result { + static unsigned int id; std::vector<MoveVertex> moves; - void reset() { moves = std::vector<MoveVertex>(); } + void reset() { ++id; moves = std::vector<MoveVertex>(); } }; private: diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index c8f7e9f1c..f5f5f6eb6 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -56,6 +56,8 @@ set(SLIC3R_GUI_SOURCES GUI/GLTexture.cpp GUI/GLToolbar.hpp GUI/GLToolbar.cpp + GUI/GCodeViewer.hpp + GUI/GCodeViewer.cpp GUI/Preferences.cpp GUI/Preferences.hpp GUI/Preset.cpp diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp new file mode 100644 index 000000000..584ef06c3 --- /dev/null +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -0,0 +1,242 @@ +#include "libslic3r/libslic3r.h" +#include "GCodeViewer.hpp" +#include "3DScene.hpp" + +#if ENABLE_GCODE_VIEWER + +#include <GL/glew.h> +#include <boost/log/trivial.hpp> + +#include <array> + +namespace Slic3r { +namespace GUI { + +static unsigned char buffer_id(GCodeProcessor::EMoveType type) { + return static_cast<unsigned char>(type) - static_cast<unsigned char>(GCodeProcessor::EMoveType::Retract); +} + +static GCodeProcessor::EMoveType buffer_type(unsigned char id) { + return static_cast<GCodeProcessor::EMoveType>(static_cast<unsigned char>(GCodeProcessor::EMoveType::Retract) + id); +} + +void GCodeViewer::generate(const GCodeProcessor::Result& gcode_result) +{ + if (m_last_result_id == gcode_result.id) + return; + + m_last_result_id = gcode_result.id; + + // release gpu memory, if used + reset_buffers(); + + // convert data + size_t vertices_count = gcode_result.moves.size(); + for (size_t i = 0; i < vertices_count; ++i) + { + // skip first vertex + if (i == 0) + continue; + + const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1]; + const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i]; + + Buffer& buffer = m_buffers[buffer_id(curr.type)]; + + switch (curr.type) + { + case GCodeProcessor::EMoveType::Tool_change: + case GCodeProcessor::EMoveType::Retract: + case GCodeProcessor::EMoveType::Unretract: + { + for (int j = 0; j < 3; ++j) + { + buffer.data.insert(buffer.data.end(), curr.position[j]); + } + break; + } + case GCodeProcessor::EMoveType::Extrude: + case GCodeProcessor::EMoveType::Travel: + { + for (int j = 0; j < 3; ++j) + { + buffer.data.insert(buffer.data.end(), prev.position[j]); + } + for (int j = 0; j < 3; ++j) + { + buffer.data.insert(buffer.data.end(), curr.position[j]); + } + break; + } + default: + { + continue; + } + } + } + + // send data to gpu + for (Buffer& buffer : m_buffers) + { + glsafe(::glGenBuffers(1, &buffer.vbo_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, buffer.data.size() * sizeof(float), buffer.data.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + } +} + +void GCodeViewer::render() const +{ + auto set_color = [](GLint current_program_id, const std::array<float, 4>& color) { + if (current_program_id > 0) + { + GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; + if (color_id >= 0) + { + glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)color.data())); + return; + } + } + BOOST_LOG_TRIVIAL(error) << "Unable to find uniform_color uniform"; + }; + + unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); + unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); + + glsafe(::glEnable(GL_DEPTH_TEST)); + + for (unsigned char i = begin_id; i < end_id; ++i) + { + const Buffer& buffer = m_buffers[i]; + if (buffer.vbo_id == 0) + continue; + + const Shader& shader = m_shaders[i]; + if (shader.is_initialized()) + { + shader.start_using(); + + GLint current_program_id; + glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); + + GCodeProcessor::EMoveType type = buffer_type(i); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id)); + glsafe(::glVertexPointer(3, GL_FLOAT, Buffer::stride(type), (const void*)0)); + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + + switch (type) + { + case GCodeProcessor::EMoveType::Tool_change: + case GCodeProcessor::EMoveType::Retract: + case GCodeProcessor::EMoveType::Unretract: + { + std::array<float, 4> color = { 0.0f, 1.0f, 0.0f, 1.0f }; + set_color(current_program_id, color); + glsafe(::glDrawArrays(GL_POINTS, 0, (GLsizei)buffer.data.size() / Buffer::record_size(type))); + break; + } + case GCodeProcessor::EMoveType::Extrude: + { + std::array<float, 4> color = { 1.0f, 0.0f, 0.0f, 1.0f }; + set_color(current_program_id, color); + glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)buffer.data.size() / Buffer::record_size(type))); + break; + } + case GCodeProcessor::EMoveType::Travel: + { + std::array<float, 4> color = { 1.0f, 1.0f, 0.0f, 1.0f }; + set_color(current_program_id, color); + glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)buffer.data.size() / Buffer::record_size(type))); + break; + } + default: + { + break; + } + } + + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + shader.stop_using(); + } + } +} + +bool GCodeViewer::init_shaders() +{ + unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract); + unsigned char end_id = buffer_id(GCodeProcessor::EMoveType::Count); + + for (unsigned char i = begin_id; i < end_id; ++i) + { + Shader& shader = m_shaders[i]; + std::string vertex_shader_src; + std::string fragment_shader_src; + GCodeProcessor::EMoveType type = buffer_type(i); + switch (type) + { + case GCodeProcessor::EMoveType::Tool_change: + { + vertex_shader_src = "toolchanges.vs"; + fragment_shader_src = "toolchanges.fs"; + break; + } + case GCodeProcessor::EMoveType::Retract: + { + vertex_shader_src = "retractions.vs"; + fragment_shader_src = "retractions.fs"; + break; + } + case GCodeProcessor::EMoveType::Unretract: + { + vertex_shader_src = "unretractions.vs"; + fragment_shader_src = "unretractions.fs"; + break; + } + case GCodeProcessor::EMoveType::Extrude: + { + vertex_shader_src = "extrusions.vs"; + fragment_shader_src = "extrusions.fs"; + break; + } + case GCodeProcessor::EMoveType::Travel: + { + vertex_shader_src = "travels.vs"; + fragment_shader_src = "travels.fs"; + break; + } + default: + { + break; + } + } + + if (!shader.init(vertex_shader_src, fragment_shader_src)) + { + BOOST_LOG_TRIVIAL(error) << "Unable to initialize toolpaths shader: please, check that the files " << vertex_shader_src << " and " << fragment_shader_src << " are available"; + return false; + } + } + + return true; +} + +void GCodeViewer::reset_buffers() +{ + for (Buffer& buffer : m_buffers) + { + // release gpu memory + if (buffer.vbo_id > 0) + glsafe(::glDeleteBuffers(1, &buffer.vbo_id)); + + // release cpu memory + buffer.data = std::vector<float>(); + } +} + +} // namespace GUI +} // namespace Slic3r + +#endif // ENABLE_GCODE_VIEWER diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp new file mode 100644 index 000000000..95250b203 --- /dev/null +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -0,0 +1,63 @@ +#ifndef slic3r_GCodeViewer_hpp_ +#define slic3r_GCodeViewer_hpp_ + +#if ENABLE_GCODE_VIEWER + +#include "GLShader.hpp" +#include "libslic3r/GCode/GCodeProcessor.hpp" + +#include <vector> + +namespace Slic3r { +namespace GUI { + +class GCodeViewer +{ + struct Buffer + { + unsigned int vbo_id{ 0 }; + std::vector<float> data; + + static size_t stride(GCodeProcessor::EMoveType type) + { + return 3 * sizeof(float); + } + + static size_t record_size(GCodeProcessor::EMoveType type) + { + switch (type) + { + case GCodeProcessor::EMoveType::Tool_change: + case GCodeProcessor::EMoveType::Retract: + case GCodeProcessor::EMoveType::Unretract: { return 3; } + case GCodeProcessor::EMoveType::Extrude: + case GCodeProcessor::EMoveType::Travel: { return 6; } + default: { return 0; } + } + } + }; + + std::vector<Buffer> m_buffers{ static_cast<size_t>(GCodeProcessor::EMoveType::Extrude) }; + std::vector<Shader> m_shaders{ static_cast<size_t>(GCodeProcessor::EMoveType::Extrude) }; + unsigned int m_last_result_id{ 0 }; + +public: + GCodeViewer() = default; + ~GCodeViewer() { reset_buffers(); } + + bool init() { return init_shaders(); } + void generate(const GCodeProcessor::Result& gcode_result); + void render() const; + +private: + bool init_shaders(); + void reset_buffers(); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // ENABLE_GCODE_VIEWER + +#endif // slic3r_GCodeViewer_hpp_ + diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 85ea44ef1..c9c4ab336 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1678,6 +1678,14 @@ bool GLCanvas3D::init() return false; } +#if ENABLE_GCODE_VIEWER + if (!m_main_toolbar.is_enabled()) + { + if (!m_gcode_viewer.init()) + return false; + } +#endif // ENABLE_GCODE_VIEWER + // on linux the gl context is not valid until the canvas is not shown on screen // we defer the geometry finalization of volumes until the first call to render() m_volumes.finalize_geometry(true); @@ -2109,6 +2117,9 @@ void GLCanvas3D::render() _render_background(); _render_objects(); +#if ENABLE_GCODE_VIEWER + _render_gcode(); +#endif // ENABLE_GCODE_VIEWER _render_sla_slices(); _render_selection(); #if ENABLE_NON_STATIC_CANVAS_MANAGER @@ -2783,6 +2794,8 @@ void GLCanvas3D::load_gcode_preview_2(const GCodeProcessor::Result& gcode_result out << v.to_string() << "\n"; } out.close(); + + m_gcode_viewer.generate(gcode_result); #endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT } #endif // ENABLE_GCODE_VIEWER @@ -5440,6 +5453,13 @@ void GLCanvas3D::_render_objects() const m_camera_clipping_plane = ClippingPlane::ClipsNothing(); } +#if ENABLE_GCODE_VIEWER +void GLCanvas3D::_render_gcode() const +{ + m_gcode_viewer.render(); +} +#endif // ENABLE_GCODE_VIEWER + void GLCanvas3D::_render_selection() const { float scale_factor = 1.0; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 0c82f058f..8c6b3c3f0 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -21,6 +21,7 @@ #endif // !ENABLE_NON_STATIC_CANVAS_MANAGER #if ENABLE_GCODE_VIEWER #include "libslic3r/GCode/GCodeProcessor.hpp" +#include "GCodeViewer.hpp" #endif // ENABLE_GCODE_VIEWER #include <float.h> @@ -468,6 +469,10 @@ private: bool m_extra_frame_requested; mutable GLVolumeCollection m_volumes; +#if ENABLE_GCODE_VIEWER + GCodeViewer m_gcode_viewer; +#endif // ENABLE_GCODE_VIEWER + Selection m_selection; const DynamicPrintConfig* m_config; Model* m_model; @@ -764,6 +769,9 @@ private: void _render_background() const; void _render_bed(float theta, bool show_axes) const; void _render_objects() const; +#if ENABLE_GCODE_VIEWER + void _render_gcode() const; +#endif // ENABLE_GCODE_VIEWER void _render_selection() const; #if ENABLE_RENDER_SELECTION_CENTER void _render_selection_center() const; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 979509562..0171dd597 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -946,9 +946,10 @@ void Preview::load_print_as_fff(bool keep_z_range) m_canvas->set_selected_extruder(0); if (gcode_preview_data_valid) { // Load the real G-code preview. - m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); #if ENABLE_GCODE_VIEWER m_canvas->load_gcode_preview_2(*m_gcode_result); +#else + m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); #endif // ENABLE_GCODE_VIEWER m_loaded = true; } else {