GCodeViewer -> Export of extrude toolpaths to obj files

This commit is contained in:
enricoturri1966 2020-07-03 12:17:12 +02:00
parent feb4857cf8
commit 0b1086f390
6 changed files with 300 additions and 10 deletions
src/slic3r/GUI

View file

@ -24,6 +24,7 @@
#include <GL/glew.h>
#include <boost/log/trivial.hpp>
#include <boost/nowide/cstdio.hpp>
#include <array>
#include <algorithm>
@ -487,6 +488,284 @@ void GCodeViewer::set_layers_z_range(const std::array<double, 2>& layers_z_range
wxGetApp().plater()->update_preview_moves_slider();
}
void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
{
if (filename == nullptr)
return;
if (!has_data())
return;
wxBusyCursor busy;
// the data needed is contained into the Extrude TBuffer
const TBuffer& buffer = m_buffers[buffer_id(GCodeProcessor::EMoveType::Extrude)];
if (buffer.vertices.id == 0 || buffer.indices.id == 0)
return;
// collect color information to generate materials
std::vector<Color> colors;
for (const RenderPath& path : buffer.render_paths) {
colors.push_back(path.color);
}
// save materials file
boost::filesystem::path mat_filename(filename);
mat_filename.replace_extension("mtl");
FILE* fp = boost::nowide::fopen(mat_filename.string().c_str(), "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error) << "GCodeViewer::export_toolpaths_to_obj: Couldn't open " << mat_filename.string().c_str() << " for writing";
return;
}
fprintf(fp, "# G-Code Toolpaths Materials\n");
fprintf(fp, "# Generated by %s based on Slic3r\n", SLIC3R_BUILD_ID);
unsigned int colors_count = 1;
for (const Color& color : colors)
{
fprintf(fp, "\nnewmtl material_%d\n", colors_count++);
fprintf(fp, "Ka 1 1 1\n");
fprintf(fp, "Kd %f %f %f\n", color[0], color[1], color[2]);
fprintf(fp, "Ks 0 0 0\n");
}
fclose(fp);
// save geometry file
fp = boost::nowide::fopen(filename, "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error) << "GCodeViewer::export_toolpaths_to_obj: Couldn't open " << filename << " for writing";
return;
}
fprintf(fp, "# G-Code Toolpaths\n");
fprintf(fp, "# Generated by %s based on Slic3r\n", SLIC3R_BUILD_ID);
fprintf(fp, "\nmtllib ./%s\n", mat_filename.filename().string().c_str());
// get vertices data from vertex buffer on gpu
size_t floats_per_vertex = buffer.vertices.vertex_size_floats();
std::vector<float> vertices = std::vector<float>(buffer.vertices.count * floats_per_vertex);
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vertices.id));
glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, 0, buffer.vertices.data_size_bytes(), vertices.data()));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
auto get_vertex = [&vertices, floats_per_vertex](size_t id) {
// extract vertex from vector of floats
size_t base_id = id * floats_per_vertex;
return Vec3f(vertices[base_id + 0], vertices[base_id + 1], vertices[base_id + 2]);
};
struct Segment
{
Vec3f v1;
Vec3f v2;
Vec3f dir;
Vec3f right;
Vec3f up;
Vec3f rl_displacement;
Vec3f tb_displacement;
float length;
};
auto generate_segment = [get_vertex](size_t start_id, float half_width, float half_height) {
auto local_basis = [](const Vec3f& dir) {
// calculate local basis (dir, right, up) on given segment
std::array<Vec3f, 3> ret;
ret[0] = dir.normalized();
if (std::abs(ret[0][2]) < EPSILON) {
// segment parallel to XY plane
ret[1] = { ret[0][1], -ret[0][0], 0.0f };
ret[2] = Vec3f::UnitZ();
}
else if (std::abs(std::abs(ret[0].dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) {
// segment parallel to Z axis
ret[1] = Vec3f::UnitX();
ret[2] = Vec3f::UnitY();
}
else {
ret[0] = dir.normalized();
ret[1] = ret[0].cross(Vec3f::UnitZ()).normalized();
ret[2] = ret[1].cross(ret[0]);
}
return ret;
};
Vec3f v1 = get_vertex(start_id);
Vec3f v2 = get_vertex(start_id + 1);
float length = (v2 - v1).norm();
const auto&& [dir, right, up] = local_basis(v2 - v1);
return Segment({ v1, v2, dir, right, up, half_width * right, half_height * up, length });
};
size_t out_vertices_count = 0;
for (size_t i = 0; i < buffer.render_paths.size(); ++i) {
// get paths segments from buffer paths
const RenderPath& render_path = buffer.render_paths[i];
const Path& path = buffer.paths[render_path.path_id];
float half_width = 0.5f * path.width;
float half_height = 0.5f * path.height;
// generates vertices/normals/triangles
std::vector<Vec3f> out_vertices;
std::vector<Vec3f> out_normals;
using Triangle = std::array<size_t, 3>;
std::vector<Triangle> out_triangles;
for (size_t j = 0; j < render_path.offsets.size(); ++j) {
unsigned int start = static_cast<unsigned int>(render_path.offsets[j] / sizeof(unsigned int));
unsigned int end = start + render_path.sizes[j];
for (size_t k = start; k < end; k += 2) {
Segment curr = generate_segment(k, half_width, half_height);
if (k == start) {
// starting endpoint vertices/normals
out_vertices.push_back(curr.v1 + curr.rl_displacement); out_normals.push_back(curr.right); // right
out_vertices.push_back(curr.v1 + curr.tb_displacement); out_normals.push_back(curr.up); // top
out_vertices.push_back(curr.v1 - curr.rl_displacement); out_normals.push_back(-curr.right); // left
out_vertices.push_back(curr.v1 - curr.tb_displacement); out_normals.push_back(-curr.up); // bottom
out_vertices_count += 4;
// starting cap triangles
size_t base_id = out_vertices_count - 4 + 1;
out_triangles.push_back({ base_id + 0, base_id + 1, base_id + 2 });
out_triangles.push_back({ base_id + 0, base_id + 2, base_id + 3 });
}
else {
// for the endpoint shared by the current and the previous segments
// we keep the top and bottom vertices of the previous vertices
// and add new left/right vertices for the current segment
out_vertices.push_back(curr.v1 + curr.rl_displacement); out_normals.push_back(curr.right); // right
out_vertices.push_back(curr.v1 - curr.rl_displacement); out_normals.push_back(-curr.right); // left
out_vertices_count += 2;
Segment prev = generate_segment(k - 2, half_width, half_height);
Vec3f med_dir = (prev.dir + curr.dir).normalized();
float disp = half_width * ::tan(::acos(std::clamp(curr.dir.dot(med_dir), -1.0f, 1.0f)));
Vec3f disp_vec = disp * prev.dir;
bool is_right_turn = prev.up.dot(prev.dir.cross(curr.dir)) <= 0.0f;
if (prev.dir.dot(curr.dir) < 0.7071068f) {
// if the angle between two consecutive segments is greater than 45 degrees
// we add a cap in the outside corner
// and displace the vertices in the inside corner to the same position, if possible
if (is_right_turn) {
// corner cap triangles (left)
size_t base_id = out_vertices_count - 6 + 1;
out_triangles.push_back({ base_id + 5, base_id + 2, base_id + 1 });
out_triangles.push_back({ base_id + 5, base_id + 3, base_id + 2 });
// update right vertices
if (disp < prev.length) {
base_id = out_vertices.size() - 6;
out_vertices[base_id + 0] -= disp_vec;
out_vertices[base_id + 4] = out_vertices[base_id + 0];
}
}
else {
// corner cap triangles (right)
size_t base_id = out_vertices_count - 6 + 1;
out_triangles.push_back({ base_id + 0, base_id + 4, base_id + 1 });
out_triangles.push_back({ base_id + 0, base_id + 3, base_id + 4 });
// update left vertices
if (disp < prev.length) {
base_id = out_vertices.size() - 6;
out_vertices[base_id + 2] -= disp_vec;
out_vertices[base_id + 5] = out_vertices[base_id + 2];
}
}
}
else {
// if the angle between two consecutive segments is lesser than 45 degrees
// displace the vertices to the same position
if (is_right_turn) {
size_t base_id = out_vertices.size() - 6;
// right
out_vertices[base_id + 0] -= disp_vec;
out_vertices[base_id + 4] = out_vertices[base_id + 0];
// left
out_vertices[base_id + 2] += disp_vec;
out_vertices[base_id + 5] = out_vertices[base_id + 2];
}
else {
size_t base_id = out_vertices.size() - 6;
// right
out_vertices[base_id + 0] += disp_vec;
out_vertices[base_id + 4] = out_vertices[base_id + 0];
// left
out_vertices[base_id + 2] -= disp_vec;
out_vertices[base_id + 5] = out_vertices[base_id + 2];
}
}
}
// current second endpoint vertices/normals
out_vertices.push_back(curr.v2 + curr.rl_displacement); out_normals.push_back(curr.right); // right
out_vertices.push_back(curr.v2 + curr.tb_displacement); out_normals.push_back(curr.up); // top
out_vertices.push_back(curr.v2 - curr.rl_displacement); out_normals.push_back(-curr.right); // left
out_vertices.push_back(curr.v2 - curr.tb_displacement); out_normals.push_back(-curr.up); // bottom
out_vertices_count += 4;
// sides triangles
if (k == start) {
size_t base_id = out_vertices_count - 8 + 1;
out_triangles.push_back({ base_id + 0, base_id + 4, base_id + 5 });
out_triangles.push_back({ base_id + 0, base_id + 5, base_id + 1 });
out_triangles.push_back({ base_id + 1, base_id + 5, base_id + 6 });
out_triangles.push_back({ base_id + 1, base_id + 6, base_id + 2 });
out_triangles.push_back({ base_id + 2, base_id + 6, base_id + 7 });
out_triangles.push_back({ base_id + 2, base_id + 7, base_id + 3 });
out_triangles.push_back({ base_id + 3, base_id + 7, base_id + 4 });
out_triangles.push_back({ base_id + 3, base_id + 4, base_id + 0 });
}
else {
size_t base_id = out_vertices_count - 10 + 1;
out_triangles.push_back({ base_id + 4, base_id + 6, base_id + 7 });
out_triangles.push_back({ base_id + 4, base_id + 7, base_id + 1 });
out_triangles.push_back({ base_id + 1, base_id + 7, base_id + 8 });
out_triangles.push_back({ base_id + 1, base_id + 8, base_id + 5 });
out_triangles.push_back({ base_id + 5, base_id + 8, base_id + 9 });
out_triangles.push_back({ base_id + 5, base_id + 9, base_id + 3 });
out_triangles.push_back({ base_id + 3, base_id + 9, base_id + 6 });
out_triangles.push_back({ base_id + 3, base_id + 6, base_id + 4 });
}
if (k + 2 == end) {
// ending cap triangles
size_t base_id = out_vertices_count - 4 + 1;
out_triangles.push_back({ base_id + 0, base_id + 2, base_id + 1 });
out_triangles.push_back({ base_id + 0, base_id + 3, base_id + 2 });
}
}
}
// save to file
fprintf(fp, "\n# vertices path %lld\n", i + 1);
for (const Vec3f& v : out_vertices) {
fprintf(fp, "v %g %g %g\n", v[0], v[1], v[2]);
}
fprintf(fp, "\n# normals path %lld\n", i + 1);
for (const Vec3f& n : out_normals) {
fprintf(fp, "vn %g %g %g\n", n[0], n[1], n[2]);
}
fprintf(fp, "\n# material path %lld\n", i + 1);
fprintf(fp, "usemtl material_%lld\n", i + 1);
fprintf(fp, "\n# triangles path %lld\n", i + 1);
for (const Triangle& t : out_triangles) {
fprintf(fp, "f %lld//%lld %lld//%lld %lld//%lld\n", t[0], t[0], t[1], t[1], t[2], t[2]);
}
}
// fprintf(fp, "\n#vertices count %lld\n", out_vertices_count);
fclose(fp);
}
void GCodeViewer::init_shaders()
{
unsigned char begin_id = buffer_id(GCodeProcessor::EMoveType::Retract);
@ -641,7 +920,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
const std::vector<unsigned int>& buffer_indices = indices[i];
buffer.indices.count = buffer_indices.size();
#if ENABLE_GCODE_VIEWER_STATISTICS
m_statistics.indices_size += SLIC3R_STDVEC_MEMSIZE(buffer_indices, unsigned int);
m_statistics.indices_gpu_size += buffer.indices.count * sizeof(unsigned int);
#endif // ENABLE_GCODE_VIEWER_STATISTICS
@ -876,6 +1154,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
if (it == buffer->render_paths.end()) {
it = buffer->render_paths.insert(buffer->render_paths.end(), RenderPath());
it->color = color;
it->path_id = id;
}
unsigned int size = std::min(m_sequential_view.current.last, path.last.s_id) - std::max(m_sequential_view.current.first, path.first.s_id) + 1;
@ -1482,12 +1761,6 @@ void GCodeViewer::render_statistics() const
ImGui::Separator();
ImGui::PushStyleColor(ImGuiCol_Text, ORANGE);
imgui.text(std::string("Indices CPU:"));
ImGui::PopStyleColor();
ImGui::SameLine(offset);
imgui.text(std::to_string(m_statistics.indices_size) + " bytes");
ImGui::PushStyleColor(ImGuiCol_Text, ORANGE);
imgui.text(std::string("Paths CPU:"));
ImGui::PopStyleColor();