diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp index 6be38e14a1..f6936efaaa 100644 --- a/src/libslic3r/Format/bbs_3mf.cpp +++ b/src/libslic3r/Format/bbs_3mf.cpp @@ -6233,7 +6233,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) continue; volume_count++; if (m_share_mesh) { - auto iter = m_shared_meshes.find(volume->mesh_ptr()); + auto iter = m_shared_meshes.find(volume->mesh_ptr().get()); if (iter != m_shared_meshes.end()) { const ModelVolume* shared_volume = iter->second.second; @@ -6248,7 +6248,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) continue; } } - const_cast<_BBS_3MF_Exporter *>(this)->m_shared_meshes.insert({volume->mesh_ptr(), {&object_data, volume}}); + const_cast<_BBS_3MF_Exporter *>(this)->m_shared_meshes.insert({volume->mesh_ptr().get(), {&object_data, volume}}); } if (m_from_backup_save) volume_id = (volume_count << 16 | backup_id); diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index d1c1a03e0d..ecee432bba 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -320,6 +320,67 @@ Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation, return transform; } +void assemble_transform(Transform3d& transform, const Transform3d& translation, const Transform3d& rotation, const Transform3d& scale, const Transform3d& mirror) +{ + transform = translation * rotation * scale * mirror; +} + +Transform3d assemble_transform(const Transform3d& translation, const Transform3d& rotation, const Transform3d& scale, const Transform3d& mirror) +{ + Transform3d transform; + assemble_transform(transform, translation, rotation, scale, mirror); + return transform; +} + +void translation_transform(Transform3d& transform, const Vec3d& translation) +{ + transform = Transform3d::Identity(); + transform.translate(translation); +} + +Transform3d translation_transform(const Vec3d& translation) +{ + Transform3d transform; + translation_transform(transform, translation); + return transform; +} + +void rotation_transform(Transform3d& transform, const Vec3d& rotation) +{ + transform = Transform3d::Identity(); + transform.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ()) * Eigen::AngleAxisd(rotation.y(), Vec3d::UnitY()) * Eigen::AngleAxisd(rotation.x(), Vec3d::UnitX())); +} + +Transform3d rotation_transform(const Vec3d& rotation) +{ + Transform3d transform; + rotation_transform(transform, 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; + scale_transform(transform, scale); + return transform; +} + Vec3d extract_euler_angles(const Eigen::Matrix& rotation_matrix) { // reference: http://www.gregslabaugh.net/publications/euler.pdf @@ -405,20 +466,6 @@ void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d& rotation_axis, doubl } } -Transform3d translation_transform(const Vec3d &translation) -{ - Transform3d transform = Transform3d::Identity(); - transform.translate(translation); - return transform; -} - -Transform3d rotation_transform(const Vec3d& rotation) -{ - Transform3d transform = Transform3d::Identity(); - transform.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ()) * Eigen::AngleAxisd(rotation.y(), Vec3d::UnitY()) * Eigen::AngleAxisd(rotation.x(), Vec3d::UnitX())); - return transform; -} - bool Transformation::Flags::needs_update(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const { return (this->dont_translate != dont_translate) || (this->dont_rotate != dont_rotate) || (this->dont_scale != dont_scale) || (this->dont_mirror != dont_mirror); diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index cbb5df6a81..bafed3b9cf 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -324,7 +324,8 @@ bool arrange( // 4) rotate Y // 5) rotate Z // 6) translate -void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones()); +void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), + const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones()); // Returns the transform obtained by assembling the given transformations in the following order: // 1) mirror @@ -333,7 +334,45 @@ void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d // 4) rotate Y // 5) rotate Z // 6) translate -Transform3d assemble_transform(const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones()); +Transform3d assemble_transform(const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), + const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones()); + +// Sets the given transform by multiplying the given transformations in the following order: +// T = translation * rotation * scale * mirror +void assemble_transform(Transform3d& transform, const Transform3d& translation = Transform3d::Identity(), + const Transform3d& rotation = Transform3d::Identity(), const Transform3d& scale = Transform3d::Identity(), + const Transform3d& mirror = Transform3d::Identity()); + +// Returns the transform obtained by multiplying the given transformations in the following order: +// T = translation * rotation * scale * mirror +Transform3d assemble_transform(const Transform3d& translation = Transform3d::Identity(), const Transform3d& rotation = Transform3d::Identity(), + const Transform3d& scale = Transform3d::Identity(), const Transform3d& mirror = Transform3d::Identity()); + +// Sets the given transform by assembling the given translation +void translation_transform(Transform3d& transform, const Vec3d& translation); + +// Returns the transform obtained by assembling the given translation +Transform3d translation_transform(const Vec3d& translation); + +// Sets the given transform by assembling the given rotations in the following order: +// 1) rotate X +// 2) rotate Y +// 3) rotate Z +void rotation_transform(Transform3d& transform, const Vec3d& rotation); + +// Returns the transform obtained by assembling the given rotations in the following order: +// 1) rotate X +// 2) rotate Y +// 3) rotate Z +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 // Warning -> The matrix should not contain any scale or shear !!! @@ -346,16 +385,7 @@ Vec3d extract_euler_angles(const Transform3d& transform); // get rotation from two vectors. // Default output is axis-angle. If rotation_matrix pointer is provided, also output rotation matrix // Euler angles can be obtained by extract_euler_angles() -void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d& rotation_axis, double& phi, Matrix3d* rotation_matrix = nullptr); - -// Returns the transform obtained by assembling the given translation -Transform3d translation_transform(const Vec3d &translation); - -// Returns the transform obtained by assembling the given rotations in the following order: -// 1) rotate X -// 2) rotate Y -// 3) rotate Z -Transform3d rotation_transform(const Vec3d &rotation); +void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d &rotation_axis, double &phi, Matrix3d *rotation_matrix = nullptr); class Transformation { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 481552c058..33e7ff6a90 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -883,7 +883,7 @@ public: // The triangular model. const TriangleMesh& mesh() const { return *m_mesh.get(); } - const TriangleMesh* mesh_ptr() const { return m_mesh.get(); } + std::shared_ptr mesh_ptr() const { return m_mesh; } void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared(mesh); } void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared(std::move(mesh)); } void set_mesh(const indexed_triangle_set &mesh) { m_mesh = std::make_shared(mesh); } diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index aa014a6dc9..40ac478c37 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -61,6 +61,8 @@ #define ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT (1 && ENABLE_2_4_0_BETA2) // Enable fit print volume command for circular printbeds #define ENABLE_ENHANCED_PRINT_VOLUME_FIT (1 && ENABLE_2_4_0_BETA2) +// Enable picking using raytracing +#define ENABLE_RAYCAST_PICKING_DEBUG (1) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 21d8fd6c85..b3d6f51442 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -99,6 +99,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 066da6b336..202912f3d7 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -30,7 +30,6 @@ static const float GROUND_Z = -0.04f; static const Slic3r::ColorRGBA DEFAULT_MODEL_COLOR = { 0.3255f, 0.337f, 0.337f, 1.0f }; static const Slic3r::ColorRGBA DEFAULT_MODEL_COLOR_DARK = { 0.255f, 0.255f, 0.283f, 1.0f }; -static const Slic3r::ColorRGBA PICKING_MODEL_COLOR = Slic3r::ColorRGBA::BLACK(); 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 }; @@ -326,6 +325,10 @@ bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_heig 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())); + // unregister from picking + // BBS: remove the bed picking logic + // wxGetApp().plater()->canvas3D()->remove_raycasters_for_picking(SceneRaycaster::EType::Bed); + // Let the calee to update the UI. return true; } @@ -366,11 +369,6 @@ void Bed3D::render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Tra render_internal(canvas, view_matrix, projection_matrix, bottom, scale_factor, show_axes); } -/*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); -}*/ - void Bed3D::render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes) { @@ -669,6 +667,9 @@ void Bed3D::render_model(const Transform3d& view_matrix, const Transform3d& proj m_model.set_color(m_is_dark ? DEFAULT_MODEL_COLOR_DARK : DEFAULT_MODEL_COLOR); update_model_offset(); + + // BBS: remove the bed picking logic + //register_raycasters_for_picking(m_model.model.get_geometry(), Geometry::assemble_transform(m_model_offset)); } if (!m_model.get_filename().empty()) { @@ -722,7 +723,7 @@ void Bed3D::render_default(bool bottom, const Transform3d& view_matrix, const Tr if (m_model.get_filename().empty() && !bottom) { // draw background glsafe(::glDepthMask(GL_FALSE)); - m_triangles.set_color(picking ? PICKING_MODEL_COLOR : DEFAULT_MODEL_COLOR); + m_triangles.set_color(DEFAULT_MODEL_COLOR); m_triangles.render(); glsafe(::glDepthMask(GL_TRUE)); } @@ -740,5 +741,26 @@ void Bed3D::render_default(bool bottom, const Transform3d& view_matrix, const Tr } } +// BBS: remove the bed picking logic +/* +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); +} +*/ } // GUI } // Slic3r diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 3fa40df084..b668b5ed61 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -147,7 +147,6 @@ public: Point point_projection(const Point& point) const; void render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes); - //void render_for_picking(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor); void on_change_color_mode(bool is_dark); @@ -167,6 +166,9 @@ private: void render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix); void render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom); void render_default(bool bottom, const Transform3d& view_matrix, const Transform3d& projection_matrix); + + // BBS: remove the bed picking logic + // void register_raycasters_for_picking(const GLModel::Geometry& geometry, const Transform3d& trafo); }; } // GUI diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index d276817234..3b735d8275 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -623,7 +623,7 @@ int GLVolumeCollection::load_object_volume( const ModelVolume *model_volume = model_object->volumes[volume_idx]; const int extruder_id = model_volume->extruder_id(); const ModelInstance *instance = model_object->instances[instance_idx]; - const TriangleMesh &mesh = model_volume->mesh(); + std::shared_ptr mesh = model_volume->mesh_ptr(); this->volumes.emplace_back(new GLVolume()); GLVolume& v = *this->volumes.back(); v.set_color(color_from_model_volume(*model_volume)); @@ -632,7 +632,8 @@ int GLVolumeCollection::load_object_volume( #if ENABLE_SMOOTH_NORMALS v.model.init_from(mesh, true); #else - v.model.init_from(mesh); + v.model.init_from(*mesh); + v.mesh_raycaster = std::make_unique(mesh); #endif // ENABLE_SMOOTH_NORMALS v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx); if (model_volume->is_model_part()) { @@ -686,8 +687,9 @@ void GLVolumeCollection::load_object_auxiliary( v.model.init_from(mesh, true); #else v.model.init_from(mesh); -#endif // ENABLE_SMOOTH_NORMALS v.model.set_color((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR); + v.mesh_raycaster = std::make_unique(std::make_shared(mesh)); +#endif // ENABLE_SMOOTH_NORMALS v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first); v.geometry_id = std::pair(timestamp, model_instance.id().id); // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. @@ -726,13 +728,6 @@ int GLVolumeCollection::load_wipe_tower_preview( colors.push_back(extruder_colors[0]); } -#if 0 - // We'll make another mesh to show the brim (fixed layer height): - TriangleMesh brim_mesh = make_cube(width + 2.f * brim_width, depth + 2.f * brim_width, 0.2f); - brim_mesh.translate(-brim_width, -brim_width, 0.f); - mesh.merge(brim_mesh); -#endif - // Orca: make it transparent for(auto& color : colors) color.a(0.66f); @@ -745,6 +740,7 @@ int GLVolumeCollection::load_wipe_tower_preview( v.model_per_colors[i].init_from(color_part); } v.model.init_from(wipe_tower_shell); + v.mesh_raycaster = std::make_unique(std::make_shared(wipe_tower_shell)); v.set_convex_hull(wipe_tower_shell); v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle)); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 4dce685753..e04e71ab45 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -13,6 +13,7 @@ #include "GLModel.hpp" #include "GLShader.hpp" +#include "MeshUtils.hpp" #include #include @@ -202,6 +203,8 @@ public: EHoverState hover; GUI::GLModel model; + // raycaster used for picking + std::unique_ptr mesh_raycaster; // BBS mutable std::vector mmuseg_models; mutable ObjectBase::Timestamp mmuseg_ts; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6c65212016..e8839e777c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1820,9 +1820,17 @@ void GLCanvas3D::render(bool only_init) _rectangular_selection_picking_pass(); //BBS: enable picking when no volumes for partplate logic //else if (!m_volumes.empty()) - else + else { // regular picking pass _picking_pass(); + +#if ENABLE_RAYCAST_PICKING_DEBUG + 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 @@ -1896,6 +1904,11 @@ void GLCanvas3D::render(bool only_init) } #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 @@ -2516,6 +2529,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re volume.model.init_from(mesh, true); #else volume.model.init_from(mesh); + volume.mesh_raycaster = std::make_unique(std::make_shared(mesh)); #endif // ENABLE_SMOOTH_NORMALS } else { @@ -2523,7 +2537,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re #if ENABLE_SMOOTH_NORMALS volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); #else - volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); + const TriangleMesh& new_mesh = m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(); + volume.model.init_from(new_mesh); + volume.mesh_raycaster = std::make_unique(std::make_shared(new_mesh)); #endif // ENABLE_SMOOTH_NORMALS } } @@ -2688,6 +2704,13 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re #endif } + // refresh volume raycasters for picking + m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Volume); + for (size_t i = 0; i < m_volumes.volumes.size(); ++i) { + assert(m_volumes.volumes[i]->mesh_raycaster != nullptr); + add_raycaster_for_picking(SceneRaycaster::EType::Volume, i, *m_volumes.volumes[i]->mesh_raycaster, m_volumes.volumes[i]->world_matrix()); + } + // and force this canvas to be redrawn. m_dirty = true; } @@ -4205,6 +4228,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) { + m_mouse.position = pos.cast(); + if (evt.LeftUp()) { m_selection.stop_dragging(); m_rotation_center(0) = m_rotation_center(1) = m_rotation_center(2) = 0.f; @@ -4247,7 +4272,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) deselect_all(); } else if (evt.RightUp() && !is_layers_editing_enabled()) { - m_mouse.position = pos.cast(); // forces a frame render to ensure that m_hover_volume_idxs is updated even when the user right clicks while // the context menu is already shown render(); @@ -6396,87 +6420,111 @@ void GLCanvas3D::_refresh_if_shown_on_screen() void GLCanvas3D::_picking_pass() { - std::vector* hover_volume_idxs = const_cast*>(&m_hover_volume_idxs); - std::vector* hover_plate_idxs = const_cast*>(&m_hover_plate_idxs); + if (!m_picking_enabled || m_mouse.dragging || m_mouse.position == Vec2d(DBL_MAX, DBL_MAX)) { + ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.begin(std::string("Hit result"), ImGuiWindowFlags_AlwaysAutoResize); + imgui.text("Picking disabled"); + imgui.end(); + return; + } - if (m_picking_enabled && !m_mouse.dragging && m_mouse.position != Vec2d(DBL_MAX, DBL_MAX)) { - hover_volume_idxs->clear(); - hover_plate_idxs->clear(); + m_hover_volume_idxs.clear(); + m_hover_plate_idxs.clear(); - // Render the object for picking. - // FIXME This cannot possibly work in a multi - sampled context as the color gets mangled by the anti - aliasing. - // Better to use software ray - casting on a bounding - box hierarchy. + // TODO: Support plate picking - if (m_multisample_allowed) - // This flag is often ignored by NVIDIA drivers if rendering into a screen buffer. - glsafe(::glDisable(GL_MULTISAMPLE)); - - glsafe(::glDisable(GL_BLEND)); - glsafe(::glEnable(GL_DEPTH_TEST)); - - glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); - - //BBS: only render plate in view 3D - if (m_canvas_type == ECanvasType::CanvasView3D) { - const Camera &camera = wxGetApp().plater()->get_camera(); - _render_plates_for_picking(camera.get_view_matrix(), camera.get_projection_matrix()); - } - - _render_volumes_for_picking(); - - //BBS: remove the bed picking logic - //_render_bed_for_picking(!wxGetApp().plater()->get_camera().is_looking_downward()); - - m_gizmos.render_current_gizmo_for_picking_pass(); - - if (m_multisample_allowed) - glsafe(::glEnable(GL_MULTISAMPLE)); - - int volume_id = -1; - int gizmo_id = -1; - - std::array color = { 0, 0, 0, 0 }; - const Size& cnv_size = get_canvas_size(); - bool inside = 0 <= m_mouse.position(0) && m_mouse.position(0) < cnv_size.get_width() && 0 <= m_mouse.position(1) && m_mouse.position(1) < cnv_size.get_height(); - if (inside) { - glsafe(::glReadPixels(m_mouse.position(0), cnv_size.get_height() - m_mouse.position.y() - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color.data())); - if (picking_checksum_alpha_channel(color[0], color[1], color[2]) == color[3]) { - // Only non-interpolated colors are valid, those have their lowest three bits zeroed. - // we reserve color = (0,0,0) for occluders (as the printbed) - // volumes' id are shifted by 1 - // see: _render_volumes_for_picking() - unsigned int id = picking_encode(color[0], color[1], color[2]); - //BBS: remove the bed picking logic - //volume_id = id - 1; - volume_id = id; - // gizmos' id are instead properly encoded by the color - gizmo_id = id; - } - } - else - m_gizmos.set_hover_id(inside && (unsigned int)gizmo_id <= GLGizmoBase::BASE_ID ? ((int)GLGizmoBase::BASE_ID - gizmo_id) : -1); - - //BBS: add plate picking logic - int plate_hover_id = PartPlate::PLATE_BASE_ID - volume_id; - if (plate_hover_id >= 0 && plate_hover_id < PartPlateList::MAX_PLATES_COUNT * PartPlate::GRABBER_COUNT) { - wxGetApp().plater()->get_partplate_list().set_hover_id(plate_hover_id); - hover_plate_idxs->emplace_back(plate_hover_id); - const_cast(&m_gizmos)->set_hover_id(-1); - } - else { - wxGetApp().plater()->get_partplate_list().reset_hover_id(); - if (0 <= volume_id && volume_id < (int)m_volumes.volumes.size()) { - // 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)) - hover_volume_idxs->emplace_back(volume_id); - const_cast(&m_gizmos)->set_hover_id(-1); + 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 - const_cast(&m_gizmos)->set_hover_id(inside && (unsigned int)volume_id <= GLGizmoBase::BASE_ID ? ((int)GLGizmoBase::BASE_ID - volume_id) : -1); - } + assert(false); - _update_volumes_hover_state(); + 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 } void GLCanvas3D::_rectangular_selection_picking_pass() @@ -6643,26 +6691,11 @@ void GLCanvas3D::_render_bed(const Transform3d& view_matrix, const Transform3d& m_bed.render(*this, view_matrix, projection_matrix, bottom, scale_factor, show_axes); } -void GLCanvas3D::_render_bed_for_picking(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom) -{ - float scale_factor = 1.0; -#if ENABLE_RETINA_GL - scale_factor = m_retina_helper->get_scale_factor(); -#endif // ENABLE_RETINA_GL - - //m_bed.render_for_picking(*this, view_matrix, projection_matrix, bottom, scale_factor); -} - void GLCanvas3D::_render_platelist(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali) { wxGetApp().plater()->get_partplate_list().render(view_matrix, projection_matrix, bottom, only_current, only_body, hover_id, render_cali); } -void GLCanvas3D::_render_plates_for_picking(const Transform3d &view_matrix, const Transform3d &projection_matrix) -{ - wxGetApp().plater()->get_partplate_list().render_for_picking_pass(view_matrix, projection_matrix); -} - void GLCanvas3D::_render_plane() const { ;//TODO render assemble plane diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 77ad673947..89f5f96952 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -16,6 +16,7 @@ #include "libslic3r/GCode/GCodeProcessor.hpp" #include "GCodeViewer.hpp" #include "Camera.hpp" +#include "SceneRaycaster.hpp" #include "IMToolbar.hpp" #include "libslic3r/Slicing.hpp" @@ -501,6 +502,7 @@ private: bool m_is_dark = false; wxGLCanvas* m_canvas; wxGLContext* m_context; + SceneRaycaster m_scene_raycaster; Bed3D &m_bed; #if ENABLE_RETINA_GL std::unique_ptr m_retina_helper; @@ -726,6 +728,16 @@ public: bool init(); void post_event(wxEvent &&event); + + void add_raycaster_for_picking(SceneRaycaster::EType type, PickingId id, const MeshRaycaster& raycaster, const Transform3d& trafo) { + m_scene_raycaster.add_raycaster(type, id, raycaster, trafo); + } + void remove_raycasters_for_picking(SceneRaycaster::EType type, PickingId id) { + m_scene_raycaster.remove_raycasters(type, id); + } + void remove_raycasters_for_picking(SceneRaycaster::EType type) { + m_scene_raycaster.remove_raycasters(type); + } void reset_explosion_ratio() { m_explosion_ratio = 1.0; } void on_change_color_mode(bool is_dark, bool reinit = true); @@ -1116,10 +1128,8 @@ private: void _rectangular_selection_picking_pass(); void _render_background(); void _render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes); - void _render_bed_for_picking(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom); //BBS: add part plate related logic void _render_platelist(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current, bool only_body = false, int hover_id = -1, bool render_cali = false); - void _render_plates_for_picking(const Transform3d& view_matrix, const Transform3d& projection_matrix); //BBS: add outline drawing logic void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true); //BBS: GUI refactor: add canvas size as parameters diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp index dcff45494e..df9def7ab4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp @@ -50,7 +50,7 @@ bool GLGizmoMeshBoolean::gizmo_event(SLAGizmoEventType action, const Vec2d& mous // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) { - MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh()); + MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh_ptr()); if (mesh_raycaster.unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), &facet)) { // Is this hit the closest to the camera so far? diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp index 6001af544f..4c2399dcd5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp @@ -287,7 +287,7 @@ bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_posit // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) { - MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh()); + MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh_ptr()); if (mesh_raycaster.unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), &facet)) { @@ -543,7 +543,7 @@ void GLGizmoText::on_update(const UpdateData &data) if (mesh_id == m_volume_idx) continue; - MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh()); + MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh_ptr()); if (mesh_raycaster.unproject_on_mesh(mouse_pos, trafo_matrices[mesh_id], camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), &facet)) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 5a7d4c0f50..09a7cff3ec 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -331,7 +331,7 @@ void Raycaster::on_update() if (meshes != m_old_meshes) { m_raycasters.clear(); for (const TriangleMesh* mesh : meshes) - m_raycasters.emplace_back(new MeshRaycaster(*mesh)); + m_raycasters.emplace_back(new MeshRaycaster(std::make_shared(*mesh))); m_old_meshes = meshes; } } diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index eaf0df314d..878aa3a1cd 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -357,6 +357,38 @@ std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo return out; } +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; +} 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 4f0210b355..b387966961 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -9,6 +9,7 @@ #include "slic3r/GUI/GLModel.hpp" #include +#include namespace Slic3r { @@ -52,6 +53,8 @@ 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]); } + 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()); } 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; } @@ -125,11 +128,10 @@ private: // whether certain points are visible or obscured by the mesh etc. class MeshRaycaster { public: - // The class references extern TriangleMesh, which must stay alive - // during MeshRaycaster existence. - MeshRaycaster(const TriangleMesh& mesh) - : m_emesh(mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length - , m_normals(its_face_normals(mesh.its)) + 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)) { } @@ -161,10 +163,22 @@ public: const ClippingPlane* clipping_plane = nullptr // clipping plane (if active) ) const; + // 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; + // 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. @@ -173,11 +187,22 @@ public: Vec3f get_triangle_normal(size_t facet_idx) const; private: + std::shared_ptr m_mesh; sla::IndexedMesh m_emesh; std::vector m_normals; }; - +struct PickingModel +{ + GLModel model; + std::unique_ptr mesh_raycaster; + + void reset() { + model.reset(); + mesh_raycaster.reset(); + } +}; + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp new file mode 100644 index 0000000000..9a4f69b9d4 --- /dev/null +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -0,0 +1,220 @@ +#include "libslic3r/libslic3r.h" +#include "SceneRaycaster.hpp" + +#include "Camera.hpp" +#include "GUI_App.hpp" + +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 +} + +void 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); + break; + } + case EType::Volume: { + m_volumes.emplace_back(encode_id(type, id), raycaster, trafo); + break; + } + case EType::Gizmo: { + m_gizmos.emplace_back(encode_id(type, id), raycaster, trafo); + break; + } + default: { break; } + }; +} + +void SceneRaycaster::remove_raycasters(EType type, PickingId id) +{ + std::vector* raycasters = get_raycasters(type); + auto it = raycasters->begin(); + while (it != raycasters->end()) { + if (it->get_id() == encode_id(type, id)) + it = raycasters->erase(it); + else + ++it; + } +} + +void SceneRaycaster::remove_raycasters(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; } + default: { break; } + }; +} + +void SceneRaycaster::set_raycaster_active_state(EType type, PickingId 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, PickingId 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; + } + } +} + +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; + }; + +#if ENABLE_RAYCAST_PICKING_DEBUG + m_last_hit.reset(); +#endif // ENABLE_RAYCAST_PICKING_DEBUG + + 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; + } + } + } + }; + + if (!m_gizmos.empty()) + test_raycasters(EType::Gizmo); + + if (!m_gizmos_on_top || !ret.is_valid()) { + if (camera.is_looking_downward() && !m_bed.empty()) + test_raycasters(EType::Bed); + if (!m_volumes.empty()) + test_raycasters(EType::Volume); + } + + if (ret.is_valid()) + ret.raycaster_id = decode_id(ret.type, ret.raycaster_id); + +#if ENABLE_RAYCAST_PICKING_DEBUG + m_last_hit = ret; +#endif // ENABLE_RAYCAST_PICKING_DEBUG + 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(10.0); + 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 diff --git a/src/slic3r/GUI/SceneRaycaster.hpp b/src/slic3r/GUI/SceneRaycaster.hpp new file mode 100644 index 0000000000..1832ffc558 --- /dev/null +++ b/src/slic3r/GUI/SceneRaycaster.hpp @@ -0,0 +1,114 @@ +#ifndef slic3r_SceneRaycaster_hpp_ +#define slic3r_SceneRaycaster_hpp_ + +#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(); + + void add_raycaster(EType type, PickingId picking_id, const MeshRaycaster& raycaster, const Transform3d& trafo); + void remove_raycasters(EType type, PickingId id); + void remove_raycasters(EType type); + + void set_raycaster_active_state(EType type, PickingId picking_id, bool active); + void set_raycaster_transform(EType type, PickingId picking_id, const Transform3d& trafo); + + 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 // slic3r_SceneRaycaster_hpp_