From cfc3988b9fdf3822e1d4a71e24602c983bbd050d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 10 Jun 2022 13:20:52 +0200 Subject: [PATCH] Tech ENABLE_RAYCAST_PICKING - 1st installment - Raytraced picking of printbed --- src/libslic3r/Geometry.cpp | 10 + src/libslic3r/Geometry.hpp | 2 + src/libslic3r/Technologies.hpp | 4 + src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/3DBed.cpp | 135 ++++++++++++++ src/slic3r/GUI/3DBed.hpp | 23 +++ src/slic3r/GUI/GLCanvas3D.cpp | 120 ++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 17 ++ src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 4 + src/slic3r/GUI/MeshUtils.cpp | 34 ++++ src/slic3r/GUI/MeshUtils.hpp | 49 ++++- src/slic3r/GUI/SceneRaycaster.cpp | 222 +++++++++++++++++++++++ src/slic3r/GUI/SceneRaycaster.hpp | 121 ++++++++++++ 13 files changed, 741 insertions(+), 2 deletions(-) create mode 100644 src/slic3r/GUI/SceneRaycaster.cpp create mode 100644 src/slic3r/GUI/SceneRaycaster.hpp diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 115d21693b..3070dbe42d 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -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; diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index aa09a0d3e8..8d981028be 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -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 diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 71d679f982..3c1de2ed0c 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -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_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 29b8b7e736..09066bebd5 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -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 diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index a9545b3e63..18451960ad 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -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(GROUND_Z) }); m_axes.set_stem_length(0.1f * static_cast(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(std::make_shared(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 diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 8eb28c9f6d..7774ebcf0a 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -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 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 diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 26df389366..27540b4546 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -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) { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 3a6a35e470..a39771b40f 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -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 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); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 9e2822dac0..19d0731512 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -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(*mesh))); +#else m_raycasters.emplace_back(new MeshRaycaster(*mesh)); +#endif // ENABLE_RAYCAST_PICKING m_old_meshes = meshes; } } diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 2e73434a32..69796dcd24 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -350,6 +350,40 @@ std::vector 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 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(); + normal = hit.normal().cast(); + + 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 { diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 3b8d247c18..ab52f188dd 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -14,6 +14,9 @@ #endif // ENABLE_LEGACY_OPENGL_REMOVAL #include +#if ENABLE_RAYCAST_PICKING +#include +#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& 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 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 m_mesh; +#endif // ENABLE_RAYCAST_PICKING sla::IndexedMesh m_emesh; std::vector m_normals; }; - +#if ENABLE_RAYCAST_PICKING +struct PickingModel +{ + GLModel model; + std::unique_ptr mesh_raycaster; + + void reset() { + model.reset(); + mesh_raycaster.reset(); + } +}; +#endif // ENABLE_RAYCAST_PICKING + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp new file mode 100644 index 0000000000..f9e0e0bb5a --- /dev/null +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -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* 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* 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* 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::max(); + auto is_closest = [&closest_hit_squared_distance](const Camera& camera, const Vec3f& hit) { + const double hit_squared_distance = (camera.get_position() - hit.cast()).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* 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()).cast(); + 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()).normalized().cast(); + 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()) * + 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()).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* SceneRaycaster::get_raycasters(EType type) +{ + std::vector* 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 diff --git a/src/slic3r/GUI/SceneRaycaster.hpp b/src/slic3r/GUI/SceneRaycaster.hpp new file mode 100644 index 0000000000..8d9496f27d --- /dev/null +++ b/src/slic3r/GUI/SceneRaycaster.hpp @@ -0,0 +1,121 @@ +#ifndef slic3r_SceneRaycaster_hpp_ +#define slic3r_SceneRaycaster_hpp_ + +#if ENABLE_RAYCAST_PICKING + +#include "MeshUtils.hpp" +#include "GLModel.hpp" + +#include +#include +#include + +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 m_bed; + std::vector m_volumes; + std::vector 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 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* 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_