#include "libslic3r/libslic3r.h" #include "GLModel.hpp" #include "3DScene.hpp" #include "GUI_App.hpp" #include "GLShader.hpp" #if ENABLE_GLMODEL_STATISTICS #include "Plater.hpp" #include "GLCanvas3D.hpp" #endif // ENABLE_GLMODEL_STATISTICS #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/Polygon.hpp" #if ENABLE_LEGACY_OPENGL_REMOVAL #include "libslic3r/BuildVolume.hpp" #include "libslic3r/Geometry/ConvexHull.hpp" #endif // ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_GLMODEL_STATISTICS #include <imgui/imgui_internal.h> #endif // ENABLE_GLMODEL_STATISTICS #include <boost/filesystem/operations.hpp> #include <boost/algorithm/string/predicate.hpp> #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_SMOOTH_NORMALS #include <igl/per_face_normals.h> #include <igl/per_corner_normals.h> #include <igl/per_vertex_normals.h> #endif // ENABLE_SMOOTH_NORMALS #endif // ENABLE_LEGACY_OPENGL_REMOVAL #include <GL/glew.h> namespace Slic3r { namespace GUI { #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_SMOOTH_NORMALS static void smooth_normals_corner(const TriangleMesh& mesh, std::vector<stl_normal>& normals) { using MapMatrixXfUnaligned = Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>; using MapMatrixXiUnaligned = Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>; std::vector<Vec3f> face_normals = its_face_normals(mesh.its); Eigen::MatrixXd vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), Eigen::Index(mesh.its.vertices.size()), 3).cast<double>(); Eigen::MatrixXi indices = MapMatrixXiUnaligned(mesh.its.indices.front().data(), Eigen::Index(mesh.its.indices.size()), 3); Eigen::MatrixXd in_normals = MapMatrixXfUnaligned(face_normals.front().data(), Eigen::Index(face_normals.size()), 3).cast<double>(); Eigen::MatrixXd out_normals; igl::per_corner_normals(vertices, indices, in_normals, 1.0, out_normals); normals = std::vector<stl_normal>(mesh.its.vertices.size()); for (size_t i = 0; i < mesh.its.indices.size(); ++i) { for (size_t j = 0; j < 3; ++j) { normals[mesh.its.indices[i][j]] = out_normals.row(i * 3 + j).cast<float>(); } } } #endif // ENABLE_SMOOTH_NORMALS #endif // ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_LEGACY_OPENGL_REMOVAL void GLModel::Geometry::add_vertex(const Vec2f& position) { assert(format.vertex_layout == EVertexLayout::P2); vertices.emplace_back(position.x()); vertices.emplace_back(position.y()); } void GLModel::Geometry::add_vertex(const Vec2f& position, const Vec2f& tex_coord) { assert(format.vertex_layout == EVertexLayout::P2T2); vertices.emplace_back(position.x()); vertices.emplace_back(position.y()); vertices.emplace_back(tex_coord.x()); vertices.emplace_back(tex_coord.y()); } void GLModel::Geometry::add_vertex(const Vec3f& position) { assert(format.vertex_layout == EVertexLayout::P3); vertices.emplace_back(position.x()); vertices.emplace_back(position.y()); vertices.emplace_back(position.z()); } void GLModel::Geometry::add_vertex(const Vec3f& position, const Vec2f& tex_coord) { assert(format.vertex_layout == EVertexLayout::P3T2); vertices.insert(vertices.end(), position.data(), position.data() + 3); vertices.insert(vertices.end(), tex_coord.data(), tex_coord.data() + 2); } void GLModel::Geometry::add_vertex(const Vec3f& position, const Vec3f& normal, const Vec2f& tex_coord) { assert(format.vertex_layout == EVertexLayout::P3N3T2); vertices.emplace_back(position.x()); vertices.emplace_back(position.y()); vertices.emplace_back(position.z()); vertices.emplace_back(normal.x()); vertices.emplace_back(normal.y()); vertices.emplace_back(normal.z()); vertices.emplace_back(tex_coord.x()); vertices.emplace_back(tex_coord.y()); } #if ENABLE_OPENGL_ES void GLModel::Geometry::add_vertex(const Vec3f& position, const Vec3f& normal, const Vec3f& extra) { assert(format.vertex_layout == EVertexLayout::P3N3E3); vertices.emplace_back(position.x()); vertices.emplace_back(position.y()); vertices.emplace_back(position.z()); vertices.emplace_back(normal.x()); vertices.emplace_back(normal.y()); vertices.emplace_back(normal.z()); vertices.emplace_back(extra.x()); vertices.emplace_back(extra.y()); vertices.emplace_back(extra.z()); } #endif // ENABLE_OPENGL_ES void GLModel::Geometry::add_vertex(const Vec4f& position) { assert(format.vertex_layout == EVertexLayout::P4); vertices.emplace_back(position.x()); vertices.emplace_back(position.y()); vertices.emplace_back(position.z()); vertices.emplace_back(position.w()); } void GLModel::Geometry::add_index(unsigned int id) { indices.emplace_back(id); } void GLModel::Geometry::add_line(unsigned int id1, unsigned int id2) { indices.emplace_back(id1); indices.emplace_back(id2); } Vec2f GLModel::Geometry::extract_position_2(size_t id) const { const size_t p_stride = position_stride_floats(format); if (p_stride != 2) { assert(false); return { FLT_MAX, FLT_MAX }; } if (vertices_count() <= id) { assert(false); return { FLT_MAX, FLT_MAX }; } const float* start = &vertices[id * vertex_stride_floats(format) + position_offset_floats(format)]; return { *(start + 0), *(start + 1) }; } Vec3f GLModel::Geometry::extract_position_3(size_t id) const { const size_t p_stride = position_stride_floats(format); if (p_stride != 3) { assert(false); return { FLT_MAX, FLT_MAX, FLT_MAX }; } if (vertices_count() <= id) { assert(false); return { FLT_MAX, FLT_MAX, FLT_MAX }; } const float* start = &vertices[id * vertex_stride_floats(format) + position_offset_floats(format)]; return { *(start + 0), *(start + 1), *(start + 2) }; } Vec3f GLModel::Geometry::extract_normal_3(size_t id) const { const size_t n_stride = normal_stride_floats(format); if (n_stride != 3) { assert(false); return { FLT_MAX, FLT_MAX, FLT_MAX }; } if (vertices_count() <= id) { assert(false); return { FLT_MAX, FLT_MAX, FLT_MAX }; } const float* start = &vertices[id * vertex_stride_floats(format) + normal_offset_floats(format)]; return { *(start + 0), *(start + 1), *(start + 2) }; } Vec2f GLModel::Geometry::extract_tex_coord_2(size_t id) const { const size_t t_stride = tex_coord_stride_floats(format); if (t_stride != 2) { assert(false); return { FLT_MAX, FLT_MAX }; } if (vertices_count() <= id) { assert(false); return { FLT_MAX, FLT_MAX }; } const float* start = &vertices[id * vertex_stride_floats(format) + tex_coord_offset_floats(format)]; return { *(start + 0), *(start + 1) }; } void GLModel::Geometry::set_vertex(size_t id, const Vec3f& position, const Vec3f& normal) { assert(format.vertex_layout == EVertexLayout::P3N3); assert(id < vertices_count()); if (id < vertices_count()) { float* start = &vertices[id * vertex_stride_floats(format)]; *(start + 0) = position.x(); *(start + 1) = position.y(); *(start + 2) = position.z(); *(start + 3) = normal.x(); *(start + 4) = normal.y(); *(start + 5) = normal.z(); } } void GLModel::Geometry::set_index(size_t id, unsigned int index) { assert(id < indices_count()); if (id < indices_count()) indices[id] = index; } unsigned int GLModel::Geometry::extract_index(size_t id) const { if (indices_count() <= id) { assert(false); return -1; } return indices[id]; } void GLModel::Geometry::remove_vertex(size_t id) { assert(id < vertices_count()); if (id < vertices_count()) { const size_t stride = vertex_stride_floats(format); std::vector<float>::const_iterator it = vertices.begin() + id * stride; vertices.erase(it, it + stride); } } size_t GLModel::Geometry::vertex_stride_floats(const Format& format) { switch (format.vertex_layout) { case EVertexLayout::P2: { return 2; } case EVertexLayout::P2T2: { return 4; } case EVertexLayout::P3: { return 3; } case EVertexLayout::P3T2: { return 5; } case EVertexLayout::P3N3: { return 6; } case EVertexLayout::P3N3T2: { return 8; } #if ENABLE_OPENGL_ES case EVertexLayout::P3N3E3: { return 9; } #endif // ENABLE_OPENGL_ES case EVertexLayout::P4: { return 4; } default: { assert(false); return 0; } }; } size_t GLModel::Geometry::position_stride_floats(const Format& format) { switch (format.vertex_layout) { case EVertexLayout::P2: case EVertexLayout::P2T2: { return 2; } case EVertexLayout::P3: case EVertexLayout::P3T2: case EVertexLayout::P3N3: #if ENABLE_OPENGL_ES case EVertexLayout::P3N3T2: case EVertexLayout::P3N3E3: { return 3; } #else case EVertexLayout::P3N3T2: { return 3; } #endif // ENABLE_OPENGL_ES case EVertexLayout::P4: { return 4; } default: { assert(false); return 0; } }; } size_t GLModel::Geometry::position_offset_floats(const Format& format) { switch (format.vertex_layout) { case EVertexLayout::P2: case EVertexLayout::P2T2: case EVertexLayout::P3: case EVertexLayout::P3T2: case EVertexLayout::P3N3: case EVertexLayout::P3N3T2: #if ENABLE_OPENGL_ES case EVertexLayout::P3N3E3: #endif // ENABLE_OPENGL_ES case EVertexLayout::P4: { return 0; } default: { assert(false); return 0; } }; } size_t GLModel::Geometry::normal_stride_floats(const Format& format) { switch (format.vertex_layout) { case EVertexLayout::P3N3: #if ENABLE_OPENGL_ES case EVertexLayout::P3N3T2: case EVertexLayout::P3N3E3: { return 3; } #else case EVertexLayout::P3N3T2: { return 3; } #endif // ENABLE_OPENGL_ES default: { assert(false); return 0; } }; } size_t GLModel::Geometry::normal_offset_floats(const Format& format) { switch (format.vertex_layout) { case EVertexLayout::P3N3: #if ENABLE_OPENGL_ES case EVertexLayout::P3N3T2: case EVertexLayout::P3N3E3: { return 3; } #else case EVertexLayout::P3N3T2: { return 3; } #endif // ENABLE_OPENGL_ES default: { assert(false); return 0; } }; } size_t GLModel::Geometry::tex_coord_stride_floats(const Format& format) { switch (format.vertex_layout) { case EVertexLayout::P2T2: case EVertexLayout::P3T2: case EVertexLayout::P3N3T2: { return 2; } default: { assert(false); return 0; } }; } size_t GLModel::Geometry::tex_coord_offset_floats(const Format& format) { switch (format.vertex_layout) { case EVertexLayout::P2T2: { return 2; } case EVertexLayout::P3T2: { return 3; } case EVertexLayout::P3N3T2: { return 6; } default: { assert(false); return 0; } }; } #if ENABLE_OPENGL_ES size_t GLModel::Geometry::extra_stride_floats(const Format& format) { switch (format.vertex_layout) { case EVertexLayout::P3N3E3: { return 3; } default: { assert(false); return 0; } }; } size_t GLModel::Geometry::extra_offset_floats(const Format& format) { switch (format.vertex_layout) { case EVertexLayout::P3N3E3: { return 6; } default: { assert(false); return 0; } }; } #endif // ENABLE_OPENGL_ES size_t GLModel::Geometry::index_stride_bytes(const Geometry& data) { switch (data.index_type) { case EIndexType::UINT: { return sizeof(unsigned int); } case EIndexType::USHORT: { return sizeof(unsigned short); } case EIndexType::UBYTE: { return sizeof(unsigned char); } default: { assert(false); return 0; } }; } bool GLModel::Geometry::has_position(const Format& format) { switch (format.vertex_layout) { case EVertexLayout::P2: case EVertexLayout::P2T2: case EVertexLayout::P3: case EVertexLayout::P3T2: case EVertexLayout::P3N3: case EVertexLayout::P3N3T2: #if ENABLE_OPENGL_ES case EVertexLayout::P3N3E3: #endif // ENABLE_OPENGL_ES case EVertexLayout::P4: { return true; } default: { assert(false); return false; } }; } bool GLModel::Geometry::has_normal(const Format& format) { switch (format.vertex_layout) { case EVertexLayout::P2: case EVertexLayout::P2T2: case EVertexLayout::P3: case EVertexLayout::P3T2: case EVertexLayout::P4: { return false; } case EVertexLayout::P3N3: #if ENABLE_OPENGL_ES case EVertexLayout::P3N3T2: case EVertexLayout::P3N3E3: { return true; } #else case EVertexLayout::P3N3T2: { return true; } #endif // ENABLE_OPENGL_ES default: { assert(false); return false; } }; } bool GLModel::Geometry::has_tex_coord(const Format& format) { switch (format.vertex_layout) { case EVertexLayout::P2T2: case EVertexLayout::P3T2: case EVertexLayout::P3N3T2: { return true; } case EVertexLayout::P2: case EVertexLayout::P3: case EVertexLayout::P3N3: #if ENABLE_OPENGL_ES case EVertexLayout::P3N3E3: #endif // ENABLE_OPENGL_ES case EVertexLayout::P4: { return false; } default: { assert(false); return false; } }; } #if ENABLE_OPENGL_ES bool GLModel::Geometry::has_extra(const Format& format) { switch (format.vertex_layout) { case EVertexLayout::P3N3E3: { return true; } case EVertexLayout::P2: case EVertexLayout::P2T2: case EVertexLayout::P3: case EVertexLayout::P3T2: case EVertexLayout::P3N3: case EVertexLayout::P3N3T2: case EVertexLayout::P4: { return false; } default: { assert(false); return false; } }; } #endif // ENABLE_OPENGL_ES #else size_t GLModel::Geometry::vertices_count() const { size_t ret = 0; for (const Entity& entity : entities) { ret += entity.positions.size(); } return ret; } size_t GLModel::Geometry::indices_count() const { size_t ret = 0; for (const Entity& entity : entities) { ret += entity.indices.size(); } return ret; } #endif // ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_GLMODEL_STATISTICS GLModel::Statistics GLModel::s_statistics; #endif // ENABLE_GLMODEL_STATISTICS #if ENABLE_LEGACY_OPENGL_REMOVAL void GLModel::init_from(Geometry&& data) #else void GLModel::init_from(const Geometry& data) #endif // ENABLE_LEGACY_OPENGL_REMOVAL { #if ENABLE_LEGACY_OPENGL_REMOVAL if (is_initialized()) { // call reset() if you want to reuse this model assert(false); return; } if (data.vertices.empty() || data.indices.empty()) { assert(false); return; } m_render_data.geometry = std::move(data); // update bounding box for (size_t i = 0; i < vertices_count(); ++i) { const size_t position_stride = Geometry::position_stride_floats(data.format); if (position_stride == 3) m_bounding_box.merge(m_render_data.geometry.extract_position_3(i).cast<double>()); else if (position_stride == 2) { const Vec2f position = m_render_data.geometry.extract_position_2(i); m_bounding_box.merge(Vec3f(position.x(), position.y(), 0.0f).cast<double>()); } } #else if (!m_render_data.empty()) // call reset() if you want to reuse this model return; for (const Geometry::Entity& entity : data.entities) { if (entity.positions.empty() || entity.indices.empty()) continue; assert(entity.normals.empty() || entity.normals.size() == entity.positions.size()); RenderData rdata; rdata.type = entity.type; rdata.color = entity.color; // vertices/normals data std::vector<float> vertices(6 * entity.positions.size()); for (size_t i = 0; i < entity.positions.size(); ++i) { const size_t offset = i * 6; ::memcpy(static_cast<void*>(&vertices[offset]), static_cast<const void*>(entity.positions[i].data()), 3 * sizeof(float)); if (!entity.normals.empty()) ::memcpy(static_cast<void*>(&vertices[3 + offset]), static_cast<const void*>(entity.normals[i].data()), 3 * sizeof(float)); } // indices data std::vector<unsigned int> indices = entity.indices; rdata.indices_count = static_cast<unsigned int>(indices.size()); // update bounding box for (size_t i = 0; i < entity.positions.size(); ++i) { m_bounding_box.merge(entity.positions[i].cast<double>()); } send_to_gpu(rdata, vertices, indices); m_render_data.emplace_back(rdata); } #endif // ENABLE_LEGACY_OPENGL_REMOVAL } #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_SMOOTH_NORMALS void GLModel::init_from(const TriangleMesh& mesh, bool smooth_normals) { if (smooth_normals) { if (is_initialized()) { // call reset() if you want to reuse this model assert(false); return; } if (mesh.its.vertices.empty() || mesh.its.indices.empty()) { assert(false); return; } std::vector<stl_normal> normals; smooth_normals_corner(mesh, normals); const indexed_triangle_set& its = mesh.its; Geometry& data = m_render_data.geometry; data.format = { Geometry::EPrimitiveType::Triangles, Geometry::EVertexLayout::P3N3 }; data.reserve_vertices(3 * its.indices.size()); data.reserve_indices(3 * its.indices.size()); // vertices for (size_t i = 0; i < its.vertices.size(); ++i) { data.add_vertex(its.vertices[i], normals[i]); } // indices for (size_t i = 0; i < its.indices.size(); ++i) { const stl_triangle_vertex_indices& idx = its.indices[i]; data.add_triangle((unsigned int)idx(0), (unsigned int)idx(1), (unsigned int)idx(2)); } // update bounding box for (size_t i = 0; i < vertices_count(); ++i) { m_bounding_box.merge(m_render_data.geometry.extract_position_3(i).cast<double>()); } } else init_from(mesh.its); } #else void GLModel::init_from(const TriangleMesh& mesh) { init_from(mesh.its); } #endif // ENABLE_SMOOTH_NORMALS void GLModel::init_from(const indexed_triangle_set& its) #else void GLModel::init_from(const indexed_triangle_set& its, const BoundingBoxf3 &bbox) #endif // ENABLE_LEGACY_OPENGL_REMOVAL { #if ENABLE_LEGACY_OPENGL_REMOVAL if (is_initialized()) { // call reset() if you want to reuse this model assert(false); return; } if (its.vertices.empty() || its.indices.empty()){ assert(false); return; } Geometry& data = m_render_data.geometry; data.format = { Geometry::EPrimitiveType::Triangles, Geometry::EVertexLayout::P3N3 }; data.reserve_vertices(3 * its.indices.size()); data.reserve_indices(3 * its.indices.size()); // vertices + indices unsigned int vertices_counter = 0; for (uint32_t i = 0; i < its.indices.size(); ++i) { const stl_triangle_vertex_indices face = its.indices[i]; const stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] }; const stl_vertex n = face_normal_normalized(vertex); for (size_t j = 0; j < 3; ++j) { data.add_vertex(vertex[j], n); } vertices_counter += 3; data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); } // update bounding box for (size_t i = 0; i < vertices_count(); ++i) { m_bounding_box.merge(data.extract_position_3(i).cast<double>()); } #else if (!m_render_data.empty()) // call reset() if you want to reuse this model return; RenderData data; data.type = EPrimitiveType::Triangles; std::vector<float> vertices = std::vector<float>(18 * its.indices.size()); std::vector<unsigned int> indices = std::vector<unsigned int>(3 * its.indices.size()); unsigned int vertices_count = 0; for (uint32_t i = 0; i < its.indices.size(); ++i) { stl_triangle_vertex_indices face = its.indices[i]; stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] }; stl_vertex n = face_normal_normalized(vertex); for (size_t j = 0; j < 3; ++ j) { size_t offset = i * 18 + j * 6; ::memcpy(static_cast<void*>(&vertices[offset]), static_cast<const void*>(vertex[j].data()), 3 * sizeof(float)); ::memcpy(static_cast<void*>(&vertices[3 + offset]), static_cast<const void*>(n.data()), 3 * sizeof(float)); } for (size_t j = 0; j < 3; ++j) indices[i * 3 + j] = vertices_count + j; vertices_count += 3; } data.indices_count = static_cast<unsigned int>(indices.size()); m_bounding_box = bbox; send_to_gpu(data, vertices, indices); m_render_data.emplace_back(data); #endif // ENABLE_LEGACY_OPENGL_REMOVAL } #if !ENABLE_LEGACY_OPENGL_REMOVAL void GLModel::init_from(const indexed_triangle_set& its) { init_from(its, bounding_box(its)); } #endif // !ENABLE_LEGACY_OPENGL_REMOVAL void GLModel::init_from(const Polygons& polygons, float z) { #if ENABLE_LEGACY_OPENGL_REMOVAL if (is_initialized()) { // call reset() if you want to reuse this model assert(false); return; } if (polygons.empty()) { assert(false); return; } Geometry& data = m_render_data.geometry; data.format = { Geometry::EPrimitiveType::Lines, Geometry::EVertexLayout::P3 }; size_t segments_count = 0; for (const Polygon& polygon : polygons) { segments_count += polygon.points.size(); } data.reserve_vertices(2 * segments_count); data.reserve_indices(2 * segments_count); // vertices + indices unsigned int vertices_counter = 0; for (const Polygon& poly : polygons) { for (size_t i = 0; i < poly.points.size(); ++i) { const Point& p0 = poly.points[i]; const Point& p1 = (i == poly.points.size() - 1) ? poly.points.front() : poly.points[i + 1]; data.add_vertex(Vec3f(unscale<float>(p0.x()), unscale<float>(p0.y()), z)); data.add_vertex(Vec3f(unscale<float>(p1.x()), unscale<float>(p1.y()), z)); vertices_counter += 2; data.add_line(vertices_counter - 2, vertices_counter - 1); } } // update bounding box for (size_t i = 0; i < vertices_count(); ++i) { m_bounding_box.merge(data.extract_position_3(i).cast<double>()); } #else auto append_polygon = [](const Polygon& polygon, float z, GUI::GLModel::Geometry& data) { if (!polygon.empty()) { GUI::GLModel::Geometry::Entity entity; entity.type = GUI::GLModel::EPrimitiveType::LineLoop; // contour entity.positions.reserve(polygon.size() + 1); entity.indices.reserve(polygon.size() + 1); unsigned int id = 0; for (const Point& p : polygon) { Vec3f position = unscale(p.x(), p.y(), 0.0).cast<float>(); position.z() = z; entity.positions.emplace_back(position); entity.indices.emplace_back(id++); } data.entities.emplace_back(entity); } }; Geometry init_data; for (const Polygon& polygon : polygons) { append_polygon(polygon, z, init_data); } init_from(init_data); #endif // ENABLE_LEGACY_OPENGL_REMOVAL } bool GLModel::init_from_file(const std::string& filename) { if (!boost::filesystem::exists(filename)) return false; if (!boost::algorithm::iends_with(filename, ".stl")) return false; Model model; try { model = Model::read_from_file(filename); } catch (std::exception&) { return false; } #if ENABLE_LEGACY_OPENGL_REMOVAL init_from(model.mesh()); #else const TriangleMesh& mesh = model.mesh(); init_from(mesh.its, mesh.bounding_box()); #endif // ENABLE_LEGACY_OPENGL_REMOVAL m_filename = filename; return true; } #if !ENABLE_LEGACY_OPENGL_REMOVAL void GLModel::set_color(int entity_id, const ColorRGBA& color) { for (size_t i = 0; i < m_render_data.size(); ++i) { if (entity_id == -1 || static_cast<int>(i) == entity_id) m_render_data[i].color = color; } } ColorRGBA GLModel::get_color(size_t entity_id) const { if (entity_id < 0 || entity_id >= m_render_data.size()) return ColorRGBA{}; return m_render_data[entity_id].color; } #endif // !ENABLE_LEGACY_OPENGL_REMOVAL void GLModel::reset() { #if ENABLE_LEGACY_OPENGL_REMOVAL // release gpu memory if (m_render_data.ibo_id > 0) { glsafe(::glDeleteBuffers(1, &m_render_data.ibo_id)); m_render_data.ibo_id = 0; #if ENABLE_GLMODEL_STATISTICS s_statistics.gpu_memory.indices.current -= indices_size_bytes(); #endif // ENABLE_GLMODEL_STATISTICS } if (m_render_data.vbo_id > 0) { glsafe(::glDeleteBuffers(1, &m_render_data.vbo_id)); m_render_data.vbo_id = 0; #if ENABLE_GLMODEL_STATISTICS s_statistics.gpu_memory.vertices.current -= vertices_size_bytes(); #endif // ENABLE_GLMODEL_STATISTICS } #if ENABLE_GL_CORE_PROFILE if (m_render_data.vao_id > 0) { glsafe(::glDeleteVertexArrays(1, &m_render_data.vao_id)); m_render_data.vao_id = 0; } #endif // ENABLE_GL_CORE_PROFILE m_render_data.vertices_count = 0; m_render_data.indices_count = 0; m_render_data.geometry.vertices = std::vector<float>(); m_render_data.geometry.indices = std::vector<unsigned int>(); #else for (RenderData& data : m_render_data) { // release gpu memory if (data.ibo_id > 0) glsafe(::glDeleteBuffers(1, &data.ibo_id)); if (data.vbo_id > 0) glsafe(::glDeleteBuffers(1, &data.vbo_id)); } m_render_data.clear(); #endif // ENABLE_LEGACY_OPENGL_REMOVAL m_bounding_box = BoundingBoxf3(); m_filename = std::string(); } #if ENABLE_LEGACY_OPENGL_REMOVAL static GLenum get_primitive_mode(const GLModel::Geometry::Format& format) { switch (format.type) { case GLModel::Geometry::EPrimitiveType::Points: { return GL_POINTS; } default: case GLModel::Geometry::EPrimitiveType::Triangles: { return GL_TRIANGLES; } case GLModel::Geometry::EPrimitiveType::TriangleStrip: { return GL_TRIANGLE_STRIP; } case GLModel::Geometry::EPrimitiveType::TriangleFan: { return GL_TRIANGLE_FAN; } case GLModel::Geometry::EPrimitiveType::Lines: { return GL_LINES; } case GLModel::Geometry::EPrimitiveType::LineStrip: { return GL_LINE_STRIP; } case GLModel::Geometry::EPrimitiveType::LineLoop: { return GL_LINE_LOOP; } } } static GLenum get_index_type(const GLModel::Geometry& data) { switch (data.index_type) { default: case GLModel::Geometry::EIndexType::UINT: { return GL_UNSIGNED_INT; } case GLModel::Geometry::EIndexType::USHORT: { return GL_UNSIGNED_SHORT; } case GLModel::Geometry::EIndexType::UBYTE: { return GL_UNSIGNED_BYTE; } } } void GLModel::render() #else void GLModel::render() const #endif // ENABLE_LEGACY_OPENGL_REMOVAL { #if ENABLE_LEGACY_OPENGL_REMOVAL render(std::make_pair<size_t, size_t>(0, indices_count())); #else GLShaderProgram* shader = wxGetApp().get_current_shader(); for (const RenderData& data : m_render_data) { if (data.vbo_id == 0 || data.ibo_id == 0) continue; GLenum mode; switch (data.type) { default: case EPrimitiveType::Triangles: { mode = GL_TRIANGLES; break; } case EPrimitiveType::Lines: { mode = GL_LINES; break; } case EPrimitiveType::LineStrip: { mode = GL_LINE_STRIP; break; } case EPrimitiveType::LineLoop: { mode = GL_LINE_LOOP; break; } } glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id)); glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)0)); glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)))); glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); if (shader != nullptr) shader->set_uniform("uniform_color", data.color); else glsafe(::glColor4fv(data.color.data())); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id)); glsafe(::glDrawElements(mode, static_cast<GLsizei>(data.indices_count), GL_UNSIGNED_INT, (const void*)0)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } #endif // ENABLE_LEGACY_OPENGL_REMOVAL } #if ENABLE_LEGACY_OPENGL_REMOVAL void GLModel::render(const std::pair<size_t, size_t>& range) { if (m_render_disabled) return; if (range.second == range.first) return; GLShaderProgram* shader = wxGetApp().get_current_shader(); if (shader == nullptr) return; // sends data to gpu if not done yet if (m_render_data.vbo_id == 0 || m_render_data.ibo_id == 0) { if (m_render_data.geometry.vertices_count() > 0 && m_render_data.geometry.indices_count() > 0 && !send_to_gpu()) return; } const Geometry& data = m_render_data.geometry; const GLenum mode = get_primitive_mode(data.format); const GLenum index_type = get_index_type(data); const size_t vertex_stride_bytes = Geometry::vertex_stride_bytes(data.format); const bool position = Geometry::has_position(data.format); const bool normal = Geometry::has_normal(data.format); const bool tex_coord = Geometry::has_tex_coord(data.format); #if ENABLE_OPENGL_ES const bool extra = Geometry::has_extra(data.format); #endif // ENABLE_OPENGL_ES #if ENABLE_GL_CORE_PROFILE if (OpenGLManager::get_gl_info().is_version_greater_or_equal_to(3, 0)) glsafe(::glBindVertexArray(m_render_data.vao_id)); // the following binding is needed to set the vertex attributes #endif // ENABLE_GL_CORE_PROFILE glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_render_data.vbo_id)); int position_id = -1; int normal_id = -1; int tex_coord_id = -1; #if ENABLE_OPENGL_ES int extra_id = -1; #endif // ENABLE_OPENGL_ES if (position) { position_id = shader->get_attrib_location("v_position"); if (position_id != -1) { glsafe(::glVertexAttribPointer(position_id, Geometry::position_stride_floats(data.format), GL_FLOAT, GL_FALSE, vertex_stride_bytes, (const void*)Geometry::position_offset_bytes(data.format))); glsafe(::glEnableVertexAttribArray(position_id)); } } if (normal) { normal_id = shader->get_attrib_location("v_normal"); if (normal_id != -1) { glsafe(::glVertexAttribPointer(normal_id, Geometry::normal_stride_floats(data.format), GL_FLOAT, GL_FALSE, vertex_stride_bytes, (const void*)Geometry::normal_offset_bytes(data.format))); glsafe(::glEnableVertexAttribArray(normal_id)); } } if (tex_coord) { tex_coord_id = shader->get_attrib_location("v_tex_coord"); if (tex_coord_id != -1) { glsafe(::glVertexAttribPointer(tex_coord_id, Geometry::tex_coord_stride_floats(data.format), GL_FLOAT, GL_FALSE, vertex_stride_bytes, (const void*)Geometry::tex_coord_offset_bytes(data.format))); glsafe(::glEnableVertexAttribArray(tex_coord_id)); } } #if ENABLE_OPENGL_ES if (extra) { extra_id = shader->get_attrib_location("v_extra"); if (extra_id != -1) { glsafe(::glVertexAttribPointer(extra_id, Geometry::extra_stride_floats(data.format), GL_FLOAT, GL_FALSE, vertex_stride_bytes, (const void*)Geometry::extra_offset_bytes(data.format))); glsafe(::glEnableVertexAttribArray(extra_id)); } } #endif // ENABLE_OPENGL_ES shader->set_uniform("uniform_color", data.color); #if ENABLE_GL_CORE_PROFILE if (!OpenGLManager::get_gl_info().is_version_greater_or_equal_to(3, 0)) #endif // ENABLE_GL_CORE_PROFILE glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_render_data.ibo_id)); glsafe(::glDrawElements(mode, range.second - range.first, index_type, (const void*)(range.first * Geometry::index_stride_bytes(data)))); #if !ENABLE_GL_CORE_PROFILE glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); #endif // !ENABLE_GL_CORE_PROFILE #if ENABLE_OPENGL_ES if (extra_id != -1) glsafe(::glDisableVertexAttribArray(extra_id)); #endif // ENABLE_OPENGL_ES if (tex_coord_id != -1) glsafe(::glDisableVertexAttribArray(tex_coord_id)); if (normal_id != -1) glsafe(::glDisableVertexAttribArray(normal_id)); if (position_id != -1) glsafe(::glDisableVertexAttribArray(position_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); #if ENABLE_GL_CORE_PROFILE if (OpenGLManager::get_gl_info().is_version_greater_or_equal_to(3, 0)) glsafe(::glBindVertexArray(0)); #endif // ENABLE_GL_CORE_PROFILE #if ENABLE_GLMODEL_STATISTICS ++s_statistics.render_calls; #endif // ENABLE_GLMODEL_STATISTICS } #endif // ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_LEGACY_OPENGL_REMOVAL void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count) #else void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count) const #endif // ENABLE_LEGACY_OPENGL_REMOVAL { if (instances_vbo == 0 || instances_count == 0) return; GLShaderProgram* shader = wxGetApp().get_current_shader(); #if ENABLE_LEGACY_OPENGL_REMOVAL if (shader == nullptr || !boost::algorithm::iends_with(shader->get_name(), "_instanced")) return; // vertex attributes const GLint position_id = shader->get_attrib_location("v_position"); const GLint normal_id = shader->get_attrib_location("v_normal"); if (position_id == -1 || normal_id == -1) return; // instance attributes const GLint offset_id = shader->get_attrib_location("i_offset"); const GLint scales_id = shader->get_attrib_location("i_scales"); if (offset_id == -1 || scales_id == -1) return; if (m_render_data.vbo_id == 0 || m_render_data.ibo_id == 0) { if (!send_to_gpu()) return; } #else 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); #endif // ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_GL_CORE_PROFILE if (OpenGLManager::get_gl_info().is_version_greater_or_equal_to(3, 0)) glsafe(::glBindVertexArray(m_render_data.vao_id)); #endif // ENABLE_GL_CORE_PROFILE glsafe(::glBindBuffer(GL_ARRAY_BUFFER, instances_vbo)); #if ENABLE_LEGACY_OPENGL_REMOVAL const size_t instance_stride = 5 * sizeof(float); glsafe(::glVertexAttribPointer(offset_id, 3, GL_FLOAT, GL_FALSE, instance_stride, (const void*)0)); glsafe(::glEnableVertexAttribArray(offset_id)); glsafe(::glVertexAttribDivisor(offset_id, 1)); glsafe(::glVertexAttribPointer(scales_id, 2, GL_FLOAT, GL_FALSE, instance_stride, (const void*)(3 * sizeof(float)))); glsafe(::glEnableVertexAttribArray(scales_id)); glsafe(::glVertexAttribDivisor(scales_id, 1)); #else 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)); } #endif // ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_LEGACY_OPENGL_REMOVAL const Geometry& data = m_render_data.geometry; const GLenum mode = get_primitive_mode(data.format); const GLenum index_type = get_index_type(data); const size_t vertex_stride_bytes = Geometry::vertex_stride_bytes(data.format); const bool position = Geometry::has_position(data.format); const bool normal = Geometry::has_normal(data.format); #if ENABLE_GL_CORE_PROFILE // the following binding is needed to set the vertex attributes #endif // ENABLE_GL_CORE_PROFILE glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_render_data.vbo_id)); if (position) { glsafe(::glVertexAttribPointer(position_id, Geometry::position_stride_floats(data.format), GL_FLOAT, GL_FALSE, vertex_stride_bytes, (const void*)Geometry::position_offset_bytes(data.format))); glsafe(::glEnableVertexAttribArray(position_id)); } if (normal) { glsafe(::glVertexAttribPointer(normal_id, Geometry::normal_stride_floats(data.format), GL_FLOAT, GL_FALSE, vertex_stride_bytes, (const void*)Geometry::normal_offset_bytes(data.format))); glsafe(::glEnableVertexAttribArray(normal_id)); } shader->set_uniform("uniform_color", data.color); #if !ENABLE_GL_CORE_PROFILE glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_render_data.ibo_id)); #endif // !ENABLE_GL_CORE_PROFILE glsafe(::glDrawElementsInstanced(mode, indices_count(), index_type, (const void*)0, instances_count)); #if !ENABLE_GL_CORE_PROFILE glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); #endif // !ENABLE_GL_CORE_PROFILE if (normal) glsafe(::glDisableVertexAttribArray(normal_id)); if (position) glsafe(::glDisableVertexAttribArray(position_id)); glsafe(::glDisableVertexAttribArray(scales_id)); glsafe(::glDisableVertexAttribArray(offset_id)); #else for (const RenderData& data : m_render_data) { if (data.vbo_id == 0 || data.ibo_id == 0) continue; GLenum mode; switch (data.type) { default: case EPrimitiveType::Triangles: { mode = GL_TRIANGLES; break; } case EPrimitiveType::Lines: { mode = GL_LINES; break; } case EPrimitiveType::LineStrip: { mode = GL_LINE_STRIP; break; } case EPrimitiveType::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<GLsizei>(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)); #endif // ENABLE_LEGACY_OPENGL_REMOVAL glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); #if ENABLE_GL_CORE_PROFILE if (OpenGLManager::get_gl_info().is_version_greater_or_equal_to(3, 0)) glsafe(::glBindVertexArray(0)); #endif // ENABLE_GL_CORE_PROFILE #if ENABLE_GLMODEL_STATISTICS ++s_statistics.render_instanced_calls; #endif // ENABLE_GLMODEL_STATISTICS } #if ENABLE_LEGACY_OPENGL_REMOVAL bool GLModel::send_to_gpu() { if (m_render_data.vbo_id > 0 || m_render_data.ibo_id > 0) { assert(false); return false; } Geometry& data = m_render_data.geometry; if (data.vertices.empty() || data.indices.empty()) { assert(false); return false; } #if ENABLE_GL_CORE_PROFILE if (OpenGLManager::get_gl_info().is_version_greater_or_equal_to(3, 0)) { glsafe(::glGenVertexArrays(1, &m_render_data.vao_id)); glsafe(::glBindVertexArray(m_render_data.vao_id)); } #endif // ENABLE_GL_CORE_PROFILE // vertices glsafe(::glGenBuffers(1, &m_render_data.vbo_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_render_data.vbo_id)); glsafe(::glBufferData(GL_ARRAY_BUFFER, data.vertices_size_bytes(), data.vertices.data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); m_render_data.vertices_count = vertices_count(); #if ENABLE_GLMODEL_STATISTICS s_statistics.gpu_memory.vertices.current += data.vertices_size_bytes(); s_statistics.gpu_memory.vertices.max = std::max(s_statistics.gpu_memory.vertices.current, s_statistics.gpu_memory.vertices.max); #endif // ENABLE_GLMODEL_STATISTICS data.vertices = std::vector<float>(); // indices const size_t indices_count = data.indices.size(); glsafe(::glGenBuffers(1, &m_render_data.ibo_id)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_render_data.ibo_id)); if (m_render_data.vertices_count <= 256) { // convert indices to unsigned char to save gpu memory std::vector<unsigned char> reduced_indices(indices_count); for (size_t i = 0; i < indices_count; ++i) { reduced_indices[i] = (unsigned char)data.indices[i]; } data.index_type = Geometry::EIndexType::UBYTE; glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_count * sizeof(unsigned char), reduced_indices.data(), GL_STATIC_DRAW)); } else if (m_render_data.vertices_count <= 65536) { // convert indices to unsigned short to save gpu memory std::vector<unsigned short> reduced_indices(indices_count); for (size_t i = 0; i < data.indices.size(); ++i) { reduced_indices[i] = (unsigned short)data.indices[i]; } data.index_type = Geometry::EIndexType::USHORT; glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_count * sizeof(unsigned short), reduced_indices.data(), GL_STATIC_DRAW)); } else { data.index_type = Geometry::EIndexType::UINT; glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.indices_size_bytes(), data.indices.data(), GL_STATIC_DRAW)); } #if ENABLE_GL_CORE_PROFILE if (!OpenGLManager::get_gl_info().is_version_greater_or_equal_to(3, 0)) #endif // ENABLE_GL_CORE_PROFILE glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); m_render_data.indices_count = indices_count; #if ENABLE_GLMODEL_STATISTICS s_statistics.gpu_memory.indices.current += data.indices_size_bytes(); s_statistics.gpu_memory.indices.max = std::max(s_statistics.gpu_memory.indices.current, s_statistics.gpu_memory.indices.max); #endif // ENABLE_GLMODEL_STATISTICS data.indices = std::vector<unsigned int>(); #if ENABLE_GL_CORE_PROFILE if (OpenGLManager::get_gl_info().is_version_greater_or_equal_to(3, 0)) glsafe(::glBindVertexArray(0)); #endif // ENABLE_GL_CORE_PROFILE return true; } #if ENABLE_GLMODEL_STATISTICS void GLModel::render_statistics() { static const float offset = 175.0f; ImGuiWrapper& imgui = *wxGetApp().imgui(); auto add_memory = [&imgui](const std::string& label, int64_t memory) { auto format_string = [memory](const std::string& units, float value) { return std::to_string(memory) + " bytes (" + Slic3r::float_to_string_decimal_point(float(memory) * value, 3) + " " + units + ")"; }; static const float kb = 1024.0f; static const float inv_kb = 1.0f / kb; static const float mb = 1024.0f * kb; static const float inv_mb = 1.0f / mb; static const float gb = 1024.0f * mb; static const float inv_gb = 1.0f / gb; imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); ImGui::SameLine(offset); if (static_cast<float>(memory) < mb) imgui.text(format_string("KB", inv_kb)); else if (static_cast<float>(memory) < gb) imgui.text(format_string("MB", inv_mb)); else imgui.text(format_string("GB", inv_gb)); }; auto add_counter = [&imgui](const std::string& label, int64_t counter) { imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); ImGui::SameLine(offset); imgui.text(std::to_string(counter)); }; imgui.set_next_window_pos(0.5f * wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_width(), 0.0f, ImGuiCond_Once, 0.5f, 0.0f); ImGui::SetNextWindowSizeConstraints({ 300.0f, 100.0f }, { 600.0f, 900.0f }); imgui.begin(std::string("GLModel Statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); add_counter(std::string("Render calls:"), s_statistics.render_calls); add_counter(std::string("Render instanced calls:"), s_statistics.render_instanced_calls); if (ImGui::CollapsingHeader("GPU memory")) { ImGui::Indent(10.0f); if (ImGui::CollapsingHeader("Vertices")) { add_memory(std::string("Current:"), s_statistics.gpu_memory.vertices.current); add_memory(std::string("Max:"), s_statistics.gpu_memory.vertices.max); } if (ImGui::CollapsingHeader("Indices")) { add_memory(std::string("Current:"), s_statistics.gpu_memory.indices.current); add_memory(std::string("Max:"), s_statistics.gpu_memory.indices.max); } ImGui::Unindent(10.0f); } imgui.end(); } #endif // ENABLE_GLMODEL_STATISTICS #else void GLModel::send_to_gpu(RenderData& data, const std::vector<float>& vertices, const std::vector<unsigned int>& indices) { assert(data.vbo_id == 0); assert(data.ibo_id == 0); // vertex data -> send to gpu glsafe(::glGenBuffers(1, &data.vbo_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id)); glsafe(::glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); // indices data -> send to gpu glsafe(::glGenBuffers(1, &data.ibo_id)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id)); glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); } #endif // ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_LEGACY_OPENGL_REMOVAL template<typename Fn> inline bool all_vertices_inside(const GLModel::Geometry& geometry, Fn fn) { const size_t position_stride_floats = geometry.position_stride_floats(geometry.format); const size_t position_offset_floats = geometry.position_offset_floats(geometry.format); assert(position_stride_floats == 3); if (geometry.vertices.empty() || position_stride_floats != 3) return false; for (auto it = geometry.vertices.begin(); it != geometry.vertices.end(); ) { it += position_offset_floats; if (!fn({ *it, *(it + 1), *(it + 2) })) return false; it += (geometry.vertex_stride_floats(geometry.format) - position_offset_floats - position_stride_floats); } return true; } bool contains(const BuildVolume& volume, const GLModel& model, bool ignore_bottom) { static constexpr const double epsilon = BuildVolume::BedEpsilon; switch (volume.type()) { case BuildVolume::Type::Rectangle: { BoundingBox3Base<Vec3d> build_volume = volume.bounding_volume().inflated(epsilon); if (volume.max_print_height() == 0.0) build_volume.max.z() = std::numeric_limits<double>::max(); if (ignore_bottom) build_volume.min.z() = -std::numeric_limits<double>::max(); const BoundingBoxf3& model_box = model.get_bounding_box(); return build_volume.contains(model_box.min) && build_volume.contains(model_box.max); } case BuildVolume::Type::Circle: { const Geometry::Circled& circle = volume.circle(); const Vec2f c = unscaled<float>(circle.center); const float r = unscaled<double>(circle.radius) + float(epsilon); const float r2 = sqr(r); return volume.max_print_height() == 0.0 ? all_vertices_inside(model.get_geometry(), [c, r2](const Vec3f& p) { return (to_2d(p) - c).squaredNorm() <= r2; }) : all_vertices_inside(model.get_geometry(), [c, r2, z = volume.max_print_height() + epsilon](const Vec3f& p) { return (to_2d(p) - c).squaredNorm() <= r2 && p.z() <= z; }); } case BuildVolume::Type::Convex: //FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently. case BuildVolume::Type::Custom: return volume.max_print_height() == 0.0 ? all_vertices_inside(model.get_geometry(), [&volume](const Vec3f& p) { return Geometry::inside_convex_polygon(volume.top_bottom_convex_hull_decomposition_bed(), to_2d(p).cast<double>()); }) : all_vertices_inside(model.get_geometry(), [&volume, z = volume.max_print_height() + epsilon](const Vec3f& p) { return Geometry::inside_convex_polygon(volume.top_bottom_convex_hull_decomposition_bed(), to_2d(p).cast<double>()) && p.z() <= z; }); default: return true; } } #endif // ENABLE_LEGACY_OPENGL_REMOVAL GLModel::Geometry stilized_arrow(unsigned int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height) { #if !ENABLE_LEGACY_OPENGL_REMOVAL auto append_vertex = [](GLModel::Geometry::Entity& entity, const Vec3f& position, const Vec3f& normal) { entity.positions.emplace_back(position); entity.normals.emplace_back(normal); }; auto append_indices = [](GLModel::Geometry::Entity& entity, unsigned int v1, unsigned int v2, unsigned int v3) { entity.indices.emplace_back(v1); entity.indices.emplace_back(v2); entity.indices.emplace_back(v3); }; #endif // !ENABLE_LEGACY_OPENGL_REMOVAL resolution = std::max<unsigned int>(4, resolution); GLModel::Geometry data; #if ENABLE_LEGACY_OPENGL_REMOVAL data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; data.reserve_vertices(6 * resolution + 2); data.reserve_indices(6 * resolution * 3); #else GLModel::Geometry::Entity entity; entity.type = GLModel::EPrimitiveType::Triangles; #endif // ENABLE_LEGACY_OPENGL_REMOVAL const float angle_step = 2.0f * float(PI) / float(resolution); std::vector<float> cosines(resolution); std::vector<float> sines(resolution); for (unsigned int i = 0; i < resolution; ++i) { const float angle = angle_step * float(i); cosines[i] = ::cos(angle); sines[i] = -::sin(angle); } const float total_height = tip_height + stem_height; // tip vertices/normals #if ENABLE_LEGACY_OPENGL_REMOVAL data.add_vertex(Vec3f(0.0f, 0.0f, total_height), (Vec3f)Vec3f::UnitZ()); for (unsigned int i = 0; i < resolution; ++i) { data.add_vertex(Vec3f(tip_radius * sines[i], tip_radius * cosines[i], stem_height), Vec3f(sines[i], cosines[i], 0.0f)); } // tip triangles for (unsigned int i = 0; i < resolution; ++i) { const unsigned int v3 = (i < resolution - 1) ? i + 2 : 1; data.add_triangle(0, i + 1, v3); } // tip cap outer perimeter vertices for (unsigned int i = 0; i < resolution; ++i) { data.add_vertex(Vec3f(tip_radius * sines[i], tip_radius * cosines[i], stem_height), (Vec3f)(-Vec3f::UnitZ())); } // tip cap inner perimeter vertices for (unsigned int i = 0; i < resolution; ++i) { data.add_vertex(Vec3f(stem_radius * sines[i], stem_radius * cosines[i], stem_height), (Vec3f)(-Vec3f::UnitZ())); } // tip cap triangles for (unsigned int i = 0; i < resolution; ++i) { const unsigned int v2 = (i < resolution - 1) ? i + resolution + 2 : resolution + 1; const unsigned int v3 = (i < resolution - 1) ? i + 2 * resolution + 2 : 2 * resolution + 1; data.add_triangle(i + resolution + 1, v3, v2); data.add_triangle(i + resolution + 1, i + 2 * resolution + 1, v3); } // stem bottom vertices for (unsigned int i = 0; i < resolution; ++i) { data.add_vertex(Vec3f(stem_radius * sines[i], stem_radius * cosines[i], stem_height), Vec3f(sines[i], cosines[i], 0.0f)); } // stem top vertices for (unsigned int i = 0; i < resolution; ++i) { data.add_vertex(Vec3f(stem_radius * sines[i], stem_radius * cosines[i], 0.0f), Vec3f(sines[i], cosines[i], 0.0f)); } // stem triangles for (unsigned int i = 0; i < resolution; ++i) { const unsigned int v2 = (i < resolution - 1) ? i + 3 * resolution + 2 : 3 * resolution + 1; const unsigned int v3 = (i < resolution - 1) ? i + 4 * resolution + 2 : 4 * resolution + 1; data.add_triangle(i + 3 * resolution + 1, v3, v2); data.add_triangle(i + 3 * resolution + 1, i + 4 * resolution + 1, v3); } // stem cap vertices data.add_vertex((Vec3f)Vec3f::Zero(), (Vec3f)(-Vec3f::UnitZ())); for (unsigned int i = 0; i < resolution; ++i) { data.add_vertex(Vec3f(stem_radius * sines[i], stem_radius * cosines[i], 0.0f), (Vec3f)(-Vec3f::UnitZ())); } // stem cap triangles for (unsigned int i = 0; i < resolution; ++i) { const unsigned int v3 = (i < resolution - 1) ? i + 5 * resolution + 3 : 5 * resolution + 2; data.add_triangle(5 * resolution + 1, v3, i + 5 * resolution + 2); } #else append_vertex(entity, { 0.0f, 0.0f, total_height }, Vec3f::UnitZ()); for (unsigned int i = 0; i < resolution; ++i) { append_vertex(entity, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); } // tip triangles for (unsigned int i = 0; i < resolution; ++i) { const int v3 = (i < resolution - 1) ? i + 2 : 1; append_indices(entity, 0, i + 1, v3); } // tip cap outer perimeter vertices for (unsigned int i = 0; i < resolution; ++i) { append_vertex(entity, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); } // tip cap inner perimeter vertices for (unsigned int i = 0; i < resolution; ++i) { append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); } // tip cap triangles for (unsigned int i = 0; i < resolution; ++i) { const unsigned int v2 = (i < resolution - 1) ? i + resolution + 2 : resolution + 1; const unsigned int v3 = (i < resolution - 1) ? i + 2 * resolution + 2 : 2 * resolution + 1; append_indices(entity, i + resolution + 1, v3, v2); append_indices(entity, i + resolution + 1, i + 2 * resolution + 1, v3); } // stem bottom vertices for (unsigned int i = 0; i < resolution; ++i) { append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); } // stem top vertices for (unsigned int i = 0; i < resolution; ++i) { append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, { sines[i], cosines[i], 0.0f }); } // stem triangles for (unsigned int i = 0; i < resolution; ++i) { const int unsigned v2 = (i < resolution - 1) ? i + 3 * resolution + 2 : 3 * resolution + 1; const int unsigned v3 = (i < resolution - 1) ? i + 4 * resolution + 2 : 4 * resolution + 1; append_indices(entity, i + 3 * resolution + 1, v3, v2); append_indices(entity, i + 3 * resolution + 1, i + 4 * resolution + 1, v3); } // stem cap vertices append_vertex(entity, Vec3f::Zero(), -Vec3f::UnitZ()); for (unsigned int i = 0; i < resolution; ++i) { append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, -Vec3f::UnitZ()); } // stem cap triangles for (unsigned int i = 0; i < resolution; ++i) { const unsigned int v3 = (i < resolution - 1) ? i + 5 * resolution + 3 : 5 * resolution + 2; append_indices(entity, 5 * resolution + 1, v3, i + 5 * resolution + 2); } data.entities.emplace_back(entity); #endif // ENABLE_LEGACY_OPENGL_REMOVAL return data; } GLModel::Geometry circular_arrow(unsigned int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness) { #if !ENABLE_LEGACY_OPENGL_REMOVAL auto append_vertex = [](GLModel::Geometry::Entity& entity, const Vec3f& position, const Vec3f& normal) { entity.positions.emplace_back(position); entity.normals.emplace_back(normal); }; auto append_indices = [](GLModel::Geometry::Entity& entity, unsigned int v1, unsigned int v2, unsigned int v3) { entity.indices.emplace_back(v1); entity.indices.emplace_back(v2); entity.indices.emplace_back(v3); }; #endif // !ENABLE_LEGACY_OPENGL_REMOVAL resolution = std::max<unsigned int>(2, resolution); GLModel::Geometry data; #if ENABLE_LEGACY_OPENGL_REMOVAL data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; data.reserve_vertices(8 * (resolution + 1) + 30); data.reserve_indices((8 * resolution + 16) * 3); #else GLModel::Geometry::Entity entity; entity.type = GLModel::EPrimitiveType::Triangles; #endif // ENABLE_LEGACY_OPENGL_REMOVAL const float half_thickness = 0.5f * thickness; const float half_stem_width = 0.5f * stem_width; const float half_tip_width = 0.5f * tip_width; const float outer_radius = radius + half_stem_width; const float inner_radius = radius - half_stem_width; const float step_angle = 0.5f * float(PI) / float(resolution); #if ENABLE_LEGACY_OPENGL_REMOVAL // tip // top face vertices data.add_vertex(Vec3f(0.0f, outer_radius, half_thickness), (Vec3f)Vec3f::UnitZ()); data.add_vertex(Vec3f(0.0f, radius + half_tip_width, half_thickness), (Vec3f)Vec3f::UnitZ()); data.add_vertex(Vec3f(-tip_height, radius, half_thickness), (Vec3f)Vec3f::UnitZ()); data.add_vertex(Vec3f(0.0f, radius - half_tip_width, half_thickness), (Vec3f)Vec3f::UnitZ()); data.add_vertex(Vec3f(0.0f, inner_radius, half_thickness), (Vec3f)Vec3f::UnitZ()); // top face triangles data.add_triangle(0, 1, 2); data.add_triangle(0, 2, 4); data.add_triangle(4, 2, 3); // bottom face vertices data.add_vertex(Vec3f(0.0f, outer_radius, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); data.add_vertex(Vec3f(0.0f, radius + half_tip_width, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); data.add_vertex(Vec3f(-tip_height, radius, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); data.add_vertex(Vec3f(0.0f, radius - half_tip_width, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); data.add_vertex(Vec3f(0.0f, inner_radius, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); // bottom face triangles data.add_triangle(5, 7, 6); data.add_triangle(5, 9, 7); data.add_triangle(9, 8, 7); // side faces vertices data.add_vertex(Vec3f(0.0f, outer_radius, -half_thickness), (Vec3f)Vec3f::UnitX()); data.add_vertex(Vec3f(0.0f, radius + half_tip_width, -half_thickness), (Vec3f)Vec3f::UnitX()); data.add_vertex(Vec3f(0.0f, outer_radius, half_thickness), (Vec3f)Vec3f::UnitX()); data.add_vertex(Vec3f(0.0f, radius + half_tip_width, half_thickness), (Vec3f)Vec3f::UnitX()); Vec3f normal(-half_tip_width, tip_height, 0.0f); normal.normalize(); data.add_vertex(Vec3f(0.0f, radius + half_tip_width, -half_thickness), normal); data.add_vertex(Vec3f(-tip_height, radius, -half_thickness), normal); data.add_vertex(Vec3f(0.0f, radius + half_tip_width, half_thickness), normal); data.add_vertex(Vec3f(-tip_height, radius, half_thickness), normal); normal = { -half_tip_width, -tip_height, 0.0f }; normal.normalize(); data.add_vertex(Vec3f(-tip_height, radius, -half_thickness), normal); data.add_vertex(Vec3f(0.0f, radius - half_tip_width, -half_thickness), normal); data.add_vertex(Vec3f(-tip_height, radius, half_thickness), normal); data.add_vertex(Vec3f(0.0f, radius - half_tip_width, half_thickness), normal); data.add_vertex(Vec3f(0.0f, radius - half_tip_width, -half_thickness), (Vec3f)Vec3f::UnitX()); data.add_vertex(Vec3f(0.0f, inner_radius, -half_thickness), (Vec3f)Vec3f::UnitX()); data.add_vertex(Vec3f(0.0f, radius - half_tip_width, half_thickness), (Vec3f)Vec3f::UnitX()); data.add_vertex(Vec3f(0.0f, inner_radius, half_thickness), (Vec3f)Vec3f::UnitX()); // side face triangles for (unsigned int i = 0; i < 4; ++i) { const unsigned int ii = i * 4; data.add_triangle(10 + ii, 11 + ii, 13 + ii); data.add_triangle(10 + ii, 13 + ii, 12 + ii); } // stem // top face vertices for (unsigned int i = 0; i <= resolution; ++i) { const float angle = float(i) * step_angle; data.add_vertex(Vec3f(inner_radius * ::sin(angle), inner_radius * ::cos(angle), half_thickness), (Vec3f)Vec3f::UnitZ()); } for (unsigned int i = 0; i <= resolution; ++i) { const float angle = float(i) * step_angle; data.add_vertex(Vec3f(outer_radius * ::sin(angle), outer_radius * ::cos(angle), half_thickness), (Vec3f)Vec3f::UnitZ()); } // top face triangles for (unsigned int i = 0; i < resolution; ++i) { data.add_triangle(26 + i, 27 + i, 27 + resolution + i); data.add_triangle(27 + i, 28 + resolution + i, 27 + resolution + i); } // bottom face vertices for (unsigned int i = 0; i <= resolution; ++i) { const float angle = float(i) * step_angle; data.add_vertex(Vec3f(inner_radius * ::sin(angle), inner_radius * ::cos(angle), -half_thickness), (Vec3f)(-Vec3f::UnitZ())); } for (unsigned int i = 0; i <= resolution; ++i) { const float angle = float(i) * step_angle; data.add_vertex(Vec3f(outer_radius * ::sin(angle), outer_radius * ::cos(angle), -half_thickness), (Vec3f)(-Vec3f::UnitZ())); } // bottom face triangles for (unsigned int i = 0; i < resolution; ++i) { data.add_triangle(28 + 2 * resolution + i, 29 + 3 * resolution + i, 29 + 2 * resolution + i); data.add_triangle(29 + 2 * resolution + i, 29 + 3 * resolution + i, 30 + 3 * resolution + i); } // side faces vertices and triangles for (unsigned int i = 0; i <= resolution; ++i) { const float angle = float(i) * step_angle; const float c = ::cos(angle); const float s = ::sin(angle); data.add_vertex(Vec3f(inner_radius * s, inner_radius * c, -half_thickness), Vec3f(-s, -c, 0.0f)); } for (unsigned int i = 0; i <= resolution; ++i) { const float angle = float(i) * step_angle; const float c = ::cos(angle); const float s = ::sin(angle); data.add_vertex(Vec3f(inner_radius * s, inner_radius * c, half_thickness), Vec3f(-s, -c, 0.0f)); } unsigned int first_id = 26 + 4 * (resolution + 1); for (unsigned int i = 0; i < resolution; ++i) { const unsigned int ii = first_id + i; data.add_triangle(ii, ii + 1, ii + resolution + 2); data.add_triangle(ii, ii + resolution + 2, ii + resolution + 1); } data.add_vertex(Vec3f(inner_radius, 0.0f, -half_thickness), (Vec3f)(-Vec3f::UnitY())); data.add_vertex(Vec3f(outer_radius, 0.0f, -half_thickness), (Vec3f)(-Vec3f::UnitY())); data.add_vertex(Vec3f(inner_radius, 0.0f, half_thickness), (Vec3f)(-Vec3f::UnitY())); data.add_vertex(Vec3f(outer_radius, 0.0f, half_thickness), (Vec3f)(-Vec3f::UnitY())); first_id = 26 + 6 * (resolution + 1); data.add_triangle(first_id, first_id + 1, first_id + 3); data.add_triangle(first_id, first_id + 3, first_id + 2); for (int i = resolution; i >= 0; --i) { const float angle = float(i) * step_angle; const float c = ::cos(angle); const float s = ::sin(angle); data.add_vertex(Vec3f(outer_radius * s, outer_radius * c, -half_thickness), Vec3f(s, c, 0.0f)); } for (int i = resolution; i >= 0; --i) { const float angle = float(i) * step_angle; const float c = ::cos(angle); const float s = ::sin(angle); data.add_vertex(Vec3f(outer_radius * s, outer_radius * c, +half_thickness), Vec3f(s, c, 0.0f)); } first_id = 30 + 6 * (resolution + 1); for (unsigned int i = 0; i < resolution; ++i) { const unsigned int ii = first_id + i; data.add_triangle(ii, ii + 1, ii + resolution + 2); data.add_triangle(ii, ii + resolution + 2, ii + resolution + 1); } #else // tip // top face vertices append_vertex(entity, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitZ()); append_vertex(entity, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitZ()); append_vertex(entity, { -tip_height, radius, half_thickness }, Vec3f::UnitZ()); append_vertex(entity, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitZ()); append_vertex(entity, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitZ()); // top face triangles append_indices(entity, 0, 1, 2); append_indices(entity, 0, 2, 4); append_indices(entity, 4, 2, 3); // bottom face vertices append_vertex(entity, { 0.0f, outer_radius, -half_thickness }, -Vec3f::UnitZ()); append_vertex(entity, { 0.0f, radius + half_tip_width, -half_thickness }, -Vec3f::UnitZ()); append_vertex(entity, { -tip_height, radius, -half_thickness }, -Vec3f::UnitZ()); append_vertex(entity, { 0.0f, radius - half_tip_width, -half_thickness }, -Vec3f::UnitZ()); append_vertex(entity, { 0.0f, inner_radius, -half_thickness }, -Vec3f::UnitZ()); // bottom face triangles append_indices(entity, 5, 7, 6); append_indices(entity, 5, 9, 7); append_indices(entity, 9, 8, 7); // side faces vertices append_vertex(entity, { 0.0f, outer_radius, -half_thickness }, Vec3f::UnitX()); append_vertex(entity, { 0.0f, radius + half_tip_width, -half_thickness }, Vec3f::UnitX()); append_vertex(entity, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitX()); append_vertex(entity, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitX()); Vec3f normal(-half_tip_width, tip_height, 0.0f); normal.normalize(); append_vertex(entity, { 0.0f, radius + half_tip_width, -half_thickness }, normal); append_vertex(entity, { -tip_height, radius, -half_thickness }, normal); append_vertex(entity, { 0.0f, radius + half_tip_width, half_thickness }, normal); append_vertex(entity, { -tip_height, radius, half_thickness }, normal); normal = Vec3f(-half_tip_width, -tip_height, 0.0f); normal.normalize(); append_vertex(entity, { -tip_height, radius, -half_thickness }, normal); append_vertex(entity, { 0.0f, radius - half_tip_width, -half_thickness }, normal); append_vertex(entity, { -tip_height, radius, half_thickness }, normal); append_vertex(entity, { 0.0f, radius - half_tip_width, half_thickness }, normal); append_vertex(entity, { 0.0f, radius - half_tip_width, -half_thickness }, Vec3f::UnitX()); append_vertex(entity, { 0.0f, inner_radius, -half_thickness }, Vec3f::UnitX()); append_vertex(entity, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitX()); append_vertex(entity, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitX()); // side face triangles for (int i = 0; i < 4; ++i) { const int ii = i * 4; append_indices(entity, 10 + ii, 11 + ii, 13 + ii); append_indices(entity, 10 + ii, 13 + ii, 12 + ii); } // stem // top face vertices for (unsigned int i = 0; i <= resolution; ++i) { const float angle = static_cast<float>(i) * step_angle; append_vertex(entity, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); } for (unsigned int i = 0; i <= resolution; ++i) { const float angle = static_cast<float>(i) * step_angle; append_vertex(entity, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); } // top face triangles for (unsigned int i = 0; i < resolution; ++i) { append_indices(entity, 26 + i, 27 + i, 27 + resolution + i); append_indices(entity, 27 + i, 28 + resolution + i, 27 + resolution + i); } // bottom face vertices for (unsigned int i = 0; i <= resolution; ++i) { const float angle = static_cast<float>(i) * step_angle; append_vertex(entity, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); } for (unsigned int i = 0; i <= resolution; ++i) { const float angle = static_cast<float>(i) * step_angle; append_vertex(entity, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); } // bottom face triangles for (unsigned int i = 0; i < resolution; ++i) { append_indices(entity, 28 + 2 * resolution + i, 29 + 3 * resolution + i, 29 + 2 * resolution + i); append_indices(entity, 29 + 2 * resolution + i, 29 + 3 * resolution + i, 30 + 3 * resolution + i); } // side faces vertices and triangles for (unsigned int i = 0; i <= resolution; ++i) { const float angle = static_cast<float>(i) * step_angle; const float c = ::cos(angle); const float s = ::sin(angle); append_vertex(entity, { inner_radius * s, inner_radius * c, -half_thickness }, { -s, -c, 0.0f }); } for (unsigned int i = 0; i <= resolution; ++i) { const float angle = static_cast<float>(i) * step_angle; const float c = ::cos(angle); const float s = ::sin(angle); append_vertex(entity, { inner_radius * s, inner_radius * c, half_thickness }, { -s, -c, 0.0f }); } unsigned int first_id = 26 + 4 * (resolution + 1); for (unsigned int i = 0; i < resolution; ++i) { const unsigned int ii = first_id + i; append_indices(entity, ii, ii + 1, ii + resolution + 2); append_indices(entity, ii, ii + resolution + 2, ii + resolution + 1); } append_vertex(entity, { inner_radius, 0.0f, -half_thickness }, -Vec3f::UnitY()); append_vertex(entity, { outer_radius, 0.0f, -half_thickness }, -Vec3f::UnitY()); append_vertex(entity, { inner_radius, 0.0f, half_thickness }, -Vec3f::UnitY()); append_vertex(entity, { outer_radius, 0.0f, half_thickness }, -Vec3f::UnitY()); first_id = 26 + 6 * (resolution + 1); append_indices(entity, first_id, first_id + 1, first_id + 3); append_indices(entity, first_id, first_id + 3, first_id + 2); for (int i = int(resolution); i >= 0; --i) { const float angle = static_cast<float>(i) * step_angle; const float c = ::cos(angle); const float s = ::sin(angle); append_vertex(entity, { outer_radius * s, outer_radius * c, -half_thickness }, { s, c, 0.0f }); } for (int i = int(resolution); i >= 0; --i) { const float angle = static_cast<float>(i) * step_angle; const float c = ::cos(angle); const float s = ::sin(angle); append_vertex(entity, { outer_radius * s, outer_radius * c, +half_thickness }, { s, c, 0.0f }); } first_id = 30 + 6 * (resolution + 1); for (unsigned int i = 0; i < resolution; ++i) { const unsigned int ii = first_id + i; append_indices(entity, ii, ii + 1, ii + resolution + 2); append_indices(entity, ii, ii + resolution + 2, ii + resolution + 1); } data.entities.emplace_back(entity); #endif // ENABLE_LEGACY_OPENGL_REMOVAL return data; } GLModel::Geometry straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness) { #if !ENABLE_LEGACY_OPENGL_REMOVAL auto append_vertex = [](GLModel::Geometry::Entity& entity, const Vec3f& position, const Vec3f& normal) { entity.positions.emplace_back(position); entity.normals.emplace_back(normal); }; auto append_indices = [](GLModel::Geometry::Entity& entity, unsigned int v1, unsigned int v2, unsigned int v3) { entity.indices.emplace_back(v1); entity.indices.emplace_back(v2); entity.indices.emplace_back(v3); }; #endif // !ENABLE_LEGACY_OPENGL_REMOVAL GLModel::Geometry data; #if ENABLE_LEGACY_OPENGL_REMOVAL data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; data.reserve_vertices(42); data.reserve_indices(72); #else GLModel::Geometry::Entity entity; entity.type = GLModel::EPrimitiveType::Triangles; #endif // ENABLE_LEGACY_OPENGL_REMOVAL const float half_thickness = 0.5f * thickness; const float half_stem_width = 0.5f * stem_width; const float half_tip_width = 0.5f * tip_width; const float total_height = tip_height + stem_height; #if ENABLE_LEGACY_OPENGL_REMOVAL // top face vertices data.add_vertex(Vec3f(half_stem_width, 0.0f, half_thickness), (Vec3f)Vec3f::UnitZ()); data.add_vertex(Vec3f(half_stem_width, stem_height, half_thickness), (Vec3f)Vec3f::UnitZ()); data.add_vertex(Vec3f(half_tip_width, stem_height, half_thickness), (Vec3f)Vec3f::UnitZ()); data.add_vertex(Vec3f(0.0f, total_height, half_thickness), (Vec3f)Vec3f::UnitZ()); data.add_vertex(Vec3f(-half_tip_width, stem_height, half_thickness), (Vec3f)Vec3f::UnitZ()); data.add_vertex(Vec3f(-half_stem_width, stem_height, half_thickness), (Vec3f)Vec3f::UnitZ()); data.add_vertex(Vec3f(-half_stem_width, 0.0f, half_thickness), (Vec3f)Vec3f::UnitZ()); // top face triangles data.add_triangle(0, 1, 6); data.add_triangle(6, 1, 5); data.add_triangle(4, 5, 3); data.add_triangle(5, 1, 3); data.add_triangle(1, 2, 3); // bottom face vertices data.add_vertex(Vec3f(half_stem_width, 0.0f, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); data.add_vertex(Vec3f(half_stem_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); data.add_vertex(Vec3f(half_tip_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); data.add_vertex(Vec3f(0.0f, total_height, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); data.add_vertex(Vec3f(-half_tip_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); data.add_vertex(Vec3f(-half_stem_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); data.add_vertex(Vec3f(-half_stem_width, 0.0f, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); // bottom face triangles data.add_triangle(7, 13, 8); data.add_triangle(13, 12, 8); data.add_triangle(12, 11, 10); data.add_triangle(8, 12, 10); data.add_triangle(9, 8, 10); // side faces vertices data.add_vertex(Vec3f(half_stem_width, 0.0f, -half_thickness), (Vec3f)Vec3f::UnitX()); data.add_vertex(Vec3f(half_stem_width, stem_height, -half_thickness), (Vec3f)Vec3f::UnitX()); data.add_vertex(Vec3f(half_stem_width, 0.0f, half_thickness), (Vec3f)Vec3f::UnitX()); data.add_vertex(Vec3f(half_stem_width, stem_height, half_thickness), (Vec3f)Vec3f::UnitX()); data.add_vertex(Vec3f(half_stem_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitY())); data.add_vertex(Vec3f(half_tip_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitY())); data.add_vertex(Vec3f(half_stem_width, stem_height, half_thickness), (Vec3f)(-Vec3f::UnitY())); data.add_vertex(Vec3f(half_tip_width, stem_height, half_thickness), (Vec3f)(-Vec3f::UnitY())); Vec3f normal(tip_height, half_tip_width, 0.0f); normal.normalize(); data.add_vertex(Vec3f(half_tip_width, stem_height, -half_thickness), normal); data.add_vertex(Vec3f(0.0f, total_height, -half_thickness), normal); data.add_vertex(Vec3f(half_tip_width, stem_height, half_thickness), normal); data.add_vertex(Vec3f(0.0f, total_height, half_thickness), normal); normal = { -tip_height, half_tip_width, 0.0f }; normal.normalize(); data.add_vertex(Vec3f(0.0f, total_height, -half_thickness), normal); data.add_vertex(Vec3f(-half_tip_width, stem_height, -half_thickness), normal); data.add_vertex(Vec3f(0.0f, total_height, half_thickness), normal); data.add_vertex(Vec3f(-half_tip_width, stem_height, half_thickness), normal); data.add_vertex(Vec3f(-half_tip_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitY())); data.add_vertex(Vec3f(-half_stem_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitY())); data.add_vertex(Vec3f(-half_tip_width, stem_height, half_thickness), (Vec3f)(-Vec3f::UnitY())); data.add_vertex(Vec3f(-half_stem_width, stem_height, half_thickness), (Vec3f)(-Vec3f::UnitY())); data.add_vertex(Vec3f(-half_stem_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitX())); data.add_vertex(Vec3f(-half_stem_width, 0.0f, -half_thickness), (Vec3f)(-Vec3f::UnitX())); data.add_vertex(Vec3f(-half_stem_width, stem_height, half_thickness), (Vec3f)(-Vec3f::UnitX())); data.add_vertex(Vec3f(-half_stem_width, 0.0f, half_thickness), (Vec3f)(-Vec3f::UnitX())); data.add_vertex(Vec3f(-half_stem_width, 0.0f, -half_thickness), (Vec3f)(-Vec3f::UnitY())); data.add_vertex(Vec3f(half_stem_width, 0.0f, -half_thickness), (Vec3f)(-Vec3f::UnitY())); data.add_vertex(Vec3f(-half_stem_width, 0.0f, half_thickness), (Vec3f)(-Vec3f::UnitY())); data.add_vertex(Vec3f(half_stem_width, 0.0f, half_thickness), (Vec3f)(-Vec3f::UnitY())); // side face triangles for (unsigned int i = 0; i < 7; ++i) { const unsigned int ii = i * 4; data.add_triangle(14 + ii, 15 + ii, 17 + ii); data.add_triangle(14 + ii, 17 + ii, 16 + ii); } #else // top face vertices append_vertex(entity, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ()); append_vertex(entity, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ()); append_vertex(entity, { half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ()); append_vertex(entity, { 0.0, total_height, half_thickness }, Vec3f::UnitZ()); append_vertex(entity, { -half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ()); append_vertex(entity, { -half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ()); append_vertex(entity, { -half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ()); // top face triangles append_indices(entity, 0, 1, 6); append_indices(entity, 6, 1, 5); append_indices(entity, 4, 5, 3); append_indices(entity, 5, 1, 3); append_indices(entity, 1, 2, 3); // bottom face vertices append_vertex(entity, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ()); append_vertex(entity, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); append_vertex(entity, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); append_vertex(entity, { 0.0, total_height, -half_thickness }, -Vec3f::UnitZ()); append_vertex(entity, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); append_vertex(entity, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); append_vertex(entity, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ()); // bottom face triangles append_indices(entity, 7, 13, 8); append_indices(entity, 13, 12, 8); append_indices(entity, 12, 11, 10); append_indices(entity, 8, 12, 10); append_indices(entity, 9, 8, 10); // side faces vertices append_vertex(entity, { half_stem_width, 0.0, -half_thickness }, Vec3f::UnitX()); append_vertex(entity, { half_stem_width, stem_height, -half_thickness }, Vec3f::UnitX()); append_vertex(entity, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitX()); append_vertex(entity, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitX()); append_vertex(entity, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY()); append_vertex(entity, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY()); append_vertex(entity, { half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY()); append_vertex(entity, { half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY()); Vec3f normal(tip_height, half_tip_width, 0.0f); normal.normalize(); append_vertex(entity, { half_tip_width, stem_height, -half_thickness }, normal); append_vertex(entity, { 0.0, total_height, -half_thickness }, normal); append_vertex(entity, { half_tip_width, stem_height, half_thickness }, normal); append_vertex(entity, { 0.0, total_height, half_thickness }, normal); normal = Vec3f(-tip_height, half_tip_width, 0.0f); normal.normalize(); append_vertex(entity, { 0.0, total_height, -half_thickness }, normal); append_vertex(entity, { -half_tip_width, stem_height, -half_thickness }, normal); append_vertex(entity, { 0.0, total_height, half_thickness }, normal); append_vertex(entity, { -half_tip_width, stem_height, half_thickness }, normal); append_vertex(entity, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY()); append_vertex(entity, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY()); append_vertex(entity, { -half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY()); append_vertex(entity, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY()); append_vertex(entity, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitX()); append_vertex(entity, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitX()); append_vertex(entity, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitX()); append_vertex(entity, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitX()); append_vertex(entity, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY()); append_vertex(entity, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY()); append_vertex(entity, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY()); append_vertex(entity, { half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY()); // side face triangles for (int i = 0; i < 7; ++i) { const int ii = i * 4; append_indices(entity, 14 + ii, 15 + ii, 17 + ii); append_indices(entity, 14 + ii, 17 + ii, 16 + ii); } data.entities.emplace_back(entity); #endif // ENABLE_LEGACY_OPENGL_REMOVAL return data; } GLModel::Geometry diamond(unsigned int resolution) { resolution = std::max<unsigned int>(4, resolution); GLModel::Geometry data; #if ENABLE_LEGACY_OPENGL_REMOVAL data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; data.reserve_vertices(resolution + 2); data.reserve_indices((2 * (resolution + 1)) * 3); #else GLModel::Geometry::Entity entity; entity.type = GLModel::EPrimitiveType::Triangles; #endif // ENABLE_LEGACY_OPENGL_REMOVAL const float step = 2.0f * float(PI) / float(resolution); #if ENABLE_LEGACY_OPENGL_REMOVAL // vertices for (unsigned int i = 0; i < resolution; ++i) { const float ii = float(i) * step; const Vec3f p = { 0.5f * ::cos(ii), 0.5f * ::sin(ii), 0.0f }; data.add_vertex(p, (Vec3f)p.normalized()); } Vec3f p = { 0.0f, 0.0f, 0.5f }; data.add_vertex(p, (Vec3f)p.normalized()); p = { 0.0f, 0.0f, -0.5f }; data.add_vertex(p, (Vec3f)p.normalized()); // triangles // top for (unsigned int i = 0; i < resolution; ++i) { data.add_triangle(i + 0, i + 1, resolution); } data.add_triangle(resolution - 1, 0, resolution); // bottom for (unsigned int i = 0; i < resolution; ++i) { data.add_triangle(i + 0, resolution + 1, i + 1); } data.add_triangle(resolution - 1, resolution + 1, 0); #else // positions for (unsigned int i = 0; i < resolution; ++i) { const 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 (unsigned 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 (unsigned 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); #endif // ENABLE_LEGACY_OPENGL_REMOVAL return data; } #if ENABLE_LEGACY_OPENGL_REMOVAL GLModel::Geometry smooth_sphere(unsigned int resolution, float radius) { resolution = std::max<unsigned int>(4, resolution); const unsigned int sectorCount = resolution; const unsigned int stackCount = resolution; const float sectorStep = float(2.0 * M_PI / sectorCount); const float stackStep = float(M_PI / stackCount); GLModel::Geometry data; data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; data.reserve_vertices((stackCount - 1) * sectorCount + 2); data.reserve_indices((2 * (stackCount - 1) * sectorCount) * 3); // vertices for (unsigned int i = 0; i <= stackCount; ++i) { // from pi/2 to -pi/2 const double stackAngle = 0.5 * M_PI - stackStep * i; const double xy = double(radius) * ::cos(stackAngle); const double z = double(radius) * ::sin(stackAngle); if (i == 0 || i == stackCount) { const Vec3f v(float(xy), 0.0f, float(z)); data.add_vertex(v, (Vec3f)v.normalized()); } else { for (unsigned int j = 0; j < sectorCount; ++j) { // from 0 to 2pi const double sectorAngle = sectorStep * j; const Vec3f v(float(xy * std::cos(sectorAngle)), float(xy * std::sin(sectorAngle)), float(z)); data.add_vertex(v, (Vec3f)v.normalized()); } } } // triangles for (unsigned int i = 0; i < stackCount; ++i) { // Beginning of current stack. unsigned int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount); const unsigned int k1_first = k1; // Beginning of next stack. unsigned int k2 = (i == 0) ? 1 : (k1 + sectorCount); const unsigned int k2_first = k2; for (unsigned int j = 0; j < sectorCount; ++j) { // 2 triangles per sector excluding first and last stacks unsigned int k1_next = k1; unsigned int k2_next = k2; if (i != 0) { k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1); data.add_triangle(k1, k2, k1_next); } if (i + 1 != stackCount) { k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1); data.add_triangle(k1_next, k2, k2_next); } k1 = k1_next; k2 = k2_next; } } return data; } GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radius, float height) { resolution = std::max<unsigned int>(4, resolution); const unsigned int sectorCount = resolution; const float sectorStep = 2.0f * float(M_PI) / float(sectorCount); GLModel::Geometry data; data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; data.reserve_vertices(sectorCount * 4 + 2); data.reserve_indices(sectorCount * 4 * 3); auto generate_vertices_on_circle = [sectorCount, sectorStep](Axis axis, float radius) { std::vector<Vec3f> ret; ret.reserve(sectorCount); for (unsigned int i = 0; i < sectorCount; ++i) { // from 0 to 2pi const float sectorAngle = (i != sectorCount) ? sectorStep * i : 0.0f; const float x1 = radius * std::cos(sectorAngle); const float x2 = radius * std::sin(sectorAngle); Vec3f v; switch (axis) { case X: { v = Vec3f(0.0f, x1, x2); break; } case Y: { v = Vec3f(-x1, 0.0f, x2); break; } case Z: { v = Vec3f(x1, x2, 0.0f); break; } default: { assert(false); break; } } ret.emplace_back(v); } return ret; }; const std::vector<Vec3f> base_vertices = generate_vertices_on_circle(axis, radius); Vec3f h; switch (axis) { case X: { h = height * Vec3f::UnitX(); break; } case Y: { h = height * Vec3f::UnitY(); break; } case Z: { h = height * Vec3f::UnitZ(); break; } default: { assert(false); break; } } // stem vertices for (unsigned int i = 0; i < sectorCount; ++i) { const Vec3f& v = base_vertices[i]; const Vec3f n = v.normalized(); data.add_vertex(v, n); data.add_vertex(v + h, n); } // stem triangles for (unsigned int i = 0; i < sectorCount; ++i) { unsigned int v1 = i * 2; unsigned int v2 = (i < sectorCount - 1) ? v1 + 2 : 0; unsigned int v3 = v2 + 1; unsigned int v4 = v1 + 1; data.add_triangle(v1, v2, v3); data.add_triangle(v1, v3, v4); } // bottom cap vertices Vec3f cap_center = Vec3f::Zero(); unsigned int cap_center_id = data.vertices_count(); Vec3f normal; switch (axis) { case X: { normal = -Vec3f::UnitX(); break; } case Y: { normal = -Vec3f::UnitY(); break; } case Z: { normal = -Vec3f::UnitZ(); break; } default: { assert(false); break; } } data.add_vertex(cap_center, normal); for (unsigned int i = 0; i < sectorCount; ++i) { data.add_vertex(base_vertices[i], normal); } // bottom cap triangles for (unsigned int i = 0; i < sectorCount; ++i) { data.add_triangle(cap_center_id, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1, cap_center_id + i + 1); } // top cap vertices cap_center += h; cap_center_id = data.vertices_count(); normal = -normal; data.add_vertex(cap_center, normal); for (unsigned int i = 0; i < sectorCount; ++i) { data.add_vertex(base_vertices[i] + h, normal); } // top cap triangles for (unsigned int i = 0; i < sectorCount; ++i) { data.add_triangle(cap_center_id, cap_center_id + i + 1, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1); } return data; } #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI } // namespace Slic3r