Tech ENABLE_RAYCAST_PICKING - 1st installment - Raytraced picking of printbed
This commit is contained in:
parent
8f40270f93
commit
cfc3988b9f
13 changed files with 741 additions and 2 deletions
|
@ -351,12 +351,22 @@ Transform3d rotation_transform(const Vec3d& rotation)
|
|||
return transform;
|
||||
}
|
||||
|
||||
void scale_transform(Transform3d& transform, double scale)
|
||||
{
|
||||
return scale_transform(transform, scale * Vec3d::Ones());
|
||||
}
|
||||
|
||||
void scale_transform(Transform3d& transform, const Vec3d& scale)
|
||||
{
|
||||
transform = Transform3d::Identity();
|
||||
transform.scale(scale);
|
||||
}
|
||||
|
||||
Transform3d scale_transform(double scale)
|
||||
{
|
||||
return scale_transform(scale * Vec3d::Ones());
|
||||
}
|
||||
|
||||
Transform3d scale_transform(const Vec3d& scale)
|
||||
{
|
||||
Transform3d transform;
|
||||
|
|
|
@ -366,9 +366,11 @@ void rotation_transform(Transform3d& transform, const Vec3d& rotation);
|
|||
Transform3d rotation_transform(const Vec3d& rotation);
|
||||
|
||||
// Sets the given transform by assembling the given scale factors
|
||||
void scale_transform(Transform3d& transform, double scale);
|
||||
void scale_transform(Transform3d& transform, const Vec3d& scale);
|
||||
|
||||
// Returns the transform obtained by assembling the given scale factors
|
||||
Transform3d scale_transform(double scale);
|
||||
Transform3d scale_transform(const Vec3d& scale);
|
||||
|
||||
// Returns the euler angles extracted from the given rotation matrix
|
||||
|
|
|
@ -83,5 +83,9 @@
|
|||
#define ENABLE_GIZMO_GRABBER_REFACTOR (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable copy of custom bed model and texture
|
||||
#define ENABLE_COPY_CUSTOM_BED_MODEL_AND_TEXTURE (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable picking using raytracing
|
||||
#define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
||||
#define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING)
|
||||
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
|
|
|
@ -27,6 +27,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/GLShader.hpp
|
||||
GUI/GLCanvas3D.hpp
|
||||
GUI/GLCanvas3D.cpp
|
||||
GUI/SceneRaycaster.hpp
|
||||
GUI/SceneRaycaster.cpp
|
||||
GUI/OpenGLManager.hpp
|
||||
GUI/OpenGLManager.cpp
|
||||
GUI/Selection.hpp
|
||||
|
|
|
@ -24,7 +24,9 @@
|
|||
|
||||
static const float GROUND_Z = -0.02f;
|
||||
static const Slic3r::ColorRGBA DEFAULT_MODEL_COLOR = Slic3r::ColorRGBA::DARK_GRAY();
|
||||
#if !ENABLE_RAYCAST_PICKING
|
||||
static const Slic3r::ColorRGBA PICKING_MODEL_COLOR = Slic3r::ColorRGBA::BLACK();
|
||||
#endif // !ENABLE_RAYCAST_PICKING
|
||||
static const Slic3r::ColorRGBA DEFAULT_SOLID_GRID_COLOR = { 0.9f, 0.9f, 0.9f, 1.0f };
|
||||
static const Slic3r::ColorRGBA DEFAULT_TRANSPARENT_GRID_COLOR = { 0.9f, 0.9f, 0.9f, 0.6f };
|
||||
|
||||
|
@ -249,6 +251,11 @@ bool Bed3D::set_shape(const Pointfs& bed_shape, const double max_print_height, c
|
|||
m_axes.set_origin({ 0.0, 0.0, static_cast<double>(GROUND_Z) });
|
||||
m_axes.set_stem_length(0.1f * static_cast<float>(m_build_volume.bounding_volume().max_size()));
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
// unregister from picking
|
||||
wxGetApp().plater()->canvas3D()->remove_all_picking_raycasters(SceneRaycaster::EType::Bed);
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
// Let the calee to update the UI.
|
||||
return true;
|
||||
}
|
||||
|
@ -266,13 +273,19 @@ Point Bed3D::point_projection(const Point& point) const
|
|||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
void Bed3D::render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes, bool show_texture)
|
||||
{
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
render_internal(canvas, view_matrix, projection_matrix, bottom, scale_factor, show_axes, show_texture);
|
||||
#else
|
||||
render_internal(canvas, view_matrix, projection_matrix, bottom, scale_factor, show_axes, show_texture, false);
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
}
|
||||
|
||||
#if !ENABLE_RAYCAST_PICKING
|
||||
void Bed3D::render_for_picking(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor)
|
||||
{
|
||||
render_internal(canvas, view_matrix, projection_matrix, bottom, scale_factor, false, false, true);
|
||||
}
|
||||
#endif // !ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes, bool show_texture)
|
||||
{
|
||||
|
@ -286,8 +299,13 @@ void Bed3D::render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_fact
|
|||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
void Bed3D::render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor,
|
||||
bool show_axes, bool show_texture)
|
||||
#else
|
||||
void Bed3D::render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor,
|
||||
bool show_axes, bool show_texture, bool picking)
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
|
||||
bool show_axes, bool show_texture, bool picking)
|
||||
|
@ -301,7 +319,9 @@ void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
|
|||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
#if !ENABLE_RAYCAST_PICKING
|
||||
m_model.set_color(picking ? PICKING_MODEL_COLOR : DEFAULT_MODEL_COLOR);
|
||||
#endif // !ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
m_model.set_color(-1, picking ? PICKING_MODEL_COLOR : DEFAULT_MODEL_COLOR);
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
@ -311,7 +331,11 @@ void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
|
|||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
case Type::System: { render_system(canvas, view_matrix, projection_matrix, bottom, show_texture); break; }
|
||||
default:
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
case Type::Custom: { render_custom(canvas, view_matrix, projection_matrix, bottom, show_texture); break; }
|
||||
#else
|
||||
case Type::Custom: { render_custom(canvas, view_matrix, projection_matrix, bottom, show_texture, picking); break; }
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
case Type::System: { render_system(canvas, bottom, show_texture); break; }
|
||||
default:
|
||||
|
@ -341,7 +365,11 @@ BoundingBoxf3 Bed3D::calc_extended_bounding_box() const
|
|||
out.merge(out.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, out.max.z()));
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
// extend to contain model, if any
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
BoundingBoxf3 model_bb = m_model.model.get_bounding_box();
|
||||
#else
|
||||
BoundingBoxf3 model_bb = m_model.get_bounding_box();
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
if (model_bb.defined) {
|
||||
model_bb.translate(m_model_offset);
|
||||
out.merge(model_bb);
|
||||
|
@ -391,7 +419,16 @@ void Bed3D::init_triangles()
|
|||
init_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
|
||||
}
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
if (m_model.model.get_filename().empty() && m_model.mesh_raycaster == nullptr)
|
||||
// register for picking
|
||||
register_raycasters_for_picking(init_data, Transform3d::Identity());
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
m_triangles.init_from(std::move(init_data));
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
m_triangles.set_color(DEFAULT_MODEL_COLOR);
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
}
|
||||
|
||||
void Bed3D::init_gridlines()
|
||||
|
@ -581,7 +618,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
|
|||
if (m_texture_filename.empty()) {
|
||||
m_texture.reset();
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
render_default(bottom, true, view_matrix, projection_matrix);
|
||||
#else
|
||||
render_default(bottom, false, true, view_matrix, projection_matrix);
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
render_default(bottom, false, true);
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
@ -598,7 +639,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
|
|||
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
|
||||
if (!m_temp_texture.load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8)) {
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
render_default(bottom, true, view_matrix, projection_matrix);
|
||||
#else
|
||||
render_default(bottom, false, true, view_matrix, projection_matrix);
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
render_default(bottom, false, true);
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
@ -610,7 +655,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
|
|||
// starts generating the main texture, compression will run asynchronously
|
||||
if (!m_texture.load_from_svg_file(m_texture_filename, true, true, true, max_tex_size)) {
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
render_default(bottom, true, view_matrix, projection_matrix);
|
||||
#else
|
||||
render_default(bottom, false, true, view_matrix, projection_matrix);
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
render_default(bottom, false, true);
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
@ -622,7 +671,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
|
|||
if (m_temp_texture.get_id() == 0 || m_temp_texture.get_source() != m_texture_filename) {
|
||||
if (!m_temp_texture.load_from_file(m_texture_filename, false, GLTexture::None, false)) {
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
render_default(bottom, true, view_matrix, projection_matrix);
|
||||
#else
|
||||
render_default(bottom, false, true, view_matrix, projection_matrix);
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
render_default(bottom, false, true);
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
@ -634,7 +687,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
|
|||
// starts generating the main texture, compression will run asynchronously
|
||||
if (!m_texture.load_from_file(m_texture_filename, true, GLTexture::MultiThreaded, true)) {
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
render_default(bottom, true, view_matrix, projection_matrix);
|
||||
#else
|
||||
render_default(bottom, false, true, view_matrix, projection_matrix);
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
render_default(bottom, false, true);
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
@ -643,7 +700,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
|
|||
}
|
||||
else {
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
render_default(bottom, true, view_matrix, projection_matrix);
|
||||
#else
|
||||
render_default(bottom, false, true, view_matrix, projection_matrix);
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
render_default(bottom, false, true);
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
@ -771,9 +832,17 @@ void Bed3D::render_model()
|
|||
if (m_model_filename.empty())
|
||||
return;
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
if (m_model.model.get_filename() != m_model_filename && m_model.model.init_from_file(m_model_filename)) {
|
||||
#else
|
||||
if (m_model.get_filename() != m_model_filename && m_model.init_from_file(m_model_filename)) {
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
m_model.model.set_color(DEFAULT_MODEL_COLOR);
|
||||
#else
|
||||
m_model.set_color(DEFAULT_MODEL_COLOR);
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
m_model.set_color(-1, DEFAULT_MODEL_COLOR);
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
@ -781,11 +850,20 @@ void Bed3D::render_model()
|
|||
// move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad
|
||||
m_model_offset = to_3d(m_build_volume.bounding_volume2d().center(), -0.03);
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
// register for picking
|
||||
register_raycasters_for_picking(m_model.model.get_geometry(), Geometry::assemble_transform(m_model_offset));
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
// update extended bounding box
|
||||
m_extended_bounding_box = this->calc_extended_bounding_box();
|
||||
}
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
if (!m_model.model.get_filename().empty()) {
|
||||
#else
|
||||
if (!m_model.get_filename().empty()) {
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
|
@ -800,7 +878,11 @@ void Bed3D::render_model()
|
|||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(m_model_offset.x(), m_model_offset.y(), m_model_offset.z()));
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
m_model.model.render();
|
||||
#else
|
||||
m_model.render();
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#if !ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
glsafe(::glPopMatrix());
|
||||
#endif // !ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
@ -810,14 +892,22 @@ void Bed3D::render_model()
|
|||
}
|
||||
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
void Bed3D::render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture)
|
||||
#else
|
||||
void Bed3D::render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture, bool picking)
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture, bool picking)
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
{
|
||||
if (m_texture_filename.empty() && m_model_filename.empty()) {
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
render_default(bottom, show_texture, view_matrix, projection_matrix);
|
||||
#else
|
||||
render_default(bottom, picking, show_texture, view_matrix, projection_matrix);
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
render_default(bottom, picking, show_texture);
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
@ -844,7 +934,11 @@ void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture, bo
|
|||
}
|
||||
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
void Bed3D::render_default(bool bottom, bool show_texture, const Transform3d& view_matrix, const Transform3d& projection_matrix)
|
||||
#else
|
||||
void Bed3D::render_default(bool bottom, bool picking, bool show_texture, const Transform3d& view_matrix, const Transform3d& projection_matrix)
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
void Bed3D::render_default(bool bottom, bool picking, bool show_texture)
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
@ -866,16 +960,35 @@ void Bed3D::render_default(bool bottom, bool picking, bool show_texture)
|
|||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
const bool has_model = !m_model.model.get_filename().empty();
|
||||
#else
|
||||
const bool has_model = !m_model.get_filename().empty();
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
if (!has_model && !bottom) {
|
||||
// draw background
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
#if !ENABLE_RAYCAST_PICKING
|
||||
m_triangles.set_color(picking ? PICKING_MODEL_COLOR : DEFAULT_MODEL_COLOR);
|
||||
#endif // !ENABLE_RAYCAST_PICKING
|
||||
m_triangles.render();
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
}
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
if (show_texture) {
|
||||
// draw grid
|
||||
#if ENABLE_GL_CORE_PROFILE
|
||||
if (!OpenGLManager::get_gl_info().is_core_profile())
|
||||
#endif // ENABLE_GL_CORE_PROFILE
|
||||
glsafe(::glLineWidth(1.5f * m_scale_factor));
|
||||
m_gridlines.set_color(has_model && !bottom ? DEFAULT_SOLID_GRID_COLOR : DEFAULT_TRANSPARENT_GRID_COLOR);
|
||||
m_gridlines.render();
|
||||
}
|
||||
else
|
||||
render_contour(view_matrix, projection_matrix);
|
||||
#else
|
||||
if (!picking && show_texture) {
|
||||
// draw grid
|
||||
#if ENABLE_GL_CORE_PROFILE
|
||||
|
@ -887,6 +1000,7 @@ void Bed3D::render_default(bool bottom, bool picking, bool show_texture)
|
|||
}
|
||||
else if (!show_texture)
|
||||
render_contour(view_matrix, projection_matrix);
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
|
||||
|
@ -979,5 +1093,26 @@ void Bed3D::release_VBOs()
|
|||
}
|
||||
#endif // !ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
void Bed3D::register_raycasters_for_picking(const GLModel::Geometry& geometry, const Transform3d& trafo)
|
||||
{
|
||||
assert(m_model.mesh_raycaster == nullptr);
|
||||
|
||||
indexed_triangle_set its;
|
||||
its.vertices.reserve(geometry.vertices_count());
|
||||
for (size_t i = 0; i < geometry.vertices_count(); ++i) {
|
||||
its.vertices.emplace_back(geometry.extract_position_3(i));
|
||||
}
|
||||
its.indices.reserve(geometry.indices_count() / 3);
|
||||
for (size_t i = 0; i < geometry.indices_count() / 3; ++i) {
|
||||
const size_t tri_id = i * 3;
|
||||
its.indices.emplace_back(geometry.extract_index(tri_id), geometry.extract_index(tri_id + 1), geometry.extract_index(tri_id + 2));
|
||||
}
|
||||
|
||||
m_model.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
|
||||
wxGetApp().plater()->canvas3D()->add_raycaster_for_picking(SceneRaycaster::EType::Bed, 0, *m_model.mesh_raycaster, trafo);
|
||||
}
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
#else
|
||||
#include "GLModel.hpp"
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
#include "MeshUtils.hpp"
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
#include "libslic3r/BuildVolume.hpp"
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
@ -108,7 +111,11 @@ private:
|
|||
GLTexture m_texture;
|
||||
// temporary texture shown until the main texture has still no levels compressed
|
||||
GLTexture m_temp_texture;
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
PickingModel m_model;
|
||||
#else
|
||||
GLModel m_model;
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
Vec3d m_model_offset{ Vec3d::Zero() };
|
||||
#if !ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
unsigned int m_vbo_id{ 0 };
|
||||
|
@ -153,7 +160,9 @@ public:
|
|||
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
void render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes, bool show_texture);
|
||||
#if !ENABLE_RAYCAST_PICKING
|
||||
void render_for_picking(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor);
|
||||
#endif // !ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
void render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes, bool show_texture);
|
||||
void render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_factor);
|
||||
|
@ -173,8 +182,13 @@ private:
|
|||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
static std::tuple<Type, std::string, std::string> detect_type(const Pointfs& shape);
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
void render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor,
|
||||
bool show_axes, bool show_texture);
|
||||
#else
|
||||
void render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor,
|
||||
bool show_axes, bool show_texture, bool picking);
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
void render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
|
||||
bool show_axes, bool show_texture, bool picking);
|
||||
|
@ -184,8 +198,13 @@ private:
|
|||
void render_system(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture);
|
||||
void render_texture(bool bottom, GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix);
|
||||
void render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix);
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
void render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture);
|
||||
void render_default(bool bottom, bool show_texture, const Transform3d& view_matrix, const Transform3d& projection_matrix);
|
||||
#else
|
||||
void render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture, bool picking);
|
||||
void render_default(bool bottom, bool picking, bool show_texture, const Transform3d& view_matrix, const Transform3d& projection_matrix);
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
void render_contour(const Transform3d& view_matrix, const Transform3d& projection_matrix);
|
||||
#else
|
||||
void render_system(GLCanvas3D& canvas, bool bottom, bool show_texture);
|
||||
|
@ -197,6 +216,10 @@ private:
|
|||
|
||||
void release_VBOs();
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
void register_raycasters_for_picking(const GLModel::Geometry& geometry, const Transform3d& trafo);
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
};
|
||||
|
||||
} // GUI
|
||||
|
|
|
@ -1664,6 +1664,14 @@ void GLCanvas3D::render()
|
|||
else if (!m_volumes.empty())
|
||||
// regular picking pass
|
||||
_picking_pass();
|
||||
#if ENABLE_RAYCAST_PICKING_DEBUG
|
||||
else {
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
imgui.begin(std::string("Hit result"), ImGuiWindowFlags_AlwaysAutoResize);
|
||||
imgui.text("Picking disabled");
|
||||
imgui.end();
|
||||
}
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
}
|
||||
|
||||
#if ENABLE_RENDER_PICKING_PASS
|
||||
|
@ -1718,6 +1726,11 @@ void GLCanvas3D::render()
|
|||
}
|
||||
#endif // ENABLE_RENDER_PICKING_PASS
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING_DEBUG
|
||||
if (m_picking_enabled && !m_mouse.dragging)
|
||||
m_scene_raycaster.render_hit(camera);
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
|
||||
#if ENABLE_SHOW_CAMERA_TARGET
|
||||
_render_camera_target();
|
||||
#endif // ENABLE_SHOW_CAMERA_TARGET
|
||||
|
@ -5392,6 +5405,108 @@ void GLCanvas3D::_refresh_if_shown_on_screen()
|
|||
}
|
||||
}
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
void GLCanvas3D::_picking_pass()
|
||||
{
|
||||
if (!m_picking_enabled || m_mouse.dragging || m_mouse.position == Vec2d(DBL_MAX, DBL_MAX))
|
||||
return;
|
||||
|
||||
m_hover_volume_idxs.clear();
|
||||
|
||||
const ClippingPlane clipping_plane = m_gizmos.get_clipping_plane().inverted_normal();
|
||||
const SceneRaycaster::HitResult hit = m_scene_raycaster.hit(m_mouse.position, wxGetApp().plater()->get_camera(), &clipping_plane);
|
||||
if (hit.is_valid()) {
|
||||
switch (hit.type)
|
||||
{
|
||||
case SceneRaycaster::EType::Volume:
|
||||
{
|
||||
if (0 <= hit.raycaster_id && hit.raycaster_id < (int)m_volumes.volumes.size()) {
|
||||
const GLVolume* volume = m_volumes.volumes[hit.raycaster_id];
|
||||
if (volume->is_active && !volume->disabled && (volume->composite_id.volume_id >= 0 || m_render_sla_auxiliaries)) {
|
||||
// do not add the volume id if any gizmo is active and CTRL is pressed
|
||||
if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL)) {
|
||||
m_hover_volume_idxs.emplace_back(hit.raycaster_id);
|
||||
m_gizmos.set_hover_id(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
assert(false);
|
||||
|
||||
break;
|
||||
}
|
||||
case SceneRaycaster::EType::Gizmo:
|
||||
{
|
||||
const Size& cnv_size = get_canvas_size();
|
||||
bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() &&
|
||||
0 <= m_mouse.position.y() && m_mouse.position.y() < cnv_size.get_height();
|
||||
m_gizmos.set_hover_id(inside ? hit.raycaster_id : -1);
|
||||
break;
|
||||
}
|
||||
case SceneRaycaster::EType::Bed:
|
||||
{
|
||||
m_gizmos.set_hover_id(-1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
m_gizmos.set_hover_id(-1);
|
||||
|
||||
_update_volumes_hover_state();
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING_DEBUG
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
imgui.begin(std::string("Hit result"), ImGuiWindowFlags_AlwaysAutoResize);
|
||||
std::string object_type = "None";
|
||||
switch (hit.type)
|
||||
{
|
||||
case SceneRaycaster::EType::Bed: { object_type = "Bed"; break; }
|
||||
case SceneRaycaster::EType::Gizmo: { object_type = "Gizmo element"; break; }
|
||||
case SceneRaycaster::EType::Volume:
|
||||
{
|
||||
if (m_volumes.volumes[hit.raycaster_id]->is_wipe_tower)
|
||||
object_type = "Wipe tower";
|
||||
else if (m_volumes.volumes[hit.raycaster_id]->volume_idx() == -int(slaposPad))
|
||||
object_type = "SLA pad";
|
||||
else if (m_volumes.volumes[hit.raycaster_id]->volume_idx() == -int(slaposSupportTree))
|
||||
object_type = "SLA supports";
|
||||
else
|
||||
object_type = "Volume";
|
||||
break;
|
||||
}
|
||||
}
|
||||
char buf[1024];
|
||||
if (hit.type != SceneRaycaster::EType::None) {
|
||||
sprintf(buf, "Object ID: %d", hit.raycaster_id);
|
||||
imgui.text(std::string(buf));
|
||||
sprintf(buf, "Type: %s", object_type.c_str());
|
||||
imgui.text(std::string(buf));
|
||||
sprintf(buf, "Position: %.3f, %.3f, %.3f", hit.position.x(), hit.position.y(), hit.position.z());
|
||||
imgui.text(std::string(buf));
|
||||
sprintf(buf, "Normal: %.3f, %.3f, %.3f", hit.normal.x(), hit.normal.y(), hit.normal.z());
|
||||
imgui.text(std::string(buf));
|
||||
}
|
||||
else
|
||||
imgui.text("NO HIT");
|
||||
|
||||
ImGui::Separator();
|
||||
imgui.text("Registered for picking:");
|
||||
sprintf(buf, "Beds: %d", (int)m_scene_raycaster.beds_count());
|
||||
imgui.text(std::string(buf));
|
||||
sprintf(buf, "Volumes: %d", (int)m_scene_raycaster.volumes_count());
|
||||
imgui.text(std::string(buf));
|
||||
sprintf(buf, "Gizmo elements: %d", (int)m_scene_raycaster.gizmos_count());
|
||||
imgui.text(std::string(buf));
|
||||
imgui.end();
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
}
|
||||
#else
|
||||
void GLCanvas3D::_picking_pass()
|
||||
{
|
||||
if (m_picking_enabled && !m_mouse.dragging && m_mouse.position != Vec2d(DBL_MAX, DBL_MAX)) {
|
||||
|
@ -5466,6 +5581,7 @@ void GLCanvas3D::_picking_pass()
|
|||
_update_volumes_hover_state();
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
void GLCanvas3D::_rectangular_selection_picking_pass()
|
||||
{
|
||||
|
@ -5485,8 +5601,10 @@ void GLCanvas3D::_rectangular_selection_picking_pass()
|
|||
|
||||
_render_volumes_for_picking();
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
#if !ENABLE_RAYCAST_PICKING
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
_render_bed_for_picking(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward());
|
||||
#endif // !ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
_render_bed_for_picking(!wxGetApp().plater()->get_camera().is_looking_downward());
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
@ -5651,6 +5769,7 @@ void GLCanvas3D::_render_bed(bool bottom, bool show_axes)
|
|||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
}
|
||||
|
||||
#if !ENABLE_RAYCAST_PICKING
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
void GLCanvas3D::_render_bed_for_picking(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom)
|
||||
#else
|
||||
|
@ -5668,6 +5787,7 @@ void GLCanvas3D::_render_bed_for_picking(bool bottom)
|
|||
m_bed.render_for_picking(*this, bottom, scale_factor);
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
}
|
||||
#endif // !ENABLE_RAYCAST_PICKING
|
||||
|
||||
void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
|
||||
{
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||
#include "GCodeViewer.hpp"
|
||||
#include "Camera.hpp"
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
#include "SceneRaycaster.hpp"
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
#include "libslic3r/Slicing.hpp"
|
||||
|
||||
|
@ -479,6 +482,9 @@ public:
|
|||
private:
|
||||
wxGLCanvas* m_canvas;
|
||||
wxGLContext* m_context;
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
SceneRaycaster m_scene_raycaster;
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
Bed3D &m_bed;
|
||||
#if ENABLE_RETINA_GL
|
||||
std::unique_ptr<RetinaHelper> m_retina_helper;
|
||||
|
@ -656,6 +662,15 @@ public:
|
|||
bool init();
|
||||
void post_event(wxEvent &&event);
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
int add_raycaster_for_picking(SceneRaycaster::EType type, int id, const MeshRaycaster& raycaster, const Transform3d& trafo) {
|
||||
return m_scene_raycaster.add_raycaster(type, id, raycaster, trafo);
|
||||
}
|
||||
void remove_all_picking_raycasters(SceneRaycaster::EType type) {
|
||||
m_scene_raycaster.reset(type);
|
||||
}
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
void set_as_dirty();
|
||||
void requires_check_outside_state() { m_requires_check_outside_state = true; }
|
||||
|
||||
|
@ -958,7 +973,9 @@ private:
|
|||
void _render_background();
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
void _render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes);
|
||||
#if !ENABLE_RAYCAST_PICKING
|
||||
void _render_bed_for_picking(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom);
|
||||
#endif // !ENABLE_RAYCAST_PICKING
|
||||
#else
|
||||
void _render_bed(bool bottom, bool show_axes);
|
||||
void _render_bed_for_picking(bool bottom);
|
||||
|
|
|
@ -351,7 +351,11 @@ void Raycaster::on_update()
|
|||
if (meshes != m_old_meshes) {
|
||||
m_raycasters.clear();
|
||||
for (const TriangleMesh* mesh : meshes)
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
m_raycasters.emplace_back(new MeshRaycaster(std::make_shared<const TriangleMesh>(*mesh)));
|
||||
#else
|
||||
m_raycasters.emplace_back(new MeshRaycaster(*mesh));
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
m_old_meshes = meshes;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -350,6 +350,40 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
|
|||
return out;
|
||||
}
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
bool MeshRaycaster::closest_hit(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
|
||||
Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane, size_t* facet_idx) const
|
||||
{
|
||||
Vec3d point;
|
||||
Vec3d direction;
|
||||
line_from_mouse_pos(mouse_pos, trafo, camera, point, direction);
|
||||
|
||||
const std::vector<sla::IndexedMesh::hit_result> hits = m_emesh.query_ray_hits(point, direction.normalized());
|
||||
|
||||
if (hits.empty())
|
||||
return false; // no intersection found
|
||||
|
||||
size_t hit_id = 0;
|
||||
if (clipping_plane != nullptr) {
|
||||
while (hit_id < hits.size() && clipping_plane->is_point_clipped(trafo * hits[hit_id].position())) {
|
||||
++hit_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (hit_id == hits.size())
|
||||
return false; // all points are obscured or cut by the clipping plane.
|
||||
|
||||
const sla::IndexedMesh::hit_result& hit = hits[hit_id];
|
||||
|
||||
position = hit.position().cast<float>();
|
||||
normal = hit.normal().cast<float>();
|
||||
|
||||
if (facet_idx != nullptr)
|
||||
*facet_idx = hit.face();
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
Vec3f MeshRaycaster::get_closest_point(const Vec3f& point, Vec3f* normal) const
|
||||
{
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
||||
#include <cfloat>
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
#include <memory>
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -57,6 +60,10 @@ public:
|
|||
void set_offset(double offset) { m_data[3] = offset; }
|
||||
double get_offset() const { return m_data[3]; }
|
||||
Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); }
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
void invert_normal() { m_data[0] *= -1.0; m_data[1] *= -1.0; m_data[2] *= -1.0; }
|
||||
ClippingPlane inverted_normal() const { return ClippingPlane(-get_normal(), get_offset()); }
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
bool is_active() const { return m_data[3] != DBL_MAX; }
|
||||
static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); }
|
||||
const std::array<double, 4>& get_data() const { return m_data; }
|
||||
|
@ -123,6 +130,14 @@ private:
|
|||
// whether certain points are visible or obscured by the mesh etc.
|
||||
class MeshRaycaster {
|
||||
public:
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
explicit MeshRaycaster(std::shared_ptr<const TriangleMesh> mesh)
|
||||
: m_mesh(mesh)
|
||||
, m_emesh(*mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length
|
||||
, m_normals(its_face_normals(mesh->its))
|
||||
{
|
||||
}
|
||||
#else
|
||||
// The class references extern TriangleMesh, which must stay alive
|
||||
// during MeshRaycaster existence.
|
||||
MeshRaycaster(const TriangleMesh& mesh)
|
||||
|
@ -130,6 +145,7 @@ public:
|
|||
, m_normals(its_face_normals(mesh.its))
|
||||
{
|
||||
}
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
|
||||
Vec3d& point, Vec3d& direction) const;
|
||||
|
@ -155,10 +171,24 @@ public:
|
|||
const ClippingPlane* clipping_plane = nullptr // clipping plane (if active)
|
||||
) const;
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
// Returns true if the ray, built from mouse position and camera direction, intersects the mesh.
|
||||
// In this case, position and normal contain the position and normal, in model coordinates, of the intersection closest to the camera,
|
||||
// depending on the position/orientation of the clipping_plane, if specified
|
||||
bool closest_hit(
|
||||
const Vec2d& mouse_pos,
|
||||
const Transform3d& trafo, // how to get the mesh into world coords
|
||||
const Camera& camera, // current camera position
|
||||
Vec3f& position, // where to save the positibon of the hit (mesh coords)
|
||||
Vec3f& normal, // normal of the triangle that was hit
|
||||
const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active)
|
||||
size_t* facet_idx = nullptr // index of the facet hit
|
||||
) const;
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
// Given a point in world coords, the method returns closest point on the mesh.
|
||||
// The output is in mesh coords.
|
||||
// normal* can be used to also get normal of the respective triangle.
|
||||
|
||||
Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const;
|
||||
|
||||
// Given a point in mesh coords, the method returns the closest facet from mesh.
|
||||
|
@ -167,11 +197,26 @@ public:
|
|||
Vec3f get_triangle_normal(size_t facet_idx) const;
|
||||
|
||||
private:
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
std::shared_ptr<const TriangleMesh> m_mesh;
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
sla::IndexedMesh m_emesh;
|
||||
std::vector<stl_normal> m_normals;
|
||||
};
|
||||
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
struct PickingModel
|
||||
{
|
||||
GLModel model;
|
||||
std::unique_ptr<MeshRaycaster> mesh_raycaster;
|
||||
|
||||
void reset() {
|
||||
model.reset();
|
||||
mesh_raycaster.reset();
|
||||
}
|
||||
};
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
222
src/slic3r/GUI/SceneRaycaster.cpp
Normal file
222
src/slic3r/GUI/SceneRaycaster.cpp
Normal file
|
@ -0,0 +1,222 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "SceneRaycaster.hpp"
|
||||
|
||||
#include "Camera.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
SceneRaycaster::SceneRaycaster() {
|
||||
#if ENABLE_RAYCAST_PICKING_DEBUG
|
||||
// hit point
|
||||
m_sphere.init_from(its_make_sphere(1.0, double(PI) / 16.0));
|
||||
m_sphere.set_color(ColorRGBA::YELLOW());
|
||||
|
||||
// hit normal
|
||||
GLModel::Geometry init_data;
|
||||
init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 };
|
||||
init_data.color = ColorRGBA::YELLOW();
|
||||
init_data.reserve_vertices(2);
|
||||
init_data.reserve_indices(2);
|
||||
|
||||
// vertices
|
||||
init_data.add_vertex((Vec3f)Vec3f::Zero());
|
||||
init_data.add_vertex((Vec3f)Vec3f::UnitZ());
|
||||
|
||||
// indices
|
||||
init_data.add_line(0, 1);
|
||||
|
||||
m_line.init_from(std::move(init_data));
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
}
|
||||
|
||||
int SceneRaycaster::add_raycaster(EType type, PickingId id, const MeshRaycaster& raycaster, const Transform3d& trafo)
|
||||
{
|
||||
switch (type) {
|
||||
case EType::Bed: {
|
||||
m_bed.emplace_back(encode_id(type, id), raycaster, trafo);
|
||||
return m_bed.size() - 1;
|
||||
}
|
||||
case EType::Volume: {
|
||||
m_volumes.emplace_back(encode_id(type, id), raycaster, trafo);
|
||||
return m_volumes.size() - 1;
|
||||
}
|
||||
case EType::Gizmo: {
|
||||
m_gizmos.emplace_back(encode_id(type, id), raycaster, trafo);
|
||||
return m_gizmos.size() - 1;
|
||||
}
|
||||
};
|
||||
|
||||
// signal error
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SceneRaycaster::set_raycaster_active_state(EType type, int id, bool active)
|
||||
{
|
||||
std::vector<SceneRaycasterItem>* raycasters = get_raycasters(type);
|
||||
for (SceneRaycasterItem& item : *raycasters) {
|
||||
if (item.get_id() == encode_id(type, id)) {
|
||||
item.set_active(active);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneRaycaster::set_raycaster_transform(EType type, int id, const Transform3d& trafo)
|
||||
{
|
||||
std::vector<SceneRaycasterItem>* raycasters = get_raycasters(type);
|
||||
for (SceneRaycasterItem& item : *raycasters) {
|
||||
if (item.get_id() == encode_id(type, id)) {
|
||||
item.set_transform(trafo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneRaycaster::remove_raycaster(EType type, int id)
|
||||
{
|
||||
std::vector<SceneRaycasterItem>* raycasters = get_raycasters(type);
|
||||
if (0 <= id && id < raycasters->size())
|
||||
raycasters->erase(raycasters->begin() + id);
|
||||
}
|
||||
|
||||
void SceneRaycaster::reset(EType type)
|
||||
{
|
||||
switch (type) {
|
||||
case EType::Bed: {
|
||||
m_bed.clear();
|
||||
break;
|
||||
}
|
||||
case EType::Volume: {
|
||||
m_volumes.clear();
|
||||
break;
|
||||
}
|
||||
case EType::Gizmo: {
|
||||
m_gizmos.clear();
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane)
|
||||
{
|
||||
double closest_hit_squared_distance = std::numeric_limits<double>::max();
|
||||
auto is_closest = [&closest_hit_squared_distance](const Camera& camera, const Vec3f& hit) {
|
||||
const double hit_squared_distance = (camera.get_position() - hit.cast<double>()).squaredNorm();
|
||||
const bool ret = hit_squared_distance < closest_hit_squared_distance;
|
||||
if (ret)
|
||||
closest_hit_squared_distance = hit_squared_distance;
|
||||
return ret;
|
||||
};
|
||||
|
||||
m_last_hit.reset();
|
||||
|
||||
HitResult ret;
|
||||
|
||||
auto test_raycasters = [&](EType type) {
|
||||
const ClippingPlane* clip_plane = (clipping_plane != nullptr && type == EType::Volume) ? clipping_plane : nullptr;
|
||||
const std::vector<SceneRaycasterItem>* raycasters = get_raycasters(type);
|
||||
HitResult current_hit = { type };
|
||||
for (const SceneRaycasterItem& item : *raycasters) {
|
||||
if (!item.is_active())
|
||||
continue;
|
||||
|
||||
current_hit.raycaster_id = item.get_id();
|
||||
const Transform3d& trafo = item.get_transform();
|
||||
if (item.get_raycaster()->closest_hit(mouse_pos, trafo, camera, current_hit.position, current_hit.normal, clip_plane)) {
|
||||
current_hit.position = (trafo * current_hit.position.cast<double>()).cast<float>();
|
||||
if (is_closest(camera, current_hit.position)) {
|
||||
const Transform3d matrix = camera.get_view_matrix() * trafo;
|
||||
const Matrix3d normal_matrix = (Matrix3d)trafo.matrix().block(0, 0, 3, 3).inverse().transpose();
|
||||
current_hit.normal = (normal_matrix * current_hit.normal.cast<double>()).normalized().cast<float>();
|
||||
ret = current_hit;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
test_raycasters(EType::Gizmo);
|
||||
if (!m_gizmos_on_top || ret.is_valid()) {
|
||||
if (camera.is_looking_downward())
|
||||
test_raycasters(EType::Bed);
|
||||
test_raycasters(EType::Volume);
|
||||
}
|
||||
|
||||
if (ret.is_valid())
|
||||
ret.raycaster_id = decode_id(ret.type, ret.raycaster_id);
|
||||
|
||||
m_last_hit = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING_DEBUG
|
||||
void SceneRaycaster::render_hit(const Camera& camera)
|
||||
{
|
||||
if (!m_last_hit.has_value() || !m_last_hit.value().is_valid())
|
||||
return;
|
||||
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("flat");
|
||||
shader->start_using();
|
||||
|
||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||
|
||||
const Transform3d sphere_view_model_matrix = camera.get_view_matrix() * Geometry::translation_transform(m_last_hit.value().position.cast<double>()) *
|
||||
Geometry::scale_transform(4.0 * camera.get_inv_zoom());
|
||||
shader->set_uniform("view_model_matrix", sphere_view_model_matrix);
|
||||
m_sphere.render();
|
||||
|
||||
Eigen::Quaterniond q;
|
||||
Transform3d m = Transform3d::Identity();
|
||||
m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3d::UnitZ(), m_last_hit.value().normal.cast<double>()).toRotationMatrix();
|
||||
|
||||
const Transform3d line_view_model_matrix = sphere_view_model_matrix * m * Geometry::scale_transform(6.25);
|
||||
shader->set_uniform("view_model_matrix", line_view_model_matrix);
|
||||
m_line.render();
|
||||
|
||||
shader->stop_using();
|
||||
}
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
|
||||
std::vector<SceneRaycasterItem>* SceneRaycaster::get_raycasters(EType type)
|
||||
{
|
||||
std::vector<SceneRaycasterItem>* ret = nullptr;
|
||||
switch (type)
|
||||
{
|
||||
case EType::Bed: { ret = &m_bed; break; }
|
||||
case EType::Volume: { ret = &m_volumes; break; }
|
||||
case EType::Gizmo: { ret = &m_gizmos; break; }
|
||||
}
|
||||
assert(ret != nullptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
PickingId SceneRaycaster::base_id(EType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EType::Bed: { return PickingId(EPickingIdBase::Bed); }
|
||||
case EType::Volume: { return PickingId(EPickingIdBase::Volume); }
|
||||
case EType::Gizmo: { return PickingId(EPickingIdBase::Gizmo); }
|
||||
};
|
||||
|
||||
assert(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
PickingId SceneRaycaster::encode_id(EType type, PickingId id)
|
||||
{
|
||||
return base_id(type) + id;
|
||||
}
|
||||
|
||||
PickingId SceneRaycaster::decode_id(EType type, PickingId id)
|
||||
{
|
||||
return id - base_id(type);
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
121
src/slic3r/GUI/SceneRaycaster.hpp
Normal file
121
src/slic3r/GUI/SceneRaycaster.hpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
#ifndef slic3r_SceneRaycaster_hpp_
|
||||
#define slic3r_SceneRaycaster_hpp_
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
|
||||
#include "MeshUtils.hpp"
|
||||
#include "GLModel.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
struct Camera;
|
||||
|
||||
using PickingId = int;
|
||||
|
||||
class SceneRaycasterItem
|
||||
{
|
||||
PickingId m_id{ -1 };
|
||||
bool m_active{ true };
|
||||
const MeshRaycaster* m_raycaster;
|
||||
Transform3d m_trafo;
|
||||
|
||||
public:
|
||||
SceneRaycasterItem(PickingId id, const MeshRaycaster& raycaster, const Transform3d& trafo)
|
||||
: m_id(id), m_raycaster(&raycaster), m_trafo(trafo)
|
||||
{}
|
||||
|
||||
PickingId get_id() const { return m_id; }
|
||||
bool is_active() const { return m_active; }
|
||||
void set_active(bool active) { m_active = active; }
|
||||
const MeshRaycaster* get_raycaster() const { return m_raycaster; }
|
||||
const Transform3d& get_transform() const { return m_trafo; }
|
||||
void set_transform(const Transform3d& trafo) { m_trafo = trafo; }
|
||||
};
|
||||
|
||||
class SceneRaycaster
|
||||
{
|
||||
public:
|
||||
enum class EType
|
||||
{
|
||||
None,
|
||||
Bed,
|
||||
Volume,
|
||||
Gizmo
|
||||
};
|
||||
|
||||
enum class EPickingIdBase
|
||||
{
|
||||
Bed = 0,
|
||||
Volume = 1000,
|
||||
Gizmo = 1000000
|
||||
};
|
||||
|
||||
struct HitResult
|
||||
{
|
||||
EType type{ EType::None };
|
||||
PickingId raycaster_id{ -1 };
|
||||
Vec3f position{ Vec3f::Zero() };
|
||||
Vec3f normal{ Vec3f::Zero() };
|
||||
|
||||
bool is_valid() const { return raycaster_id != -1; }
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<SceneRaycasterItem> m_bed;
|
||||
std::vector<SceneRaycasterItem> m_volumes;
|
||||
std::vector<SceneRaycasterItem> m_gizmos;
|
||||
|
||||
// When set to true, if checking gizmos returns a valid hit,
|
||||
// the search is not performed on other types
|
||||
bool m_gizmos_on_top{ false };
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING_DEBUG
|
||||
GLModel m_sphere;
|
||||
GLModel m_line;
|
||||
std::optional<HitResult> m_last_hit;
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
|
||||
public:
|
||||
SceneRaycaster();
|
||||
|
||||
// returns the internal index of the added raycaster
|
||||
// can be used with remove_raycaster()
|
||||
int add_raycaster(EType type, PickingId picking_id, const MeshRaycaster& raycaster, const Transform3d& trafo);
|
||||
void set_raycaster_active_state(EType type, PickingId picking_id, bool active);
|
||||
void set_raycaster_transform(EType type, PickingId picking_id, const Transform3d& trafo);
|
||||
// id: the value returned by add_raycaster()
|
||||
void remove_raycaster(EType type, int id);
|
||||
|
||||
void reset(EType type);
|
||||
|
||||
void set_gizmos_on_top(bool value) { m_gizmos_on_top = value; }
|
||||
|
||||
HitResult hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane = nullptr);
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING_DEBUG
|
||||
void render_hit(const Camera& camera);
|
||||
|
||||
size_t beds_count() const { return m_bed.size(); }
|
||||
size_t volumes_count() const { return m_volumes.size(); }
|
||||
size_t gizmos_count() const { return m_gizmos.size(); }
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
|
||||
private:
|
||||
std::vector<SceneRaycasterItem>* get_raycasters(EType type);
|
||||
|
||||
static PickingId encode_id(EType type, PickingId id);
|
||||
static PickingId decode_id(EType type, PickingId id);
|
||||
static PickingId base_id(EType type);
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
|
||||
#endif // slic3r_SceneRaycaster_hpp_
|
Loading…
Reference in a new issue