mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2026-01-29 12:20:50 -07:00
Includes (but is not limited to) the following commits: SPE-2218 - libvgcode - Fixed color of wipe moves for speed range view SPE-2218 - libvgcode - Fixed detection of fan speed range SPE-2218 - libvgcode - Fixed detection of temperature range SPE-2218 - libvgcode - Fixed colors for Actual volumetric flow rate view SPE-2214 - Fixed detection of toolpaths bounding box in GCodeViewer SPE-2206 - Modified LibBGCode.cmake to get latest version of libbgcode which fixed parsing of gcode lines G4 libvgcode - Fixed potential out of bound access in ViewerImpl::update_view_full_range() Tech ENABLE_GL_CORE_PROFILE set as default Tech ENABLE_OPENGL_ES replaced by build option SLIC3R_OPENGL_ES libvgcode - Precompiler definition of ENABLE_OPENGL_ES moved into CMakeLists.txt Added missing include libvgcode - Textures setup modified to work when building using emscripten libvgcode - small optimization libvgcode - fixed OpenGLWrapper::unload_opengl() libvgcode - CMakeLists.txt modified to work with emscripten libvgcode - Replace 'glVertexAttribIPointer()' with 'glVertexAttribPointer()' in SegmentTemplate::init() for OpenGL ES libvgcode - Replace 'xor' with '^' Bitset.hpp libvgcode - Newer glad library for OpenGL 4.6 and OpenGL ES 3.0 libvgcode - Alternate fix in method ViewerImpl::update_heights_widths() for OpenGL ES libvgcode - Fixes in glAssertRecentCallImpl() libvgcode - Fixes in method ViewerImpl::update_heights_widths() for OpenGL ES Fixed ES shaders so they work with OpenGL ES 3.0 libvgcode - Use multiple plain textures in place of texture buffers for OpenGL ES libvgcode - Use plain textures in place of texture buffers for OpenGL ES (partial implementation using one texture per buffer) libvgcode - refactoring of class OpenGLWrapper libvgcode - small refactoring in shaders libvgcode - replacement of glMapBuffer() call for OpenGL ES Fixed warning libvgcode - Changes into CMakeLists.txt Fixed debug export of gcode data to be configuration indipendent Disabled tech ENABLE_NEW_GCODE_VIEWER_DEBUG Removed obsolete tech ENABLE_GCODE_VIEWER_DATA_CHECKING Code cleanup and techs removal - completed Code cleanup and techs removal - step 1 SPE-1872: Implemented G2/G3 lines discretization for gcfMarlinFirmware firmware flavour SPE-1872: Corrections into GCodeProcessor::process_G2_G3() to match firmware code SPE-1872: Actual speed profile - Further enhancements of imgui debug window SPE-1872: Actual speed profile - Rework in its calculation + enhanced imgui debug window SPE-1872: New imgui widget to show actual speed profile SPE-1872: Fixed actual speed for seam moves and at extrusion/travel/wipe start SPE-1872: Fixed rendering of wipe moves when actual speed view is selected SPE-1872: Actual speed profile extended to travel and wipe moves SPE-1872: Fixes in function recalculate_trapezoids() and method GCodeProcessor::TimeMachine::calculate_time() to smooth actual speed profile SPE-1872: Added debug graphic to show move actual speed profile SPE-1872: libvgcode library: replace volumetric flow rate data with mm3_per_mm to reduce memory usage SPE-1872: Added visualization of actual volumetric flow rate SPE-1872: Fixes in calculating actual speed SPE-1872: Added visualization of actual speed in gcode preview SPE-2124: Added command line option 'opengl-aa' to allow the user to turn on the automatic selection of max number of supported samples for OpenGL antialising #12117: Reduced moire patterns by using the highest number of samples available for multisampling New gcode visualization integration - Partially enabled imgui debug window New gcode visualization integration - Fixed center of gravity calculation and rendering New gcode visualization library - Interface for estimated times New gcode visualization library - Tool marker rendering New gcode visualization library - Axes aligned bounding boxes calculation Removed obsolete debug code New gcode visualization library - Added statistic of used memory New gcode visualization library - Separation of tool colors and color changes colors to simplify client code Added missing include New gcode visualization library - Added print color changes detection New gcode visualization library - Modified OpenGL ES context detection New gcode visualization library - Another makefile cleanup New gcode visualization library - Makefiles cleanup New gcode visualization library - Added suppression of error 'This function or variable may be unsafe' in VS2022 New gcode visualization library - Compatibility with OpenGL ES New gcode visualization library - Interface cleanup and documentation New gcode visualization library - Extended interface to give access to estimated times New gcode visualization integration - Toggling of top layer only view state New gcode visualization integration - Removed imperial units from tool position data Small refactoring New gcode visualization library - Custom values for travel and wipe moves radius New gcode visualization library - Allow customization of range colors New gcode visualization library - Partial update of interface comments/documentation New gcode visualization integration - Follow-up of 35ee55e29bb231fd01a2eb71ae293832a37ca65d - Better fix for toolpaths visible range when toggling options' visibility New gcode visualization integration - Fixed toolpaths reset New gcode visualization library - Fixed method set_option_color() New gcode visualization library - Fixed method ViewerImpl::set_extrusion_role_color() New gcode visualization library - Added methods to release gpu resources on demand. New gcode visualization library - Travel and wipe moves as options New gcode visualization integration - Fixed toolpaths visible range when toggling options' visibility New gcode visualization integration - Fixed management of gcode preview view type New gcode visualization - Fixed wrong include New gcode visualization - Added missing headers New gcode visualization - Refactoring + added missing headers New gcode visualization - New code set as standalone library + embed glad library to load OpenGL functions New gcode visualization - Fixed errors and warnings when building the new code as a standalone library New gcode visualization integration - Fixed layers ordering in pre-gcode preview New gcode visualization integration - Fixed objects' tool colors in pre-gcode preview Code cleanup New gcode visualization integration - Tool position properties data window New gcode visualization integration - Fixed in export toolpaths to obj New gcode visualization - Inlining in source code Refactoring New gcode visualization integration - Export toolpaths to obj Some refactoring and warning fix New gcode visualization integration - Customizable travel moves colors New gcode visualization integration - Customizable options colors New gcode visualization integration - Customizable extrusion roles colors New gcode visualization integration - Fixed pre-gcode preview layers times New gcode visualization integration - Modify pre-gcode preview to use the new toolpaths renderer, objects WIP New gcode visualization - Modify pre-gcode preview to use the new toolpaths renderer, WIP (brim/skirt/wipe tower) New gcode visualization integration - Do not reset visible range when toggling options/roles visibility New gcode visualization - Fixed color of first vertex of top layer (when top layer only option is enabled) New gcode visualization - Customizable travels and wipes segment radius New gcode visualization integration - Removed tech ENABLE_GCODE_VIEWER_STATISTICS New gcode visualization integration - Added check of OpenGL version New gcode visualization integration - Removed GCodeProcessorResult::spiral_vase_layers Another bunch of warnings fixes Fixed warnings New gcode visualization integration - Removal of old visualization Fixed includes New gcode visualization integration - File structure of new code separated in api + src New gcode visualization integration - View ranges management moved to new visualizer New gcode visualization integration - Fixed top layer only visualization for MMU printers New gcode visualization integration - Removed dependency on imgui from new visualizer Some refactoring New gcode visualization integration - Removed dependency on Slic3r::GCodeProcessorResult from new visualizer New gcode visualization integration - Moves' data conversion moved to client side New gcode visualization: layers times refactoring A bunch of fixes for the new gcode visualization New gcode visualization: render in gray color layers containing pause print or custom gcode options when in Color Print view New gcode visualization integration - Tool colors New gcode visualization integration - Layers times New gcode visualization integration - Travels and Extrusion roles times Fixed detection of start/end of contiguous extrusion paths New gcode visualization integration - Extrusion roles New gcode visualization integration - Colors New gcode visualization integration - Tool position Center of gravity and tool marker may both be rendered with fixed screen size and a scaling factor Fixed rendering of options in new gcode visualization Tool marker NOT rendered by the new visualization code Center of gravity marker NOT rendered by the new visualization code Fixed toolpaths_cog shaders Tool position window for new gcode visualization Top layer only coloring for neww gcode visualization Refactoring in preview's new visualization Hidden imgui debug dialog for new visualization in preview Synchronization of moves between old and new visualization Fixed missing gcode window in new visualization Rendering of debug imgui dialog moved from class libvgcode::Viewer to class libvgcode::Toolpaths + warnings fixing Some functionality moved from class libvgcode::Viewer to class libvgcode::Toolpaths Some refactoring and cleanup Refatoring of PathVertex and Toolpaths::load() SPE-1982: Tech ENABLE_NEW_GCODE_VIEWER - 1st installment of new toolpaths rendering code (WIP)
1552 lines
59 KiB
C++
1552 lines
59 KiB
C++
#include "libslic3r/libslic3r.h"
|
|
#include "GLModel.hpp"
|
|
|
|
#include "3DScene.hpp"
|
|
#include "GUI_App.hpp"
|
|
#include "GLShader.hpp"
|
|
|
|
#include "libslic3r/TriangleMesh.hpp"
|
|
#include "libslic3r/Model.hpp"
|
|
#include "libslic3r/Polygon.hpp"
|
|
#include "libslic3r/BuildVolume.hpp"
|
|
#include "libslic3r/Geometry/ConvexHull.hpp"
|
|
|
|
#include <boost/filesystem/operations.hpp>
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
|
|
#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
|
|
|
|
#include <GL/glew.h>
|
|
|
|
namespace Slic3r {
|
|
namespace GUI {
|
|
|
|
#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
|
|
|
|
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.emplace_back(position.x());
|
|
vertices.emplace_back(position.y());
|
|
vertices.emplace_back(position.z());
|
|
vertices.emplace_back(tex_coord.x());
|
|
vertices.emplace_back(tex_coord.y());
|
|
}
|
|
|
|
void GLModel::Geometry::add_vertex(const Vec3f& position, const Vec3f& normal)
|
|
{
|
|
assert(format.vertex_layout == EVertexLayout::P3N3);
|
|
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());
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void GLModel::Geometry::add_triangle(unsigned int id1, unsigned int id2, unsigned int id3)
|
|
{
|
|
indices.emplace_back(id1);
|
|
indices.emplace_back(id2);
|
|
indices.emplace_back(id3);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
indexed_triangle_set GLModel::Geometry::get_as_indexed_triangle_set() const
|
|
{
|
|
indexed_triangle_set its;
|
|
its.vertices.reserve(vertices_count());
|
|
for (size_t i = 0; i < vertices_count(); ++i) {
|
|
its.vertices.emplace_back(extract_position_3(i));
|
|
}
|
|
its.indices.reserve(indices_count() / 3);
|
|
for (size_t i = 0; i < indices_count() / 3; ++i) {
|
|
const size_t tri_id = i * 3;
|
|
its.indices.emplace_back(extract_index(tri_id), extract_index(tri_id + 1), extract_index(tri_id + 2));
|
|
}
|
|
return its;
|
|
}
|
|
|
|
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; }
|
|
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:
|
|
case EVertexLayout::P3N3T2: { return 3; }
|
|
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:
|
|
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:
|
|
case EVertexLayout::P3N3T2: { return 3; }
|
|
default: { assert(false); return 0; }
|
|
};
|
|
}
|
|
|
|
size_t GLModel::Geometry::normal_offset_floats(const Format& format)
|
|
{
|
|
switch (format.vertex_layout)
|
|
{
|
|
case EVertexLayout::P3N3:
|
|
case EVertexLayout::P3N3T2: { return 3; }
|
|
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; }
|
|
};
|
|
}
|
|
|
|
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:
|
|
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:
|
|
case EVertexLayout::P3N3T2: { return true; }
|
|
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:
|
|
case EVertexLayout::P4: { return false; }
|
|
default: { assert(false); return false; }
|
|
};
|
|
}
|
|
|
|
void GLModel::init_from(Geometry&& data)
|
|
{
|
|
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>());
|
|
}
|
|
}
|
|
}
|
|
|
|
void GLModel::init_from(const TriangleMesh& mesh)
|
|
{
|
|
init_from(mesh.its);
|
|
}
|
|
|
|
void GLModel::init_from(const indexed_triangle_set& its)
|
|
{
|
|
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>());
|
|
}
|
|
}
|
|
|
|
void GLModel::init_from(const Polygons& polygons, float z)
|
|
{
|
|
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>());
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
init_from(model.mesh());
|
|
|
|
m_filename = filename;
|
|
|
|
return true;
|
|
}
|
|
|
|
void GLModel::reset()
|
|
{
|
|
// 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 (m_render_data.vbo_id > 0) {
|
|
glsafe(::glDeleteBuffers(1, &m_render_data.vbo_id));
|
|
m_render_data.vbo_id = 0;
|
|
}
|
|
#if !SLIC3R_OPENGL_ES
|
|
if (OpenGLManager::get_gl_info().is_core_profile()) {
|
|
#endif // !SLIC3R_OPENGL_ES
|
|
if (m_render_data.vao_id > 0) {
|
|
glsafe(::glDeleteVertexArrays(1, &m_render_data.vao_id));
|
|
m_render_data.vao_id = 0;
|
|
}
|
|
#if !SLIC3R_OPENGL_ES
|
|
}
|
|
#endif // !SLIC3R_OPENGL_ES
|
|
|
|
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>();
|
|
m_bounding_box = BoundingBoxf3();
|
|
m_filename = std::string();
|
|
}
|
|
|
|
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()
|
|
{
|
|
render(std::make_pair<size_t, size_t>(0, indices_count()));
|
|
}
|
|
|
|
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 !SLIC3R_OPENGL_ES
|
|
if (OpenGLManager::get_gl_info().is_core_profile()) {
|
|
#endif // !SLIC3R_OPENGL_ES
|
|
glsafe(::glBindVertexArray(m_render_data.vao_id));
|
|
#if !SLIC3R_OPENGL_ES
|
|
}
|
|
#endif // !SLIC3R_OPENGL_ES
|
|
// the following binding is needed to set the vertex attributes
|
|
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_render_data.vbo_id));
|
|
|
|
int position_id = -1;
|
|
int normal_id = -1;
|
|
int tex_coord_id = -1;
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
shader->set_uniform("uniform_color", data.color);
|
|
|
|
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))));
|
|
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
|
|
|
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 !SLIC3R_OPENGL_ES
|
|
if (OpenGLManager::get_gl_info().is_core_profile()) {
|
|
#endif // !SLIC3R_OPENGL_ES
|
|
glsafe(::glBindVertexArray(0));
|
|
#if !SLIC3R_OPENGL_ES
|
|
}
|
|
#endif // !SLIC3R_OPENGL_ES
|
|
}
|
|
|
|
void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count)
|
|
{
|
|
if (instances_vbo == 0 || instances_count == 0)
|
|
return;
|
|
|
|
GLShaderProgram* shader = wxGetApp().get_current_shader();
|
|
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;
|
|
}
|
|
|
|
#if !SLIC3R_OPENGL_ES
|
|
if (OpenGLManager::get_gl_info().is_core_profile()) {
|
|
#endif // !SLIC3R_OPENGL_ES
|
|
glsafe(::glBindVertexArray(m_render_data.vao_id));
|
|
#if !SLIC3R_OPENGL_ES
|
|
}
|
|
#endif // !SLIC3R_OPENGL_ES
|
|
|
|
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, instances_vbo));
|
|
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));
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_render_data.ibo_id));
|
|
glsafe(::glDrawElementsInstanced(mode, indices_count(), index_type, (const void*)0, instances_count));
|
|
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
|
|
|
if (normal)
|
|
glsafe(::glDisableVertexAttribArray(normal_id));
|
|
if (position)
|
|
glsafe(::glDisableVertexAttribArray(position_id));
|
|
|
|
glsafe(::glDisableVertexAttribArray(scales_id));
|
|
glsafe(::glDisableVertexAttribArray(offset_id));
|
|
|
|
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
|
#if !SLIC3R_OPENGL_ES
|
|
if (OpenGLManager::get_gl_info().is_core_profile()) {
|
|
#endif // !SLIC3R_OPENGL_ES
|
|
glsafe(::glBindVertexArray(0));
|
|
#if !SLIC3R_OPENGL_ES
|
|
}
|
|
#endif // !SLIC3R_OPENGL_ES
|
|
}
|
|
|
|
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 !SLIC3R_OPENGL_ES
|
|
if (OpenGLManager::get_gl_info().is_core_profile()) {
|
|
#endif // !SLIC3R_OPENGL_ES
|
|
glsafe(::glGenVertexArrays(1, &m_render_data.vao_id));
|
|
glsafe(::glBindVertexArray(m_render_data.vao_id));
|
|
#if !SLIC3R_OPENGL_ES
|
|
}
|
|
#endif // !SLIC3R_OPENGL_ES
|
|
|
|
// 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();
|
|
data.vertices = std::vector<float>();
|
|
|
|
// indices
|
|
glsafe(::glGenBuffers(1, &m_render_data.ibo_id));
|
|
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_render_data.ibo_id));
|
|
const size_t indices_count = data.indices.size();
|
|
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));
|
|
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
|
}
|
|
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));
|
|
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
|
}
|
|
else {
|
|
data.index_type = Geometry::EIndexType::UINT;
|
|
glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.indices_size_bytes(), data.indices.data(), GL_STATIC_DRAW));
|
|
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
|
}
|
|
m_render_data.indices_count = indices_count;
|
|
data.indices = std::vector<unsigned int>();
|
|
|
|
#if !SLIC3R_OPENGL_ES
|
|
if (OpenGLManager::get_gl_info().is_core_profile()) {
|
|
#endif // !SLIC3R_OPENGL_ES
|
|
glsafe(::glBindVertexArray(0));
|
|
#if !SLIC3R_OPENGL_ES
|
|
}
|
|
#endif // !SLIC3R_OPENGL_ES
|
|
|
|
return true;
|
|
}
|
|
|
|
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.printable_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.printable_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.printable_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.printable_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.printable_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;
|
|
}
|
|
}
|
|
|
|
GLModel::Geometry stilized_arrow(unsigned int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height)
|
|
{
|
|
resolution = std::max<unsigned int>(4, resolution);
|
|
|
|
GLModel::Geometry data;
|
|
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
|
|
data.reserve_vertices(6 * resolution + 2);
|
|
data.reserve_indices(6 * resolution * 3);
|
|
|
|
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
|
|
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);
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
GLModel::Geometry circular_arrow(unsigned int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness)
|
|
{
|
|
resolution = std::max<unsigned int>(2, resolution);
|
|
|
|
GLModel::Geometry data;
|
|
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
|
|
data.reserve_vertices(8 * (resolution + 1) + 30);
|
|
data.reserve_indices((8 * resolution + 16) * 3);
|
|
|
|
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);
|
|
|
|
// 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);
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
GLModel::Geometry straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness)
|
|
{
|
|
GLModel::Geometry data;
|
|
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
|
|
data.reserve_vertices(42);
|
|
data.reserve_indices(72);
|
|
|
|
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;
|
|
|
|
// 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);
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
GLModel::Geometry diamond(unsigned int resolution)
|
|
{
|
|
resolution = std::max<unsigned int>(4, resolution);
|
|
|
|
GLModel::Geometry data;
|
|
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
|
|
data.reserve_vertices(resolution + 2);
|
|
data.reserve_indices((2 * (resolution + 1)) * 3);
|
|
|
|
const float step = 2.0f * float(PI) / float(resolution);
|
|
|
|
// 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);
|
|
|
|
return data;
|
|
}
|
|
|
|
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(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](float radius) {
|
|
std::vector<Vec3f> ret;
|
|
ret.reserve(sectorCount);
|
|
for (unsigned int i = 0; i < sectorCount; ++i) {
|
|
// from 0 to 2pi
|
|
const float sectorAngle = sectorStep * i;
|
|
ret.emplace_back(radius * std::cos(sectorAngle), radius * std::sin(sectorAngle), 0.0f);
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
const std::vector<Vec3f> base_vertices = generate_vertices_on_circle(radius);
|
|
const Vec3f h = height * Vec3f::UnitZ();
|
|
|
|
// 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 = -Vec3f::UnitZ();
|
|
|
|
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;
|
|
}
|
|
|
|
GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness)
|
|
{
|
|
const unsigned int torus_sector_count = std::max<unsigned int>(4, primary_resolution);
|
|
const float torus_sector_step = 2.0f * float(M_PI) / float(torus_sector_count);
|
|
const unsigned int section_sector_count = std::max<unsigned int>(4, secondary_resolution);
|
|
const float section_sector_step = 2.0f * float(M_PI) / float(section_sector_count);
|
|
|
|
GLModel::Geometry data;
|
|
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
|
|
data.reserve_vertices(torus_sector_count * section_sector_count);
|
|
data.reserve_indices(torus_sector_count * section_sector_count * 2 * 3);
|
|
|
|
// vertices
|
|
for (unsigned int i = 0; i < torus_sector_count; ++i) {
|
|
const float section_angle = torus_sector_step * i;
|
|
const float csa = std::cos(section_angle);
|
|
const float ssa = std::sin(section_angle);
|
|
const Vec3f section_center(radius * csa, radius * ssa, 0.0f);
|
|
for (unsigned int j = 0; j < section_sector_count; ++j) {
|
|
const float circle_angle = section_sector_step * j;
|
|
const float thickness_xy = thickness * std::cos(circle_angle);
|
|
const float thickness_z = thickness * std::sin(circle_angle);
|
|
const Vec3f v(thickness_xy * csa, thickness_xy * ssa, thickness_z);
|
|
data.add_vertex(section_center + v, (Vec3f)v.normalized());
|
|
}
|
|
}
|
|
|
|
// triangles
|
|
for (unsigned int i = 0; i < torus_sector_count; ++i) {
|
|
const unsigned int ii = i * section_sector_count;
|
|
const unsigned int ii_next = ((i + 1) % torus_sector_count) * section_sector_count;
|
|
for (unsigned int j = 0; j < section_sector_count; ++j) {
|
|
const unsigned int j_next = (j + 1) % section_sector_count;
|
|
const unsigned int i0 = ii + j;
|
|
const unsigned int i1 = ii_next + j;
|
|
const unsigned int i2 = ii_next + j_next;
|
|
const unsigned int i3 = ii + j_next;
|
|
data.add_triangle(i0, i1, i2);
|
|
data.add_triangle(i0, i2, i3);
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector<int>& triangle_indices, float normal_offset)
|
|
{
|
|
GLModel::Geometry init_data;
|
|
init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
|
|
init_data.reserve_indices(3 * triangle_indices.size());
|
|
init_data.reserve_vertices(3 * triangle_indices.size());
|
|
unsigned int i = 0;
|
|
for (int idx : triangle_indices) {
|
|
Vec3f v0 = its.vertices[its.indices[idx][0]];
|
|
Vec3f v1 = its.vertices[its.indices[idx][1]];
|
|
Vec3f v2 = its.vertices[its.indices[idx][2]];
|
|
const Vec3f n = (v1 - v0).cross(v2 - v0).normalized();
|
|
if (std::abs(normal_offset) > 0.0) {
|
|
v0 = v0 + n * normal_offset;
|
|
v1 = v1 + n * normal_offset;
|
|
v2 = v2 + n * normal_offset;
|
|
}
|
|
init_data.add_vertex(v0, n);
|
|
init_data.add_vertex(v1, n);
|
|
init_data.add_vertex(v2, n);
|
|
init_data.add_triangle(i, i + 1, i + 2);
|
|
i += 3;
|
|
}
|
|
|
|
return init_data;
|
|
}
|
|
|
|
GLModel::Geometry init_torus_data(unsigned int primary_resolution,
|
|
unsigned int secondary_resolution,
|
|
const Vec3f & center,
|
|
float radius,
|
|
float thickness,
|
|
const Vec3f & model_axis,
|
|
const Transform3f &world_trafo)
|
|
{
|
|
const unsigned int torus_sector_count = std::max<unsigned int>(4, primary_resolution);
|
|
const unsigned int section_sector_count = std::max<unsigned int>(4, secondary_resolution);
|
|
const float torus_sector_step = 2.0f * float(M_PI) / float(torus_sector_count);
|
|
const float section_sector_step = 2.0f * float(M_PI) / float(section_sector_count);
|
|
|
|
GLModel::Geometry data;
|
|
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
|
|
data.reserve_vertices(torus_sector_count * section_sector_count);
|
|
data.reserve_indices(torus_sector_count * section_sector_count * 2 * 3);
|
|
|
|
// vertices
|
|
const Transform3f local_to_world_matrix = world_trafo * Geometry::translation_transform(center.cast<double>()).cast<float>() *
|
|
Eigen::Quaternion<float>::FromTwoVectors(Vec3f::UnitZ(), model_axis);
|
|
for (unsigned int i = 0; i < torus_sector_count; ++i) {
|
|
const float section_angle = torus_sector_step * i;
|
|
const Vec3f radius_dir(std::cos(section_angle), std::sin(section_angle), 0.0f);
|
|
const Vec3f local_section_center = radius * radius_dir;
|
|
const Vec3f world_section_center = local_to_world_matrix * local_section_center;
|
|
const Vec3f local_section_normal = local_section_center.normalized().cross(Vec3f::UnitZ()).normalized();
|
|
const Vec3f world_section_normal = (Vec3f) (local_to_world_matrix.matrix().block(0, 0, 3, 3) * local_section_normal).normalized();
|
|
const Vec3f base_v = thickness * radius_dir;
|
|
for (unsigned int j = 0; j < section_sector_count; ++j) {
|
|
const Vec3f v = Eigen::AngleAxisf(section_sector_step * j, world_section_normal) * base_v;
|
|
data.add_vertex(world_section_center + v, (Vec3f) v.normalized());
|
|
}
|
|
}
|
|
|
|
// triangles
|
|
for (unsigned int i = 0; i < torus_sector_count; ++i) {
|
|
const unsigned int ii = i * section_sector_count;
|
|
const unsigned int ii_next = ((i + 1) % torus_sector_count) * section_sector_count;
|
|
for (unsigned int j = 0; j < section_sector_count; ++j) {
|
|
const unsigned int j_next = (j + 1) % section_sector_count;
|
|
const unsigned int i0 = ii + j;
|
|
const unsigned int i1 = ii_next + j;
|
|
const unsigned int i2 = ii_next + j_next;
|
|
const unsigned int i3 = ii + j_next;
|
|
data.add_triangle(i0, i1, i2);
|
|
data.add_triangle(i0, i2, i3);
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
} // namespace GUI
|
|
} // namespace Slic3r
|