diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 115d21693..3070dbe42 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 aa09a0d3e..8d981028b 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/Model.hpp b/src/libslic3r/Model.hpp index 828358419..0e5033475 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -618,6 +618,9 @@ public: // The triangular model. const TriangleMesh& mesh() const { return *m_mesh.get(); } +#if ENABLE_RAYCAST_PICKING + std::shared_ptr mesh_ptr() const { return m_mesh; } +#endif // ENABLE_RAYCAST_PICKING 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 01b3bb2eb..b2b02b643 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -12,8 +12,6 @@ #define ENABLE_RENDER_SELECTION_CENTER 0 // Shows an imgui dialog with camera related data #define ENABLE_CAMERA_STATISTICS 0 -// Render the picking pass instead of the main scene (use [T] key to toggle between regular rendering and picking pass only rendering) -#define ENABLE_RENDER_PICKING_PASS 0 // Enable extracting thumbnails from selected gcode and save them as png files #define ENABLE_THUMBNAIL_GENERATOR_DEBUG 0 // Disable synchronization of unselected instances @@ -73,17 +71,17 @@ #define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_5_0_ALPHA1) // Enable modified camera control using mouse #define ENABLE_NEW_CAMERA_MOVEMENTS (1 && ENABLE_2_5_0_ALPHA1) -// Enable modified rectangle selection -#define ENABLE_NEW_RECTANGLE_SELECTION (1 && ENABLE_2_5_0_ALPHA1) // Enable alternative version of file_wildcards() #define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_5_0_ALPHA1) // Enable processing of gcode G2 and G3 lines #define ENABLE_PROCESS_G2_G3_LINES (1 && ENABLE_2_5_0_ALPHA1) // Enable fix of used filament data exported to gcode file #define ENABLE_USED_FILAMENT_POST_PROCESS (1 && ENABLE_2_5_0_ALPHA1) -// Enable gizmo grabbers to share common models -#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 29b8b7e73..09066bebd 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 a9545b3e6..8dff7bc49 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -249,6 +249,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_raycasters_for_picking(SceneRaycaster::EType::Bed); +#endif // ENABLE_RAYCAST_PICKING + // Let the calee to update the UI. return true; } @@ -301,7 +306,11 @@ 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.model.set_color(picking ? PICKING_MODEL_COLOR : DEFAULT_MODEL_COLOR); +#else 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 @@ -341,7 +350,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 +404,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() @@ -771,9 +793,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 +811,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 +839,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 @@ -866,16 +909,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 +949,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 +1042,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 8eb28c9f6..7f0dff19d 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 }; @@ -197,6 +204,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/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 074269c92..83b8cae63 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -804,15 +804,24 @@ 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]; +#if ENABLE_RAYCAST_PICKING + std::shared_ptr mesh = model_volume->mesh_ptr(); +#else const TriangleMesh &mesh = model_volume->mesh(); +#endif // ENABLE_RAYCAST_PICKING this->volumes.emplace_back(new GLVolume()); GLVolume& v = *this->volumes.back(); v.set_color(color_from_model_volume(*model_volume)); #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_SMOOTH_NORMALS v.model.init_from(mesh, true); +#else +#if ENABLE_RAYCAST_PICKING + v.model.init_from(*mesh); + v.mesh_raycaster = std::make_unique(mesh); #else v.model.init_from(mesh); +#endif // ENABLE_RAYCAST_PICKING #endif // ENABLE_SMOOTH_NORMALS #else #if ENABLE_SMOOTH_NORMALS @@ -877,8 +886,11 @@ 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); +#if ENABLE_RAYCAST_PICKING + v.mesh_raycaster = std::make_unique(std::make_shared(mesh)); +#endif // ENABLE_RAYCAST_PICKING +#endif // ENABLE_SMOOTH_NORMALS #else #if ENABLE_SMOOTH_NORMALS v.indexed_vertex_array.load_mesh(mesh, true); @@ -941,8 +953,115 @@ int GLVolumeCollection::load_wipe_tower_preview( // Too narrow tower would interfere with the teeth. The estimate is not precise anyway. depth = std::max(depth, 10.f); float min_width = 30.f; + +#if ENABLE_RAYCAST_PICKING + const float scaled_brim_height = 0.2f / height; +#endif // ENABLE_RAYCAST_PICKING + // We'll now create the box with jagged edge. y-coordinates of the pre-generated model // are shifted so that the front edge has y=0 and centerline of the back edge has y=depth: +#if ENABLE_RAYCAST_PICKING + // We split the box in three main pieces, + // the two laterals are identical and the central is the one containing the jagged geometry + + // lateral parts generator + auto generate_lateral = [&](float min_x, float max_x) { + const std::vector vertices = { + { min_x, -(depth + brim_width), 0.0f }, + { max_x, -(depth + brim_width), 0.0f }, + { min_x, -(depth + brim_width), scaled_brim_height }, + { max_x, -(depth + brim_width), scaled_brim_height }, + { min_x, -depth, scaled_brim_height }, + { max_x, -depth, scaled_brim_height }, + { min_x, -depth, 1.0f }, + { max_x, -depth, 1.0f }, + { min_x, 0.0f, 1.0f }, + { max_x, 0.0f, 1.0f }, + { min_x, 0.0f, scaled_brim_height }, + { max_x, 0.0f, scaled_brim_height }, + { min_x, brim_width, scaled_brim_height }, + { max_x, brim_width, scaled_brim_height }, + { min_x, brim_width, 0.0f }, + { max_x, brim_width, 0.0f } + }; + const std::vector triangles = { + { 0, 1, 3 }, { 0, 3, 2 }, { 2, 3, 5 }, { 2, 5, 4 }, { 4, 5, 7 }, { 4, 7, 6 }, { 6, 7, 9 }, { 6, 9, 8 }, + { 8, 9, 11 }, { 8, 11, 10 }, { 10, 11, 13 }, { 10, 13, 12 }, { 12, 13, 15 }, { 12, 15, 14 }, { 14, 15, 1 }, { 14, 1, 0 } + }; + + indexed_triangle_set its; + its.vertices.reserve(vertices.size()); + for (const Vec3f& v : vertices) { + its.vertices.emplace_back(v.x(), v.y() + depth, v.z()); + } + its.indices.reserve(triangles.size()); + for (const Vec3i& t : triangles) { + its.indices.emplace_back(t); + } + return its; + }; + + // central parts generator + auto generate_central = [&]() { + const std::vector vertices = { + { 38.453f, -(depth + brim_width), 0.0f }, + { 61.547f, -(depth + brim_width), 0.0f }, + { 38.453f, -(depth + brim_width), scaled_brim_height }, + { 61.547f, -(depth + brim_width), scaled_brim_height }, + { 38.453f, -depth, scaled_brim_height }, + { 61.547f, -depth, scaled_brim_height }, + { 38.453f, -depth, 1.0f }, + { 61.547f, -depth, 1.0f }, + { 38.453f, 0.0f, 1.0f }, + { 38.453f + 0.57735f * brim_width, brim_width, 1.0f }, + { 44.2265f, 10.0f, 1.0f }, + { 50.0f - 0.57735f * brim_width, brim_width, 1.0f }, + { 50.0f, 0.0f, 1.0f }, + { 55.7735f, -10.0f, 1.0f }, + { 61.547f, 0.0f, 1.0f }, + { 38.453f, 0.0f, scaled_brim_height }, + { 38.453f, brim_width, scaled_brim_height }, + { 38.453f + 0.57735f * brim_width, brim_width, scaled_brim_height }, + { 50.0f - 0.57735f * brim_width, brim_width, scaled_brim_height }, + { 50.0f, 0.0f, scaled_brim_height }, + { 55.7735f, -10.0f, scaled_brim_height }, + { 61.547f, 0.0f, scaled_brim_height }, + { 61.547f, brim_width, scaled_brim_height }, + { 38.453f, brim_width, 0.0f }, + { 38.453f + 0.57735f * brim_width, brim_width, 0.0f }, + { 44.2265f, 10.0f, 0.0f }, + { 50.0f - 0.57735f * brim_width, brim_width, 0.0f }, + { 61.547f, brim_width, 0.0f } + }; + + const std::vector triangles = { + { 0, 1, 3 }, { 0, 3, 2 }, { 2, 3, 5 }, { 2, 5, 4 }, { 4, 5, 7 }, { 4, 7, 6 }, { 7, 14, 13 }, { 7, 13, 6 }, + { 6, 13, 12 }, { 6, 12, 8 }, { 8, 12, 11 }, { 8, 11, 9 }, { 9, 11, 10 }, { 18, 19, 22 }, { 22, 19, 21 }, { 19, 20, 21 }, + { 15, 17, 16 }, { 17, 15, 8 }, { 17, 8, 9 }, { 21, 13, 14 }, { 21, 20, 13 }, { 20, 19, 12 }, { 20, 12, 13 }, { 19, 18, 11 }, + { 19, 11, 12 }, { 27, 26, 18 }, { 27, 18, 22 }, { 26, 25, 18 }, { 18, 25, 11 }, { 11, 25, 10 }, { 25, 24, 17 }, { 25, 17, 9 }, + { 25, 9, 10 }, { 24, 23, 16 }, { 24, 16, 17 }, { 1, 26, 27 }, { 1, 23, 26 }, { 1, 0, 23 }, { 0, 23, 24 }, { 24, 25, 26 } + }; + + indexed_triangle_set its; + its.vertices.reserve(vertices.size()); + for (const Vec3f& v : vertices) { + its.vertices.emplace_back(v.x(), v.y() + depth, v.z()); + } + its.indices.reserve(triangles.size()); + for (const Vec3i& t : triangles) { + its.indices.emplace_back(t); + } + return its; + }; + + TriangleMesh tooth_mesh; + indexed_triangle_set data = generate_lateral(0.0f, 38.453f); + tooth_mesh.merge(TriangleMesh(std::move(data))); + data = generate_central(); + tooth_mesh.merge(TriangleMesh(std::move(data))); + data = generate_lateral(61.547f, 100.0f); + tooth_mesh.merge(TriangleMesh(std::move(data))); +#else float out_points_idx[][3] = { { 0, -depth, 0 }, { 0, 0, 0 }, { 38.453f, 0, 0 }, { 61.547f, 0, 0 }, { 100.0f, 0, 0 }, { 100.0f, -depth, 0 }, { 55.7735f, -10.0f, 0 }, { 44.2265f, 10.0f, 0 }, { 38.453f, 0, 1 }, { 0, 0, 1 }, { 0, -depth, 1 }, { 100.0f, -depth, 1 }, { 100.0f, 0, 1 }, { 61.547f, 0, 1 }, { 55.7735f, -10.0f, 1 }, { 44.2265f, 10.0f, 1 } }; static constexpr const int out_facets_idx[][3] = { @@ -957,6 +1076,7 @@ int GLVolumeCollection::load_wipe_tower_preview( for (const int *face : out_facets_idx) its.indices.emplace_back(face); TriangleMesh tooth_mesh(std::move(its)); +#endif // ENABLE_RAYCAST_PICKING // We have the mesh ready. It has one tooth and width of min_width. We will now // append several of these together until we are close to the required width @@ -964,24 +1084,110 @@ int GLVolumeCollection::load_wipe_tower_preview( size_t n = std::max(1, int(width / min_width)); // How many shall be merged? for (size_t i = 0; i < n; ++i) { mesh.merge(tooth_mesh); +#if ENABLE_RAYCAST_PICKING + tooth_mesh.translate(100.0f, 0.0f, 0.0f); +#else tooth_mesh.translate(min_width, 0.f, 0.f); +#endif // ENABLE_RAYCAST_PICKING } +#if ENABLE_RAYCAST_PICKING + // Now we add the caps along the X axis + const float scaled_brim_width_x = brim_width * n * width / min_width; + auto generate_negx_cap = [&]() { + const std::vector vertices = { + { -scaled_brim_width_x, -(depth + brim_width), 0.0f }, + { 0.0f, -(depth + brim_width), 0.0f }, + { -scaled_brim_width_x, -(depth + brim_width), scaled_brim_height }, + { 0.0f, -(depth + brim_width), scaled_brim_height }, + { 0.0f, -depth, scaled_brim_height }, + { 0.0f, -depth, 1.0f }, + { 0.0f, 0.0f, 1.0f }, + { 0.0f, 0.0f, scaled_brim_height }, + { 0.0f, brim_width, scaled_brim_height }, + { -scaled_brim_width_x, brim_width, scaled_brim_height }, + { 0.0f, brim_width, 0.0f }, + { -scaled_brim_width_x, brim_width, 0.0f } + }; + + const std::vector triangles = { + { 0, 1, 3 }, { 0, 3, 2 }, { 2, 3, 4 }, { 2, 4, 9 }, { 9, 4, 7 }, { 9, 7, 8 }, { 9, 8, 10 }, { 9, 10, 11 }, + { 11, 10, 1 }, { 11, 1, 0 }, { 11, 0, 2 }, { 11, 2, 9 }, { 7, 4, 5 }, { 7, 5, 6 } + }; + + indexed_triangle_set its; + its.vertices.reserve(vertices.size()); + for (const Vec3f& v : vertices) { + its.vertices.emplace_back(v.x(), v.y() + depth, v.z()); + } + its.indices.reserve(triangles.size()); + for (const Vec3i& t : triangles) { + its.indices.emplace_back(t); + } + return its; + }; + + auto generate_posx_cap = [&]() { + const float posx_cap_x = n * 100.0f; + const std::vector vertices = { + { posx_cap_x, -(depth + brim_width), 0.0f }, + { posx_cap_x + scaled_brim_width_x, -(depth + brim_width), 0.0f }, + { posx_cap_x, -(depth + brim_width), scaled_brim_height }, + { posx_cap_x + scaled_brim_width_x, -(depth + brim_width), scaled_brim_height }, + { posx_cap_x, -depth, scaled_brim_height }, + { posx_cap_x, -depth, 1.0f }, + { posx_cap_x, 0.0f, 1.0f }, + { posx_cap_x, 0.0f, scaled_brim_height }, + { posx_cap_x, brim_width, scaled_brim_height }, + { posx_cap_x + scaled_brim_width_x, brim_width, scaled_brim_height }, + { posx_cap_x, brim_width, 0.0f }, + { posx_cap_x + scaled_brim_width_x, brim_width, 0.0f } + }; + + const std::vector triangles = { + { 0, 1, 3 }, { 0, 3, 2 }, { 2, 3, 4 }, { 4, 3, 9 }, { 4, 9, 7 }, { 7, 9, 8 }, { 8, 9, 11 }, { 8, 11, 10 }, + { 10, 11, 1 }, { 10, 1, 0 }, { 1, 11, 9 }, { 1, 9, 3 }, { 4, 7, 6 }, { 4, 6, 5 } + }; + + indexed_triangle_set its; + its.vertices.reserve(vertices.size()); + for (const Vec3f& v : vertices) { + its.vertices.emplace_back(v.x(), v.y() + depth, v.z()); + } + its.indices.reserve(triangles.size()); + for (const Vec3i& t : triangles) { + its.indices.emplace_back(t); + } + return its; + }; + + data = generate_negx_cap(); + mesh.merge(TriangleMesh(std::move(data))); + data = generate_posx_cap(); + mesh.merge(TriangleMesh(std::move(data))); + mesh.scale(Vec3f(width / (n * 100.0f), 1.0f, height)); // Scaling to proper width +#else mesh.scale(Vec3f(width / (n * min_width), 1.f, height)); // Scaling to proper width +#endif // ENABLE_RAYCAST_PICKING } else mesh = make_cube(width, depth, height); +#if !ENABLE_RAYCAST_PICKING // 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 // !ENABLE_RAYCAST_PICKING volumes.emplace_back(new GLVolume(color)); GLVolume& v = *volumes.back(); #if ENABLE_LEGACY_OPENGL_REMOVAL v.model.init_from(mesh); v.model.set_color(color); +#if ENABLE_RAYCAST_PICKING + v.mesh_raycaster = std::make_unique(std::make_shared(mesh)); +#endif // ENABLE_RAYCAST_PICKING #else v.indexed_vertex_array.load_mesh(mesh); #endif // ENABLE_LEGACY_OPENGL_REMOVAL diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 237d19405..a47c8d05d 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -10,6 +10,9 @@ #include "libslic3r/Color.hpp" #include "GLModel.hpp" +#if ENABLE_RAYCAST_PICKING +#include "MeshUtils.hpp" +#endif // ENABLE_RAYCAST_PICKING #include #include @@ -392,6 +395,10 @@ public: #if ENABLE_LEGACY_OPENGL_REMOVAL GUI::GLModel model; +#if ENABLE_RAYCAST_PICKING + // raycaster used for picking + std::unique_ptr mesh_raycaster; +#endif // ENABLE_RAYCAST_PICKING #else // Interleaved triangles & normals with indexed triangles & quads. GLIndexedVertexArray indexed_vertex_array; diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 1370484c3..56980c43a 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -91,6 +91,80 @@ void Camera::select_view(const std::string& direction) look_at(m_target + m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ()); } +#if ENABLE_RAYCAST_PICKING +double Camera::get_near_left() const +{ + switch (m_type) + { + case EType::Perspective: + return m_frustrum_zs.first * (m_projection_matrix.matrix()(0, 2) - 1.0) / m_projection_matrix.matrix()(0, 0); + default: + case EType::Ortho: + return -1.0 / m_projection_matrix.matrix()(0, 0) - 0.5 * m_projection_matrix.matrix()(0, 0) * m_projection_matrix.matrix()(0, 3); + } +} + +double Camera::get_near_right() const +{ + switch (m_type) + { + case EType::Perspective: + return m_frustrum_zs.first * (m_projection_matrix.matrix()(0, 2) + 1.0) / m_projection_matrix.matrix()(0, 0); + default: + case EType::Ortho: + return 1.0 / m_projection_matrix.matrix()(0, 0) - 0.5 * m_projection_matrix.matrix()(0, 0) * m_projection_matrix.matrix()(0, 3); + } +} + +double Camera::get_near_top() const +{ + switch (m_type) + { + case EType::Perspective: + return m_frustrum_zs.first * (m_projection_matrix.matrix()(1, 2) + 1.0) / m_projection_matrix.matrix()(1, 1); + default: + case EType::Ortho: + return 1.0 / m_projection_matrix.matrix()(1, 1) - 0.5 * m_projection_matrix.matrix()(1, 1) * m_projection_matrix.matrix()(1, 3); + } +} + +double Camera::get_near_bottom() const +{ + switch (m_type) + { + case EType::Perspective: + return m_frustrum_zs.first * (m_projection_matrix.matrix()(1, 2) - 1.0) / m_projection_matrix.matrix()(1, 1); + default: + case EType::Ortho: + return -1.0 / m_projection_matrix.matrix()(1, 1) - 0.5 * m_projection_matrix.matrix()(1, 1) * m_projection_matrix.matrix()(1, 3); + } +} + +double Camera::get_near_width() const +{ + switch (m_type) + { + case EType::Perspective: + return 2.0 * m_frustrum_zs.first / m_projection_matrix.matrix()(0, 0); + default: + case EType::Ortho: + return 2.0 / m_projection_matrix.matrix()(0, 0); + } +} + +double Camera::get_near_height() const +{ + switch (m_type) + { + case EType::Perspective: + return 2.0 * m_frustrum_zs.first / m_projection_matrix.matrix()(1, 1); + default: + case EType::Ortho: + return 2.0 / m_projection_matrix.matrix()(1, 1); + } +} +#endif // ENABLE_RAYCAST_PICKING + double Camera::get_fov() const { switch (m_type) @@ -103,6 +177,21 @@ double Camera::get_fov() const }; } +#if ENABLE_RAYCAST_PICKING +void Camera::set_viewport(int x, int y, unsigned int w, unsigned int h) +{ +#if ENABLE_LEGACY_OPENGL_REMOVAL + m_viewport = { 0, 0, int(w), int(h) }; +#else + glsafe(::glGetIntegerv(GL_VIEWPORT, m_viewport.data())); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL +} + +void Camera::apply_viewport() const +{ + glsafe(::glViewport(m_viewport[0], m_viewport[1], m_viewport[2], m_viewport[3])); +} +#else void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) { glsafe(::glViewport(0, 0, w, h)); @@ -112,6 +201,7 @@ void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) glsafe(::glGetIntegerv(GL_VIEWPORT, m_viewport.data())); #endif // ENABLE_LEGACY_OPENGL_REMOVAL } +#endif // ENABLE_RAYCAST_PICKING #if !ENABLE_LEGACY_OPENGL_REMOVAL void Camera::apply_view_matrix() @@ -170,6 +260,9 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa } #if ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + apply_projection(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second); +#else switch (m_type) { default: @@ -197,6 +290,7 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa break; } } +#endif // ENABLE_RAYCAST_PICKING #else glsafe(::glMatrixMode(GL_PROJECTION)); glsafe(::glLoadIdentity()); @@ -221,6 +315,37 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa #endif // ENABLE_LEGACY_OPENGL_REMOVAL } +#if ENABLE_RAYCAST_PICKING +void Camera::apply_projection(double left, double right, double bottom, double top, double near_z, double far_z) +{ + assert(left != right && bottom != top && near_z != far_z); + const double inv_dx = 1.0 / (right - left); + const double inv_dy = 1.0 / (top - bottom); + const double inv_dz = 1.0 / (far_z - near_z); + + switch (m_type) + { + default: + case EType::Ortho: + { + m_projection_matrix.matrix() << 2.0 * inv_dx, 0.0, 0.0, -(left + right) * inv_dx, + 0.0, 2.0 * inv_dy, 0.0, -(bottom + top) * inv_dy, + 0.0, 0.0, -2.0 * inv_dz, -(near_z + far_z) * inv_dz, + 0.0, 0.0, 0.0, 1.0; + break; + } + case EType::Perspective: + { + m_projection_matrix.matrix() << 2.0 * near_z * inv_dx, 0.0, (left + right) * inv_dx, 0.0, + 0.0, 2.0 * near_z * inv_dy, (bottom + top) * inv_dy, 0.0, + 0.0, 0.0, -(near_z + far_z) * inv_dz, -2.0 * near_z * far_z * inv_dz, + 0.0, 0.0, -1.0, 0.0; + break; + } + } +} +#endif // ENABLE_RAYCAST_PICKING + void Camera::zoom_to_box(const BoundingBoxf3& box, double margin_factor) { // Calculate the zoom factor needed to adjust the view around the given box. diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index a1d742688..b2672e14e 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -89,9 +89,23 @@ public: double get_far_z() const { return m_frustrum_zs.second; } const std::pair& get_z_range() const { return m_frustrum_zs; } +#if ENABLE_RAYCAST_PICKING + double get_near_left() const; + double get_near_right() const; + double get_near_top() const; + double get_near_bottom() const; + double get_near_width() const; + double get_near_height() const; +#endif // ENABLE_RAYCAST_PICKING + double get_fov() const; +#if ENABLE_RAYCAST_PICKING + void set_viewport(int x, int y, unsigned int w, unsigned int h); + void apply_viewport() const; +#else void apply_viewport(int x, int y, unsigned int w, unsigned int h); +#endif // ENABLE_RAYCAST_PICKING #if !ENABLE_LEGACY_OPENGL_REMOVAL void apply_view_matrix(); #endif // !ENABLE_LEGACY_OPENGL_REMOVAL @@ -99,6 +113,10 @@ public: // If larger z span is needed, pass the desired values of near and far z (negative values are ignored) void apply_projection(const BoundingBoxf3& box, double near_z = -1.0, double far_z = -1.0); +#if ENABLE_RAYCAST_PICKING + void apply_projection(double left, double right, double bottom, double top, double near_z, double far_z); +#endif // ENABLE_RAYCAST_PICKING + void zoom_to_box(const BoundingBoxf3& box, double margin_factor = DefaultZoomToBoxMarginFactor); void zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor = DefaultZoomToVolumesMarginFactor); diff --git a/src/slic3r/GUI/CoordAxes.cpp b/src/slic3r/GUI/CoordAxes.cpp index b1c3ba9e0..1ac234d5b 100644 --- a/src/slic3r/GUI/CoordAxes.cpp +++ b/src/slic3r/GUI/CoordAxes.cpp @@ -29,10 +29,11 @@ void CoordAxes::render(float emission_factor) #if ENABLE_LEGACY_OPENGL_REMOVAL auto render_axis = [this](GLShaderProgram& shader, const Transform3d& transform) { const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d matrix = camera.get_view_matrix() * transform; + const Transform3d& view_matrix = camera.get_view_matrix(); + const Transform3d matrix = view_matrix * transform; shader.set_uniform("view_model_matrix", matrix); shader.set_uniform("projection_matrix", camera.get_projection_matrix()); - shader.set_uniform("normal_matrix", (Matrix3d)matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); + shader.set_uniform("view_normal_matrix", (Matrix3d)(view_matrix.matrix().block(0, 0, 3, 3) * transform.matrix().block(0, 0, 3, 3).inverse().transpose())); m_arrow.render(); #else auto render_axis = [this](const Transform3f& transform) { diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 26df38936..4b3dd163f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1177,9 +1177,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed) , m_tab_down(false) , m_cursor_type(Standard) , m_reload_delayed(false) -#if ENABLE_RENDER_PICKING_PASS - , m_show_picking_texture(false) -#endif // ENABLE_RENDER_PICKING_PASS , m_render_sla_auxiliaries(true) , m_labels(*this) , m_slope(m_volumes) @@ -1631,7 +1628,12 @@ void GLCanvas3D::render() // and the viewport was set incorrectly, leading to tripping glAsserts further down // the road (in apply_projection). That's why the minimum size is forced to 10. Camera& camera = wxGetApp().plater()->get_camera(); +#if ENABLE_RAYCAST_PICKING + camera.set_viewport(0, 0, std::max(10u, (unsigned int)cnv_size.get_width()), std::max(10u, (unsigned int)cnv_size.get_height())); + camera.apply_viewport(); +#else camera.apply_viewport(0, 0, std::max(10u, (unsigned int)cnv_size.get_width()), std::max(10u, (unsigned int)cnv_size.get_height())); +#endif // ENABLE_RAYCAST_PICKING if (camera.requires_zoom_to_bed) { zoom_to_bed(); @@ -1654,22 +1656,22 @@ void GLCanvas3D::render() wxGetApp().imgui()->new_frame(); if (m_picking_enabled) { -#if ENABLE_NEW_RECTANGLE_SELECTION if (m_rectangle_selection.is_dragging() && !m_rectangle_selection.is_empty()) -#else - if (m_rectangle_selection.is_dragging()) -#endif // ENABLE_NEW_RECTANGLE_SELECTION // picking pass using rectangle selection _rectangular_selection_picking_pass(); 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 - if (!m_picking_enabled || !m_show_picking_texture) { -#endif // ENABLE_RENDER_PICKING_PASS - const bool is_looking_downward = camera.is_looking_downward(); // draw scene @@ -1714,9 +1716,11 @@ void GLCanvas3D::render() #else _render_bed(true, true); #endif // ENABLE_LEGACY_OPENGL_REMOVAL -#if ENABLE_RENDER_PICKING_PASS - } -#endif // ENABLE_RENDER_PICKING_PASS + +#if ENABLE_RAYCAST_PICKING_DEBUG + if (m_picking_enabled && !m_mouse.dragging && !m_gizmos.is_dragging() && !m_rectangle_selection.is_dragging()) + m_scene_raycaster.render_hit(camera); +#endif // ENABLE_RAYCAST_PICKING_DEBUG #if ENABLE_SHOW_CAMERA_TARGET _render_camera_target(); @@ -2216,6 +2220,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re #else #if ENABLE_LEGACY_OPENGL_REMOVAL volume.model.init_from(mesh); +#if ENABLE_RAYCAST_PICKING + volume.mesh_raycaster = std::make_unique(std::make_shared(mesh)); +#endif // ENABLE_RAYCAST_PICKING #else volume.indexed_vertex_array.load_mesh(mesh); #endif // ENABLE_LEGACY_OPENGL_REMOVAL @@ -2231,7 +2238,13 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re #endif // ENABLE_LEGACY_OPENGL_REMOVAL #else #if ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + 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)); +#else volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); +#endif // ENABLE_RAYCAST_PICKING #else volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); #endif // ENABLE_LEGACY_OPENGL_REMOVAL @@ -2379,6 +2392,23 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re manip->set_dirty(); } +#if ENABLE_RAYCAST_PICKING + // 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()); + } + + // refresh gizmo elements raycasters for picking + GLGizmoBase* curr_gizmo = m_gizmos.get_current(); + if (curr_gizmo != nullptr) + curr_gizmo->unregister_raycasters_for_picking(); + m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Gizmo); + if (curr_gizmo != nullptr && !m_selection.is_empty()) + curr_gizmo->register_raycasters_for_picking(); +#endif // ENABLE_RAYCAST_PICKING + // and force this canvas to be redrawn. m_dirty = true; } @@ -2751,14 +2781,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) } case 'O': case 'o': { _update_camera_zoom(-1.0); break; } -#if ENABLE_RENDER_PICKING_PASS - case 'T': - case 't': { - m_show_picking_texture = !m_show_picking_texture; - m_dirty = true; - break; - } -#endif // ENABLE_RENDER_PICKING_PASS case 'Z': case 'z': { if (!m_selection.is_empty()) @@ -2931,13 +2953,8 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) _update_selection_from_hover(); m_rectangle_selection.stop_dragging(); m_mouse.ignore_left_up = true; -#if !ENABLE_NEW_RECTANGLE_SELECTION - m_dirty = true; -#endif // !ENABLE_NEW_RECTANGLE_SELECTION } -#if ENABLE_NEW_RECTANGLE_SELECTION m_dirty = true; -#endif // ENABLE_NEW_RECTANGLE_SELECTION // set_cursor(Standard); } else if (keyCode == WXK_ALT) { @@ -2994,9 +3011,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) m_mouse.ignore_left_up = false; // set_cursor(Cross); } -#if ENABLE_NEW_RECTANGLE_SELECTION m_dirty = true; -#endif // ENABLE_NEW_RECTANGLE_SELECTION } else if (keyCode == WXK_ALT) { if (m_picking_enabled && (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)) { @@ -3418,19 +3433,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_layers_editing.state = LayersEditing::Editing; _perform_layer_editing_action(&evt); } -#if !ENABLE_NEW_RECTANGLE_SELECTION - else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled) { - if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports - && m_gizmos.get_current_type() != GLGizmosManager::FdmSupports - && m_gizmos.get_current_type() != GLGizmosManager::Seam - && m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation) { - m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect); - m_dirty = true; - } - } -#endif // !ENABLE_NEW_RECTANGLE_SELECTION else { -#if ENABLE_NEW_RECTANGLE_SELECTION const bool rectangle_selection_dragging = m_rectangle_selection.is_dragging(); if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled) { if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports && @@ -3441,39 +3444,23 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } } -#endif // ENABLE_NEW_RECTANGLE_SELECTION // Select volume in this 3D canvas. // Don't deselect a volume if layer editing is enabled or any gizmo is active. We want the object to stay selected // during the scene manipulation. -#if ENABLE_NEW_RECTANGLE_SELECTION if (m_picking_enabled && (!any_gizmo_active || !evt.CmdDown()) && (!m_hover_volume_idxs.empty() || !is_layers_editing_enabled()) && !rectangle_selection_dragging) { -#else - if (m_picking_enabled && (!any_gizmo_active || !evt.CmdDown()) && (!m_hover_volume_idxs.empty() || !is_layers_editing_enabled())) { -#endif // ENABLE_NEW_RECTANGLE_SELECTION if (evt.LeftDown() && !m_hover_volume_idxs.empty()) { int volume_idx = get_first_hover_volume_idx(); bool already_selected = m_selection.contains_volume(volume_idx); -#if ENABLE_NEW_RECTANGLE_SELECTION bool shift_down = evt.ShiftDown(); -#else - bool ctrl_down = evt.CmdDown(); -#endif // ENABLE_NEW_RECTANGLE_SELECTION Selection::IndicesList curr_idxs = m_selection.get_volume_idxs(); -#if ENABLE_NEW_RECTANGLE_SELECTION if (already_selected && shift_down) m_selection.remove(volume_idx); else { m_selection.add(volume_idx, !shift_down, true); -#else - if (already_selected && ctrl_down) - m_selection.remove(volume_idx); - else { - m_selection.add(volume_idx, !ctrl_down, true); -#endif // ENABLE_NEW_RECTANGLE_SELECTION m_mouse.drag.move_requires_threshold = !already_selected; if (already_selected) m_mouse.set_move_start_threshold_position_2D_as_invalid(); @@ -3495,11 +3482,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } -#if ENABLE_NEW_RECTANGLE_SELECTION if (!m_hover_volume_idxs.empty() && !m_rectangle_selection.is_dragging()) { -#else - if (!m_hover_volume_idxs.empty()) { -#endif // ENABLE_NEW_RECTANGLE_SELECTION if (evt.LeftDown() && m_moving_enabled && m_mouse.drag.move_volume_idx == -1) { // Only accept the initial position, if it is inside the volume bounding box. const int volume_idx = get_first_hover_volume_idx(); @@ -3576,13 +3559,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } else if (evt.Dragging() && evt.LeftIsDown() && m_picking_enabled && m_rectangle_selection.is_dragging()) { -#if ENABLE_NEW_RECTANGLE_SELECTION // keeps the mouse position updated while dragging the selection rectangle m_mouse.position = pos.cast(); m_rectangle_selection.dragging(m_mouse.position); -#else - m_rectangle_selection.dragging(pos.cast()); -#endif // ENABLE_NEW_RECTANGLE_SELECTION m_dirty = true; } else if (evt.Dragging()) { @@ -3645,6 +3624,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) { +#if ENABLE_RAYCAST_PICKING + m_mouse.position = pos.cast(); +#endif // ENABLE_RAYCAST_PICKING + if (m_layers_editing.state != LayersEditing::Unknown) { m_layers_editing.state = LayersEditing::Unknown; _stop_timer(); @@ -3669,7 +3652,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) deselect_all(); } else if (evt.RightUp()) { +#if !ENABLE_RAYCAST_PICKING m_mouse.position = pos.cast(); +#endif // !ENABLE_RAYCAST_PICKING // 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(); @@ -4626,7 +4611,12 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const Camera camera; camera.set_type(camera_type); camera.set_scene_box(scene_bounding_box()); +#if ENABLE_RAYCAST_PICKING + camera.set_viewport(0, 0, thumbnail_data.width, thumbnail_data.height); + camera.apply_viewport(); +#else camera.apply_viewport(0, 0, thumbnail_data.width, thumbnail_data.height); +#endif // ENABLE_RAYCAST_PICKING camera.zoom_to_box(volumes_box); #if ENABLE_LEGACY_OPENGL_REMOVAL @@ -4928,7 +4918,11 @@ void GLCanvas3D::_render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigne #endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT // restore the default framebuffer size to avoid flickering on the 3D scene +#if ENABLE_RAYCAST_PICKING + wxGetApp().plater()->get_camera().apply_viewport(); +#else wxGetApp().plater()->get_camera().apply_viewport(0, 0, cnv_size.get_width(), cnv_size.get_height()); +#endif // ENABLE_RAYCAST_PICKING } bool GLCanvas3D::_init_toolbars() @@ -5392,6 +5386,118 @@ void GLCanvas3D::_refresh_if_shown_on_screen() } } +#if ENABLE_RAYCAST_PICKING +void GLCanvas3D::_picking_pass() +{ +#if ENABLE_RAYCAST_PICKING_DEBUG + if (!m_picking_enabled || m_mouse.dragging || m_mouse.position == Vec2d(DBL_MAX, DBL_MAX) || m_gizmos.is_dragging()) { + ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.begin(std::string("Hit result"), ImGuiWindowFlags_AlwaysAutoResize); + imgui.text("Picking disabled"); + imgui.end(); + return; + } +#endif // ENABLE_RAYCAST_PICKING_DEBUG + + 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 = "Volume (Wipe tower)"; + else if (m_volumes.volumes[hit.raycaster_id]->volume_idx() == -int(slaposPad)) + object_type = "Volume (SLA pad)"; + else if (m_volumes.volumes[hit.raycaster_id]->volume_idx() == -int(slaposSupportTree)) + object_type = "Volume (SLA supports)"; + else if (m_volumes.volumes[hit.raycaster_id]->is_modifier) + object_type = "Volume (Modifier)"; + else + object_type = "Volume (Part)"; + break; + } + default: { 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 +5572,7 @@ void GLCanvas3D::_picking_pass() _update_volumes_hover_state(); } } +#endif // ENABLE_RAYCAST_PICKING void GLCanvas3D::_rectangular_selection_picking_pass() { @@ -5474,6 +5581,58 @@ void GLCanvas3D::_rectangular_selection_picking_pass() std::set idxs; if (m_picking_enabled) { +#if ENABLE_RAYCAST_PICKING + const size_t width = std::max(m_rectangle_selection.get_width(), 1); + const size_t height = std::max(m_rectangle_selection.get_height(), 1); + + const OpenGLManager::EFramebufferType framebuffers_type = OpenGLManager::get_framebuffers_type(); + bool use_framebuffer = framebuffers_type != OpenGLManager::EFramebufferType::Unknown; + + GLuint render_fbo = 0; + GLuint render_tex = 0; + GLuint render_depth = 0; + if (use_framebuffer) { + // setup a framebuffer which covers only the selection rectangle + if (framebuffers_type == OpenGLManager::EFramebufferType::Arb) { + glsafe(::glGenFramebuffers(1, &render_fbo)); + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, render_fbo)); + } + else { + glsafe(::glGenFramebuffersEXT(1, &render_fbo)); + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, render_fbo)); + } + glsafe(::glGenTextures(1, &render_tex)); + glsafe(::glBindTexture(GL_TEXTURE_2D, render_tex)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + if (framebuffers_type == OpenGLManager::EFramebufferType::Arb) { + glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, render_tex, 0)); + glsafe(::glGenRenderbuffers(1, &render_depth)); + glsafe(::glBindRenderbuffer(GL_RENDERBUFFER, render_depth)); + glsafe(::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height)); + glsafe(::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, render_depth)); + } + else { + glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, render_tex, 0)); + glsafe(::glGenRenderbuffersEXT(1, &render_depth)); + glsafe(::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_depth)); + glsafe(::glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height)); + glsafe(::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, render_depth)); + } + const GLenum drawBufs[] = { GL_COLOR_ATTACHMENT0 }; + glsafe(::glDrawBuffers(1, drawBufs)); + if (framebuffers_type == OpenGLManager::EFramebufferType::Arb) { + if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + use_framebuffer = false; + } + else { + if (::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) + use_framebuffer = false; + } + } +#endif // ENABLE_RAYCAST_PICKING + if (m_multisample_allowed) // This flag is often ignored by NVIDIA drivers if rendering into a screen buffer. glsafe(::glDisable(GL_MULTISAMPLE)); @@ -5483,10 +5642,49 @@ void GLCanvas3D::_rectangular_selection_picking_pass() glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); +#if ENABLE_RAYCAST_PICKING + const Camera& main_camera = wxGetApp().plater()->get_camera(); + Camera framebuffer_camera; + const Camera* camera = &main_camera; + if (use_framebuffer) { + // setup a camera which covers only the selection rectangle + const std::array& viewport = camera->get_viewport(); + const double near_left = camera->get_near_left(); + const double near_bottom = camera->get_near_bottom(); + const double near_width = camera->get_near_width(); + const double near_height = camera->get_near_height(); + + const double ratio_x = near_width / double(viewport[2]); + const double ratio_y = near_height / double(viewport[3]); + + const double rect_near_left = near_left + double(m_rectangle_selection.get_left()) * ratio_x; + const double rect_near_bottom = near_bottom + (double(viewport[3]) - double(m_rectangle_selection.get_bottom())) * ratio_y; + double rect_near_right = near_left + double(m_rectangle_selection.get_right()) * ratio_x; + double rect_near_top = near_bottom + (double(viewport[3]) - double(m_rectangle_selection.get_top())) * ratio_y; + + if (rect_near_left == rect_near_right) + rect_near_right = rect_near_left + ratio_x; + if (rect_near_bottom == rect_near_top) + rect_near_top = rect_near_bottom + ratio_y; + + framebuffer_camera.look_at(camera->get_position(), camera->get_target(), camera->get_dir_up()); + framebuffer_camera.apply_projection(rect_near_left, rect_near_right, rect_near_bottom, rect_near_top, camera->get_near_z(), camera->get_far_z()); + framebuffer_camera.set_viewport(0, 0, width, height); + framebuffer_camera.apply_viewport(); + camera = &framebuffer_camera; + } + + _render_volumes_for_picking(*camera); +#else _render_volumes_for_picking(); +#endif // ENABLE_RAYCAST_PICKING #if ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + _render_bed_for_picking(camera->get_view_matrix(), camera->get_projection_matrix(), !camera->is_looking_downward()); +#else 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 @@ -5494,6 +5692,12 @@ void GLCanvas3D::_rectangular_selection_picking_pass() if (m_multisample_allowed) glsafe(::glEnable(GL_MULTISAMPLE)); +#if ENABLE_RAYCAST_PICKING + const size_t px_count = width * height; + + const size_t left = use_framebuffer ? 0 : (size_t)m_rectangle_selection.get_left(); + const size_t top = use_framebuffer ? 0 : (size_t)get_canvas_size().get_height() - (size_t)m_rectangle_selection.get_top(); +#else int width = std::max((int)m_rectangle_selection.get_width(), 1); int height = std::max((int)m_rectangle_selection.get_height(), 1); int px_count = width * height; @@ -5501,6 +5705,7 @@ void GLCanvas3D::_rectangular_selection_picking_pass() int left = (int)m_rectangle_selection.get_left(); int top = get_canvas_size().get_height() - (int)m_rectangle_selection.get_top(); if (left >= 0 && top >= 0) { +#endif // ENABLE_RAYCAST_PICKING #define USE_PARALLEL 1 #if USE_PARALLEL struct Pixel @@ -5542,7 +5747,30 @@ void GLCanvas3D::_rectangular_selection_picking_pass() idxs.insert(volume_id); } #endif // USE_PARALLEL +#if ENABLE_RAYCAST_PICKING + if (camera != &main_camera) + main_camera.apply_viewport(); + + if (framebuffers_type == OpenGLManager::EFramebufferType::Arb) { + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0)); + if (render_depth != 0) + glsafe(::glDeleteRenderbuffers(1, &render_depth)); + if (render_fbo != 0) + glsafe(::glDeleteFramebuffers(1, &render_fbo)); + } + else if (framebuffers_type == OpenGLManager::EFramebufferType::Ext) { + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); + if (render_depth != 0) + glsafe(::glDeleteRenderbuffersEXT(1, &render_depth)); + if (render_fbo != 0) + glsafe(::glDeleteFramebuffersEXT(1, &render_fbo)); + } + + if (render_tex != 0) + glsafe(::glDeleteTextures(1, &render_tex)); +#else } +#endif // ENABLE_RAYCAST_PICKING } m_hover_volume_idxs.assign(idxs.begin(), idxs.end()); @@ -5945,7 +6173,11 @@ void GLCanvas3D::_render_overlays() #endif // !ENABLE_LEGACY_OPENGL_REMOVAL } +#if ENABLE_RAYCAST_PICKING +void GLCanvas3D::_render_volumes_for_picking(const Camera& camera) const +#else void GLCanvas3D::_render_volumes_for_picking() const +#endif // ENABLE_RAYCAST_PICKING { #if ENABLE_LEGACY_OPENGL_REMOVAL GLShaderProgram* shader = wxGetApp().get_shader("flat_clip"); @@ -5961,7 +6193,11 @@ void GLCanvas3D::_render_volumes_for_picking() const glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); #endif // !ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + const Transform3d& view_matrix = camera.get_view_matrix(); +#else const Transform3d& view_matrix = wxGetApp().plater()->get_camera().get_view_matrix(); +#endif // ENABLE_RAYCAST_PICKING for (size_t type = 0; type < 2; ++ type) { GLVolumeWithIdAndZList to_render = volumes_to_render(m_volumes.volumes, (type == 0) ? GLVolumeCollection::ERenderType::Opaque : GLVolumeCollection::ERenderType::Transparent, view_matrix); for (const GLVolumeWithIdAndZ& volume : to_render) @@ -5973,8 +6209,12 @@ void GLCanvas3D::_render_volumes_for_picking() const #if ENABLE_LEGACY_OPENGL_REMOVAL volume.first->model.set_color(picking_decode(id)); shader->start_using(); +#if ENABLE_RAYCAST_PICKING + shader->set_uniform("view_model_matrix", view_matrix * volume.first->world_matrix()); +#else const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * volume.first->world_matrix()); +#endif // ENABLE_RAYCAST_PICKING shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("volume_world_matrix", volume.first->world_matrix()); shader->set_uniform("z_range", m_volumes.get_z_range()); @@ -6447,10 +6687,6 @@ void GLCanvas3D::_update_volumes_hover_state() return; } -#if !ENABLE_NEW_RECTANGLE_SELECTION - bool selection_modifiers_only = m_selection.is_empty() || m_selection.is_any_modifier(); -#endif // !ENABLE_NEW_RECTANGLE_SELECTION - bool hover_modifiers_only = true; for (int i : m_hover_volume_idxs) { if (!m_volumes.volumes[i]->is_modifier) { @@ -6478,14 +6714,8 @@ void GLCanvas3D::_update_volumes_hover_state() if (volume.hover != GLVolume::HS_None) continue; -#if ENABLE_NEW_RECTANGLE_SELECTION bool deselect = volume.selected && ((shift_pressed && m_rectangle_selection.is_empty()) || (alt_pressed && !m_rectangle_selection.is_empty())); bool select = !volume.selected && (m_rectangle_selection.is_empty() || (shift_pressed && !m_rectangle_selection.is_empty())); -#else - bool deselect = volume.selected && ((ctrl_pressed && !shift_pressed) || alt_pressed); - // (volume->is_modifier && !selection_modifiers_only && !is_ctrl_pressed) -> allows hovering on selected modifiers belonging to selection of type Instance - bool select = (!volume.selected || (volume.is_modifier && !selection_modifiers_only && !ctrl_pressed)) && !alt_pressed; -#endif // ENABLE_NEW_RECTANGLE_SELECTION if (select || deselect) { bool as_volume = @@ -7420,42 +7650,38 @@ void GLCanvas3D::_update_selection_from_hover() } } -#if ENABLE_NEW_RECTANGLE_SELECTION if (!m_rectangle_selection.is_empty()) { -#endif // ENABLE_NEW_RECTANGLE_SELECTION - if (state == GLSelectionRectangle::EState::Select) { - bool contains_all = true; - for (int i : m_hover_volume_idxs) { - if (!m_selection.contains_volume((unsigned int)i)) { - contains_all = false; - break; + if (state == GLSelectionRectangle::EState::Select) { + bool contains_all = true; + for (int i : m_hover_volume_idxs) { + if (!m_selection.contains_volume((unsigned int)i)) { + contains_all = false; + break; + } + } + + // the selection is going to be modified (Add) + if (!contains_all) { + wxGetApp().plater()->take_snapshot(_L("Selection-Add from rectangle"), UndoRedo::SnapshotType::Selection); + selection_changed = true; } } + else { + bool contains_any = false; + for (int i : m_hover_volume_idxs) { + if (m_selection.contains_volume((unsigned int)i)) { + contains_any = true; + break; + } + } - // the selection is going to be modified (Add) - if (!contains_all) { - wxGetApp().plater()->take_snapshot(_L("Selection-Add from rectangle"), UndoRedo::SnapshotType::Selection); - selection_changed = true; - } - } - else { - bool contains_any = false; - for (int i : m_hover_volume_idxs) { - if (m_selection.contains_volume((unsigned int)i)) { - contains_any = true; - break; + // the selection is going to be modified (Remove) + if (contains_any) { + wxGetApp().plater()->take_snapshot(_L("Selection-Remove from rectangle"), UndoRedo::SnapshotType::Selection); + selection_changed = true; } } - - // the selection is going to be modified (Remove) - if (contains_any) { - wxGetApp().plater()->take_snapshot(_L("Selection-Remove from rectangle"), UndoRedo::SnapshotType::Selection); - selection_changed = true; - } } -#if ENABLE_NEW_RECTANGLE_SELECTION - } -#endif // ENABLE_NEW_RECTANGLE_SELECTION if (!selection_changed) return; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 3a6a35e47..8f4efffd8 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; @@ -533,10 +539,6 @@ private: bool m_reload_delayed; -#if ENABLE_RENDER_PICKING_PASS - bool m_show_picking_texture; -#endif // ENABLE_RENDER_PICKING_PASS - RenderStats m_render_stats; int m_imgui_undo_redo_hovered_pos{ -1 }; @@ -656,6 +658,26 @@ public: bool init(); void post_event(wxEvent &&event); +#if ENABLE_RAYCAST_PICKING + std::shared_ptr 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_raycasters_for_picking(SceneRaycaster::EType type, int id) { + m_scene_raycaster.remove_raycasters(type, id); + } + void remove_raycasters_for_picking(SceneRaycaster::EType type) { + m_scene_raycaster.remove_raycasters(type); + } + + std::vector>* get_raycasters_for_picking(SceneRaycaster::EType type) { + return m_scene_raycaster.get_raycasters(type); + } + + void set_raycaster_gizmos_on_top(bool value) { + m_scene_raycaster.set_gizmos_on_top(value); + } +#endif // ENABLE_RAYCAST_PICKING + void set_as_dirty(); void requires_check_outside_state() { m_requires_check_outside_state = true; } @@ -975,7 +997,11 @@ private: #endif // ENABLE_RENDER_SELECTION_CENTER void _check_and_update_toolbar_icon_scale(); void _render_overlays(); +#if ENABLE_RAYCAST_PICKING + void _render_volumes_for_picking(const Camera& camera) const; +#else void _render_volumes_for_picking() const; +#endif // ENABLE_RAYCAST_PICKING void _render_current_gizmo() const; void _render_gizmos_overlay(); void _render_main_toolbar(); diff --git a/src/slic3r/GUI/GLSelectionRectangle.cpp b/src/slic3r/GUI/GLSelectionRectangle.cpp index fece29a7d..9249afc83 100644 --- a/src/slic3r/GUI/GLSelectionRectangle.cpp +++ b/src/slic3r/GUI/GLSelectionRectangle.cpp @@ -29,19 +29,25 @@ namespace GUI { m_end_corner = mouse_position; } +#if ENABLE_RAYCAST_PICKING + std::vector GLSelectionRectangle::contains(const std::vector& points) const +#else std::vector GLSelectionRectangle::stop_dragging(const GLCanvas3D& canvas, const std::vector& points) +#endif // ENABLE_RAYCAST_PICKING { std::vector out; +#if !ENABLE_RAYCAST_PICKING if (!is_dragging()) return out; m_state = EState::Off; +#endif // !ENABLE_RAYCAST_PICKING const Camera& camera = wxGetApp().plater()->get_camera(); - Matrix4d modelview = camera.get_view_matrix().matrix(); - Matrix4d projection= camera.get_projection_matrix().matrix(); - Vec4i viewport(camera.get_viewport().data()); + const Matrix4d modelview = camera.get_view_matrix().matrix(); + const Matrix4d projection= camera.get_projection_matrix().matrix(); + const Vec4i viewport(camera.get_viewport().data()); // Convert our std::vector to Eigen dynamic matrix. Eigen::Matrix pts(points.size(), 3); @@ -53,11 +59,15 @@ namespace GUI { igl::project(pts, modelview, projection, viewport, projections); // bounding box created from the rectangle corners - will take care of order of the corners - BoundingBox rectangle(Points{ Point(m_start_corner.cast()), Point(m_end_corner.cast()) }); + const BoundingBox rectangle(Points{ Point(m_start_corner.cast()), Point(m_end_corner.cast()) }); // Iterate over all points and determine whether they're in the rectangle. for (int i = 0; i contains(const std::vector& points) const; +#else // Given a vector of points in world coordinates, the function returns indices of those // that are in the rectangle. It then disables the rectangle. std::vector stop_dragging(const GLCanvas3D& canvas, const std::vector& points); +#endif // ENABLE_RAYCAST_PICKING // Disables the rectangle. void stop_dragging(); @@ -36,9 +42,7 @@ public: void render(const GLCanvas3D& canvas); bool is_dragging() const { return m_state != EState::Off; } -#if ENABLE_NEW_RECTANGLE_SELECTION bool is_empty() const { return m_state == EState::Off || m_start_corner.isApprox(m_end_corner); } -#endif // ENABLE_NEW_RECTANGLE_SELECTION EState get_state() const { return m_state; } diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 88bd42a66..7ee4e7955 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -63,7 +63,7 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig m_canvas->set_context(wxGetApp().init_glcontext(*m_canvas_widget)); m_canvas->allow_multisample(OpenGLManager::can_multisample()); - // XXX: If have OpenGL + m_canvas->enable_picking(true); m_canvas->enable_moving(true); // XXX: more config from 3D.pm diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 1b6eb8233..afaff9751 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -18,19 +18,30 @@ const float GLGizmoBase::Grabber::SizeFactor = 0.05f; const float GLGizmoBase::Grabber::MinHalfSize = 1.5f; const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f; -#if ENABLE_GIZMO_GRABBER_REFACTOR +#if ENABLE_RAYCAST_PICKING +PickingModel GLGizmoBase::Grabber::s_cube; +PickingModel GLGizmoBase::Grabber::s_cone; +#else GLModel GLGizmoBase::Grabber::s_cube; GLModel GLGizmoBase::Grabber::s_cone; +#endif // ENABLE_RAYCAST_PICKING GLGizmoBase::Grabber::~Grabber() { +#if ENABLE_RAYCAST_PICKING + if (s_cube.model.is_initialized()) + s_cube.model.reset(); + + if (s_cone.model.is_initialized()) + s_cone.model.reset(); +#else if (s_cube.is_initialized()) s_cube.reset(); if (s_cone.is_initialized()) s_cone.reset(); +#endif // ENABLE_RAYCAST_PICKING } -#endif // ENABLE_GIZMO_GRABBER_REFACTOR float GLGizmoBase::Grabber::get_half_size(float size) const { @@ -42,7 +53,26 @@ float GLGizmoBase::Grabber::get_dragging_half_size(float size) const return get_half_size(size) * DraggingScaleFactor; } +#if ENABLE_RAYCAST_PICKING +void GLGizmoBase::Grabber::register_raycasters_for_picking(int id) +{ + picking_id = id; + // registration will happen on next call to render() +} + +void GLGizmoBase::Grabber::unregister_raycasters_for_picking() +{ + wxGetApp().plater()->canvas3D()->remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, picking_id); + picking_id = -1; + raycasters = { nullptr }; +} +#endif // ENABLE_RAYCAST_PICKING + +#if ENABLE_RAYCAST_PICKING +void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color) +#else void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color, bool picking) +#endif // ENABLE_RAYCAST_PICKING { #if ENABLE_LEGACY_OPENGL_REMOVAL GLShaderProgram* shader = wxGetApp().get_current_shader(); @@ -50,106 +80,148 @@ void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color, boo return; #endif // ENABLE_LEGACY_OPENGL_REMOVAL -#if ENABLE_GIZMO_GRABBER_REFACTOR - if (!s_cube.is_initialized()) { +#if ENABLE_RAYCAST_PICKING + if (!s_cube.model.is_initialized()) { #else - if (!m_cube.is_initialized()) { -#endif // ENABLE_GIZMO_GRABBER_REFACTOR + if (!s_cube.is_initialized()) { +#endif // ENABLE_RAYCAST_PICKING // This cannot be done in constructor, OpenGL is not yet // initialized at that point (on Linux at least). indexed_triangle_set its = its_make_cube(1.0, 1.0, 1.0); its_translate(its, -0.5f * Vec3f::Ones()); #if ENABLE_LEGACY_OPENGL_REMOVAL -#if ENABLE_GIZMO_GRABBER_REFACTOR +#if ENABLE_RAYCAST_PICKING + s_cube.model.init_from(its); + s_cube.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); +#else s_cube.init_from(its); +#endif // ENABLE_RAYCAST_PICKING #else - m_cube.init_from(its); -#endif // ENABLE_GIZMO_GRABBER_REFACTOR -#else -#if ENABLE_GIZMO_GRABBER_REFACTOR s_cube.init_from(its, BoundingBoxf3{ { -0.5, -0.5, -0.5 }, { 0.5, 0.5, 0.5 } }); -#else - m_cube.init_from(its, BoundingBoxf3{ { -0.5, -0.5, -0.5 }, { 0.5, 0.5, 0.5 } }); -#endif // ENABLE_GIZMO_GRABBER_REFACTOR #endif // ENABLE_LEGACY_OPENGL_REMOVAL } -#if ENABLE_GIZMO_GRABBER_REFACTOR +#if ENABLE_RAYCAST_PICKING + if (!s_cone.model.is_initialized()) { + indexed_triangle_set its = its_make_cone(0.375, 1.5, double(PI) / 18.0); + s_cone.model.init_from(its); + s_cone.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + } +#else if (!s_cone.is_initialized()) s_cone.init_from(its_make_cone(0.375, 1.5, double(PI) / 18.0)); -#endif // ENABLE_GIZMO_GRABBER_REFACTOR +#endif // ENABLE_RAYCAST_PICKING -#if ENABLE_GIZMO_GRABBER_REFACTOR const float half_size = dragging ? get_dragging_half_size(size) : get_half_size(size); -#else - const float fullsize = 2.0f * (dragging ? get_dragging_half_size(size) : get_half_size(size)); -#endif // ENABLE_GIZMO_GRABBER_REFACTOR #if ENABLE_LEGACY_OPENGL_REMOVAL -#if ENABLE_GIZMO_GRABBER_REFACTOR +#if ENABLE_RAYCAST_PICKING + s_cube.model.set_color(render_color); + s_cone.model.set_color(render_color); +#else s_cube.set_color(render_color); s_cone.set_color(render_color); -#else - m_cube.set_color(render_color); -#endif // ENABLE_GIZMO_GRABBER_REFACTOR +#endif // ENABLE_RAYCAST_PICKING + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); +#if ENABLE_RAYCAST_PICKING const Transform3d& view_matrix = camera.get_view_matrix(); -#if ENABLE_GIZMO_GRABBER_REFACTOR - const Transform3d model_matrix = matrix * Geometry::assemble_transform(center, angles, 2.0 * half_size * Vec3d::Ones()); + const Matrix3d view_matrix_no_offset = view_matrix.matrix().block(0, 0, 3, 3); + std::vector elements_matrices(GRABBER_ELEMENTS_MAX_COUNT, Transform3d::Identity()); + elements_matrices[0] = matrix * Geometry::assemble_transform(center, angles, 2.0 * half_size * Vec3d::Ones()); + Transform3d view_model_matrix = view_matrix * elements_matrices[0]; #else - const Transform3d model_matrix = matrix * Geometry::assemble_transform(center, angles, fullsize * Vec3d::Ones()); -#endif // ENABLE_GIZMO_GRABBER_REFACTOR + const Transform3d& view_matrix = camera.get_view_matrix(); + const Transform3d model_matrix = matrix * Geometry::assemble_transform(center, angles, 2.0 * half_size * Vec3d::Ones()); const Transform3d view_model_matrix = view_matrix * model_matrix; +#endif // ENABLE_RAYCAST_PICKING shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); +#if ENABLE_RAYCAST_PICKING + Matrix3d view_normal_matrix = view_matrix_no_offset * elements_matrices[0].matrix().block(0, 0, 3, 3).inverse().transpose(); +#else const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); +#endif // ENABLE_RAYCAST_PICKING shader->set_uniform("view_normal_matrix", view_normal_matrix); #else -#if ENABLE_GIZMO_GRABBER_REFACTOR s_cube.set_color(-1, render_color); s_cone.set_color(-1, render_color); -#else - m_cube.set_color(-1, render_color); -#endif // ENABLE_GIZMO_GRABBER_REFACTOR glsafe(::glPushMatrix()); glsafe(::glTranslated(center.x(), center.y(), center.z())); glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); -#if ENABLE_GIZMO_GRABBER_REFACTOR glsafe(::glScaled(2.0 * half_size, 2.0 * half_size, 2.0 * half_size)); -#else - glsafe(::glScaled(fullsize, fullsize, fullsize)); -#endif // ENABLE_GIZMO_GRABBER_REFACTOR #endif // ENABLE_LEGACY_OPENGL_REMOVAL -#if ENABLE_GIZMO_GRABBER_REFACTOR +#if ENABLE_RAYCAST_PICKING + s_cube.model.render(); + + auto render_extension = [&view_matrix, &view_matrix_no_offset, shader](const Transform3d& matrix) { + const Transform3d view_model_matrix = view_matrix * matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix_no_offset * matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + s_cone.model.render(); + }; +#else s_cube.render(); +#endif // ENABLE_RAYCAST_PICKING #if ENABLE_LEGACY_OPENGL_REMOVAL if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosX)) != 0) { +#if ENABLE_RAYCAST_PICKING + elements_matrices[1] = elements_matrices[0] * Geometry::assemble_transform(Vec3d::UnitX(), Vec3d(0.0, 0.5 * double(PI), 0.0)); + render_extension(elements_matrices[1]); +#else shader->set_uniform("view_model_matrix", view_model_matrix * Geometry::assemble_transform(Vec3d::UnitX(), Vec3d(0.0, 0.5 * double(PI), 0.0))); s_cone.render(); +#endif // ENABLE_RAYCAST_PICKING } if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegX)) != 0) { +#if ENABLE_RAYCAST_PICKING + elements_matrices[2] = elements_matrices[0] * Geometry::assemble_transform(-Vec3d::UnitX(), Vec3d(0.0, -0.5 * double(PI), 0.0)); + render_extension(elements_matrices[2]); +#else shader->set_uniform("view_model_matrix", view_model_matrix * Geometry::assemble_transform(-Vec3d::UnitX(), Vec3d(0.0, -0.5 * double(PI), 0.0))); s_cone.render(); +#endif // ENABLE_RAYCAST_PICKING } if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosY)) != 0) { +#if ENABLE_RAYCAST_PICKING + elements_matrices[3] = elements_matrices[0] * Geometry::assemble_transform(Vec3d::UnitY(), Vec3d(-0.5 * double(PI), 0.0, 0.0)); + render_extension(elements_matrices[3]); +#else shader->set_uniform("view_model_matrix", view_model_matrix * Geometry::assemble_transform(Vec3d::UnitY(), Vec3d(-0.5 * double(PI), 0.0, 0.0))); s_cone.render(); +#endif // ENABLE_RAYCAST_PICKING } if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegY)) != 0) { +#if ENABLE_RAYCAST_PICKING + elements_matrices[4] = elements_matrices[0] * Geometry::assemble_transform(-Vec3d::UnitY(), Vec3d(0.5 * double(PI), 0.0, 0.0)); + render_extension(elements_matrices[4]); +#else shader->set_uniform("view_model_matrix", view_model_matrix * Geometry::assemble_transform(-Vec3d::UnitY(), Vec3d(0.5 * double(PI), 0.0, 0.0))); s_cone.render(); +#endif // ENABLE_RAYCAST_PICKING } if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosZ)) != 0) { +#if ENABLE_RAYCAST_PICKING + elements_matrices[5] = elements_matrices[0] * Geometry::assemble_transform(Vec3d::UnitZ()); + render_extension(elements_matrices[5]); +#else shader->set_uniform("view_model_matrix", view_model_matrix * Geometry::assemble_transform(Vec3d::UnitZ())); s_cone.render(); +#endif // ENABLE_RAYCAST_PICKING } if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegZ)) != 0) { +#if ENABLE_RAYCAST_PICKING + elements_matrices[6] = elements_matrices[0] * Geometry::assemble_transform(-Vec3d::UnitZ(), Vec3d(double(PI), 0.0, 0.0)); + render_extension(elements_matrices[6]); +#else shader->set_uniform("view_model_matrix", view_model_matrix * Geometry::assemble_transform(-Vec3d::UnitZ(), Vec3d(double(PI), 0.0, 0.0))); s_cone.render(); +#endif // ENABLE_RAYCAST_PICKING } #else if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosX)) != 0) { @@ -194,26 +266,41 @@ void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color, boo glsafe(::glPopMatrix()); } #endif // ENABLE_LEGACY_OPENGL_REMOVAL -#else - m_cube.render(); -#endif // ENABLE_GIZMO_GRABBER_REFACTOR #if !ENABLE_LEGACY_OPENGL_REMOVAL glsafe(::glPopMatrix()); #endif // !ENABLE_LEGACY_OPENGL_REMOVAL + +#if ENABLE_RAYCAST_PICKING + if (raycasters[0] == nullptr) { + GLCanvas3D& canvas = *wxGetApp().plater()->canvas3D(); + raycasters[0] = canvas.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, picking_id, *s_cube.mesh_raycaster, elements_matrices[0]); + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosX)) != 0) + raycasters[1] = canvas.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, picking_id, *s_cone.mesh_raycaster, elements_matrices[1]); + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegX)) != 0) + raycasters[2] = canvas.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, picking_id, *s_cone.mesh_raycaster, elements_matrices[2]); + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosY)) != 0) + raycasters[3] = canvas.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, picking_id, *s_cone.mesh_raycaster, elements_matrices[3]); + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegY)) != 0) + raycasters[4] = canvas.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, picking_id, *s_cone.mesh_raycaster, elements_matrices[4]); + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosZ)) != 0) + raycasters[5] = canvas.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, picking_id, *s_cone.mesh_raycaster, elements_matrices[5]); + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegZ)) != 0) + raycasters[6] = canvas.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, picking_id, *s_cone.mesh_raycaster, elements_matrices[6]); + } + else { + for (size_t i = 0; i < GRABBER_ELEMENTS_MAX_COUNT; ++i) { + if (raycasters[i] != nullptr) + raycasters[i]->set_transform(elements_matrices[i]); + } + } +#endif // ENABLE_RAYCAST_PICKING } GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : m_parent(parent) - , m_group_id(-1) - , m_state(Off) - , m_shortcut_key(0) , m_icon_filename(icon_filename) , m_sprite_id(sprite_id) - , m_hover_id(-1) - , m_dragging(false) , m_imgui(wxGetApp().imgui()) - , m_first_input_window_render(true) - , m_dirty(false) { } @@ -237,6 +324,21 @@ bool GLGizmoBase::update_items_state() return res; } +#if ENABLE_RAYCAST_PICKING +void GLGizmoBase::register_grabbers_for_picking() +{ + for (size_t i = 0; i < m_grabbers.size(); ++i) { + m_grabbers[i].register_raycasters_for_picking((m_group_id >= 0) ? m_group_id : i); + } +} + +void GLGizmoBase::unregister_grabbers_for_picking() +{ + for (size_t i = 0; i < m_grabbers.size(); ++i) { + m_grabbers[i].unregister_raycasters_for_picking(); + } +} +#else ColorRGBA GLGizmoBase::picking_color_component(unsigned int id) const { id = BASE_ID - id; @@ -245,6 +347,7 @@ ColorRGBA GLGizmoBase::picking_color_component(unsigned int id) const return picking_decode(id); } +#endif // ENABLE_RAYCAST_PICKING void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const { @@ -265,6 +368,7 @@ void GLGizmoBase::render_grabbers(float size) const shader->stop_using(); } +#if !ENABLE_RAYCAST_PICKING void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const { #if ENABLE_LEGACY_OPENGL_REMOVAL @@ -285,6 +389,7 @@ void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const } #endif // ENABLE_LEGACY_OPENGL_REMOVAL } +#endif // !ENABLE_RAYCAST_PICKING // help function to process grabbers // call start_dragging, stop_dragging, on_dragging diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 0ca0afca8..09766bbac 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -6,6 +6,10 @@ #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/GLModel.hpp" +#if ENABLE_RAYCAST_PICKING +#include "slic3r/GUI/MeshUtils.hpp" +#include "slic3r/GUI/SceneRaycaster.hpp" +#endif // ENABLE_RAYCAST_PICKING #include @@ -37,8 +41,8 @@ public: // Starting value for ids to avoid clashing with ids used by GLVolumes // (254 is choosen to leave some space for forward compatibility) static const unsigned int BASE_ID = 255 * 255 * 254; + static const unsigned int GRABBER_ELEMENTS_MAX_COUNT = 7; -#if ENABLE_GIZMO_GRABBER_REFACTOR enum class EGrabberExtension { None = 0, @@ -49,7 +53,6 @@ public: PosZ = 1 << 4, NegZ = 1 << 5, }; -#endif // ENABLE_GIZMO_GRABBER_REFACTOR protected: struct Grabber @@ -66,30 +69,45 @@ protected: Transform3d matrix{ Transform3d::Identity() }; #endif // ENABLE_LEGACY_OPENGL_REMOVAL ColorRGBA color{ ColorRGBA::WHITE() }; -#if ENABLE_GIZMO_GRABBER_REFACTOR EGrabberExtension extensions{ EGrabberExtension::None }; -#endif // ENABLE_GIZMO_GRABBER_REFACTOR +#if ENABLE_RAYCAST_PICKING + // the picking id shared by all the elements + int picking_id{ -1 }; + std::array, GRABBER_ELEMENTS_MAX_COUNT> raycasters = { nullptr }; +#endif // ENABLE_RAYCAST_PICKING Grabber() = default; -#if ENABLE_GIZMO_GRABBER_REFACTOR ~Grabber(); -#endif // ENABLE_GIZMO_GRABBER_REFACTOR +#if ENABLE_RAYCAST_PICKING + void render(bool hover, float size) { render(size, hover ? complementary(color) : color); } +#else void render(bool hover, float size) { render(size, hover ? complementary(color) : color, false); } void render_for_picking(float size) { render(size, color, true); } +#endif // ENABLE_RAYCAST_PICKING float get_half_size(float size) const; float get_dragging_half_size(float size) const; - private: - void render(float size, const ColorRGBA& render_color, bool picking); +#if ENABLE_RAYCAST_PICKING + void register_raycasters_for_picking(int id); + void unregister_raycasters_for_picking(); +#endif // ENABLE_RAYCAST_PICKING -#if ENABLE_GIZMO_GRABBER_REFACTOR + private: +#if ENABLE_RAYCAST_PICKING + void render(float size, const ColorRGBA& render_color); +#else + void render(float size, const ColorRGBA& render_color, bool picking); +#endif // ENABLE_RAYCAST_PICKING + +#if ENABLE_RAYCAST_PICKING + static PickingModel s_cube; + static PickingModel s_cone; +#else static GLModel s_cube; static GLModel s_cone; -#else - GLModel m_cube; -#endif // ENABLE_GIZMO_GRABBER_REFACTOR +#endif // ENABLE_RAYCAST_PICKING }; public: @@ -112,17 +130,19 @@ public: protected: GLCanvas3D& m_parent; - int m_group_id; // TODO: remove only for rotate - EState m_state; - int m_shortcut_key; + + int m_group_id{ -1 }; // TODO: remove only for rotate + EState m_state{ Off }; + int m_shortcut_key{ 0 }; std::string m_icon_filename; unsigned int m_sprite_id; - int m_hover_id; - bool m_dragging; + int m_hover_id{ -1 }; + bool m_dragging{ false }; mutable std::vector m_grabbers; ImGuiWrapper* m_imgui; - bool m_first_input_window_render; - CommonGizmosDataPool* m_c; + bool m_first_input_window_render{ true }; + CommonGizmosDataPool* m_c{ nullptr }; + public: GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, @@ -163,7 +183,9 @@ public: bool update_items_state(); void render() { on_render(); } +#if !ENABLE_RAYCAST_PICKING void render_for_picking() { on_render_for_picking(); } +#endif // !ENABLE_RAYCAST_PICKING void render_input_window(float x, float y, float bottom_limit); /// @@ -184,6 +206,12 @@ public: /// Keep information about mouse click /// Return True when use the information and don't want to propagate it otherwise False. virtual bool on_mouse(const wxMouseEvent &mouse_event) { return false; } + +#if ENABLE_RAYCAST_PICKING + void register_raycasters_for_picking() { register_grabbers_for_picking(); on_register_raycasters_for_picking(); } + void unregister_raycasters_for_picking() { unregister_grabbers_for_picking(); on_unregister_raycasters_for_picking(); } +#endif // ENABLE_RAYCAST_PICKING + protected: virtual bool on_init() = 0; virtual void on_load(cereal::BinaryInputArchive& ar) {} @@ -203,16 +231,27 @@ protected: virtual void on_dragging(const UpdateData& data) {} virtual void on_render() = 0; +#if !ENABLE_RAYCAST_PICKING virtual void on_render_for_picking() = 0; +#endif // !ENABLE_RAYCAST_PICKING virtual void on_render_input_window(float x, float y, float bottom_limit) {} +#if ENABLE_RAYCAST_PICKING + void register_grabbers_for_picking(); + void unregister_grabbers_for_picking(); + virtual void on_register_raycasters_for_picking() {} + virtual void on_unregister_raycasters_for_picking() {} +#else // Returns the picking color for the given id, based on the BASE_ID constant // No check is made for clashing with other picking color (i.e. GLVolumes) ColorRGBA picking_color_component(unsigned int id) const; +#endif // ENABLE_RAYCAST_PICKING void render_grabbers(const BoundingBoxf3& box) const; void render_grabbers(float size) const; +#if !ENABLE_RAYCAST_PICKING void render_grabbers_for_picking(const BoundingBoxf3& box) const; +#endif // !ENABLE_RAYCAST_PICKING std::string format(float value, unsigned int decimals) const; @@ -235,7 +274,7 @@ protected: private: // Flag for dirty visible state of Gizmo // When True then need new rendering - bool m_dirty; + bool m_dirty{ false }; }; } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index a83fcde2c..96259792b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -269,11 +269,24 @@ void GLGizmoCut::on_render() #endif // !ENABLE_GL_CORE_PROFILE } +#if ENABLE_RAYCAST_PICKING +void GLGizmoCut::on_register_raycasters_for_picking() +{ + // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(true); +} + +void GLGizmoCut::on_unregister_raycasters_for_picking() +{ + m_parent.set_raycaster_gizmos_on_top(false); +} +#else void GLGizmoCut::on_render_for_picking() { glsafe(::glDisable(GL_DEPTH_TEST)); render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); } +#endif // ENABLE_RAYCAST_PICKING void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index d81b53e5f..ee3bc78e9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -68,7 +68,12 @@ protected: virtual void on_start_dragging() override; virtual void on_dragging(const UpdateData& data) override; virtual void on_render() override; +#if ENABLE_RAYCAST_PICKING + virtual void on_register_raycasters_for_picking() override; + virtual void on_unregister_raycasters_for_picking() override; +#else virtual void on_render_for_picking() override; +#endif // ENABLE_RAYCAST_PICKING virtual void on_render_input_window(float x, float y, float bottom_limit) override; private: diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index fea23034b..23889c9d7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -133,8 +133,13 @@ void GLGizmoFlatten::on_render() update_planes(); for (int i = 0; i < (int)m_planes.size(); ++i) { #if ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + m_planes[i].vbo.model.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); + m_planes[i].vbo.model.render(); +#else m_planes[i].vbo.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); m_planes[i].vbo.render(); +#endif // ENABLE_RAYCAST_PICKING #else glsafe(::glColor4fv(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR.data() : DEFAULT_PLANE_COLOR.data())); if (m_planes[i].vbo.has_VBOs()) @@ -154,6 +159,29 @@ void GLGizmoFlatten::on_render() #endif // ENABLE_LEGACY_OPENGL_REMOVAL } +#if ENABLE_RAYCAST_PICKING +void GLGizmoFlatten::on_register_raycasters_for_picking() +{ + // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(true); + + if (!m_planes.empty()) { + const Selection& selection = m_parent.get_selection(); + const Transform3d matrix = Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * + selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); + + for (int i = 0; i < (int)m_planes.size(); ++i) { + m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_planes[i].vbo.mesh_raycaster, matrix); + } + } +} + +void GLGizmoFlatten::on_unregister_raycasters_for_picking() +{ + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); + m_parent.set_raycaster_gizmos_on_top(false); +} +#else void GLGizmoFlatten::on_render_for_picking() { const Selection& selection = m_parent.get_selection(); @@ -204,12 +232,16 @@ void GLGizmoFlatten::on_render_for_picking() shader->stop_using(); #endif // ENABLE_LEGACY_OPENGL_REMOVAL } +#endif // ENABLE_RAYCAST_PICKING void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) { if (model_object != m_old_model_object) { m_planes.clear(); m_planes_valid = false; +#if ENABLE_RAYCAST_PICKING + on_unregister_raycasters_for_picking(); +#endif // ENABLE_RAYCAST_PICKING } } @@ -226,6 +258,9 @@ void GLGizmoFlatten::update_planes() } ch = ch.convex_hull_3d(); m_planes.clear(); +#if ENABLE_RAYCAST_PICKING + on_unregister_raycasters_for_picking(); +#endif // ENABLE_RAYCAST_PICKING #if ENABLE_WORLD_COORDINATE const Transform3d inst_matrix = mo->instances.front()->get_matrix_no_offset(); #else @@ -422,6 +457,19 @@ void GLGizmoFlatten::update_planes() // the vertices in order, so triangulation is trivial. for (auto& plane : m_planes) { #if ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + indexed_triangle_set its; + its.vertices.reserve(plane.vertices.size()); + its.indices.reserve(plane.vertices.size() / 3); + for (size_t i = 0; i < plane.vertices.size(); ++i) { + its.vertices.emplace_back((Vec3f)plane.vertices[i].cast()); + } + for (size_t i = 1; i < plane.vertices.size() - 1; ++i) { + its.indices.emplace_back(0, i, i + 1); // triangle fan + } + plane.vbo.model.init_from(its); + plane.vbo.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); +#else GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::TriangleFan, GLModel::Geometry::EVertexLayout::P3N3 }; init_data.reserve_vertices(plane.vertices.size()); @@ -432,6 +480,7 @@ void GLGizmoFlatten::update_planes() init_data.add_index((unsigned int)i); } plane.vbo.init_from(std::move(init_data)); +#endif // ENABLE_RAYCAST_PICKING #else plane.vbo.reserve(plane.vertices.size()); for (const auto& vert : plane.vertices) @@ -446,6 +495,9 @@ void GLGizmoFlatten::update_planes() plane.vertices.shrink_to_fit(); } +#if ENABLE_RAYCAST_PICKING + on_register_raycasters_for_picking(); +#endif // ENABLE_RAYCAST_PICKING m_planes_valid = true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp index 3244056a4..32d119fbc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp @@ -4,6 +4,9 @@ #include "GLGizmoBase.hpp" #if ENABLE_LEGACY_OPENGL_REMOVAL #include "slic3r/GUI/GLModel.hpp" +#if ENABLE_RAYCAST_PICKING +#include "slic3r/GUI/MeshUtils.hpp" +#endif // ENABLE_RAYCAST_PICKING #else #include "slic3r/GUI/3DScene.hpp" #endif // ENABLE_LEGACY_OPENGL_REMOVAL @@ -26,12 +29,19 @@ private: struct PlaneData { std::vector vertices; // should be in fact local in update_planes() #if ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + PickingModel vbo; +#else GLModel vbo; +#endif // ENABLE_RAYCAST_PICKING #else GLIndexedVertexArray vbo; #endif // ENABLE_LEGACY_OPENGL_REMOVAL Vec3d normal; float area; +#if ENABLE_RAYCAST_PICKING + int picking_id{ -1 }; +#endif // ENABLE_RAYCAST_PICKING }; // This holds information to decide whether recalculation is necessary: @@ -67,7 +77,12 @@ protected: std::string on_get_name() const override; bool on_is_activable() const override; void on_render() override; +#if ENABLE_RAYCAST_PICKING + virtual void on_register_raycasters_for_picking() override; + virtual void on_unregister_raycasters_for_picking() override; +#else void on_render_for_picking() override; +#endif // ENABLE_RAYCAST_PICKING void on_set_state() override; CommonGizmosDataID on_get_requirements() const override; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 3485075ec..b95d25f2d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -55,6 +55,12 @@ void GLGizmoHollow::data_changed() } if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh()) m_holes_in_drilled_mesh = mo->sla_drain_holes; +#if ENABLE_RAYCAST_PICKING + if (m_raycasters.empty()) + on_register_raycasters_for_picking(); + else + update_raycasters_for_picking_transform(); +#endif // ENABLE_RAYCAST_PICKING } } @@ -62,8 +68,16 @@ void GLGizmoHollow::data_changed() void GLGizmoHollow::on_render() { +#if ENABLE_RAYCAST_PICKING + if (!m_cylinder.model.is_initialized()) { + indexed_triangle_set its = its_make_cylinder(1.0, 1.0); + m_cylinder.model.init_from(its); + m_cylinder.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + } +#else if (!m_cylinder.is_initialized()) m_cylinder.init_from(its_make_cylinder(1.0, 1.0)); +#endif // ENABLE_RAYCAST_PICKING const Selection& selection = m_parent.get_selection(); const CommonGizmosDataObjects::SelectionInfo* sel_info = m_c->selection_info(); @@ -80,7 +94,11 @@ void GLGizmoHollow::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); if (selection.is_from_single_instance()) +#if ENABLE_RAYCAST_PICKING + render_points(selection); +#else render_points(selection, false); +#endif // ENABLE_RAYCAST_PICKING m_selection_rectangle.render(m_parent); m_c->object_clipper()->render_cut(); @@ -89,22 +107,49 @@ void GLGizmoHollow::on_render() glsafe(::glDisable(GL_BLEND)); } +#if ENABLE_RAYCAST_PICKING +void GLGizmoHollow::on_register_raycasters_for_picking() +{ + assert(m_raycasters.empty()); + set_sla_auxiliary_volumes_picking_state(false); + const CommonGizmosDataObjects::SelectionInfo* info = m_c->selection_info(); + if (info != nullptr && !info->model_object()->sla_drain_holes.empty()) { + const sla::DrainHoles& drain_holes = info->model_object()->sla_drain_holes; + for (int i = 0; i < (int)drain_holes.size(); ++i) { + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cylinder.mesh_raycaster, Transform3d::Identity())); + } + update_raycasters_for_picking_transform(); + } +} + +void GLGizmoHollow::on_unregister_raycasters_for_picking() +{ + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); + m_raycasters.clear(); + set_sla_auxiliary_volumes_picking_state(true); +} +#else void GLGizmoHollow::on_render_for_picking() { const Selection& selection = m_parent.get_selection(); -//#if ENABLE_RENDER_PICKING_PASS -// m_z_shift = selection.get_first_volume()->get_sla_shift_z(); -//#endif - glsafe(::glEnable(GL_DEPTH_TEST)); render_points(selection, true); } +#endif // ENABLE_RAYCAST_PICKING +#if ENABLE_RAYCAST_PICKING +void GLGizmoHollow::render_points(const Selection& selection) +#else void GLGizmoHollow::render_points(const Selection& selection, bool picking) +#endif // ENABLE_RAYCAST_PICKING { #if ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); +#else GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat") : wxGetApp().get_shader("gouraud_light"); +#endif // ENABLE_RAYCAST_PICKING if (shader == nullptr) return; @@ -118,7 +163,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) #endif // ENABLE_LEGACY_OPENGL_REMOVAL const GLVolume* vol = selection.get_first_volume(); - Geometry::Transformation trafo = vol->get_instance_transformation() * vol->get_volume_transformation(); + const Transform3d trafo = vol->world_matrix(); #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_WORLD_COORDINATE @@ -126,13 +171,9 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) #else const Transform3d instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); #endif // ENABLE_WORLD_COORDINATE - const Transform3d instance_matrix = Geometry::translation_transform(m_c->selection_info()->get_sla_shift() * Vec3d::UnitZ()) * trafo.get_matrix(); - const Camera& camera = wxGetApp().plater()->get_camera(); const Transform3d& view_matrix = camera.get_view_matrix(); - const Transform3d& projection_matrix = camera.get_projection_matrix(); - - shader->set_uniform("projection_matrix", projection_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #else const Transform3d& instance_scaling_matrix_inverse = trafo.get_matrix(true, true, false, true).inverse(); const Transform3d& instance_matrix = trafo.get_matrix(); @@ -150,13 +191,22 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) const sla::DrainHole& drain_hole = drain_holes[i]; const bool point_selected = m_selected[i]; +#if ENABLE_RAYCAST_PICKING + const bool clipped = is_mesh_point_clipped(drain_hole.pos.cast()); + m_raycasters[i]->set_active(!clipped); + if (clipped) + continue; +#else if (is_mesh_point_clipped(drain_hole.pos.cast())) continue; +#endif // ENABLE_RAYCAST_PICKING // First decide about the color of the point. +#if !ENABLE_RAYCAST_PICKING if (picking) render_color = picking_color_component(i); else { +#endif // !ENABLE_RAYCAST_PICKING if (size_t(m_hover_id) == i) render_color = ColorRGBA::CYAN(); else if (m_c->hollowed_mesh() && @@ -164,14 +214,20 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) m_c->hollowed_mesh()->get_drainholes()[i].failed) { render_color = { 1.0f, 0.0f, 0.0f, 0.5f }; } - else // neither hover nor picking + else render_color = point_selected ? ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f) : ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f); +#if !ENABLE_RAYCAST_PICKING } +#endif // !ENABLE_RAYCAST_PICKING #if ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + m_cylinder.model.set_color(render_color); +#else m_cylinder.set_color(render_color); +#endif // ENABLE_RAYCAST_PICKING // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. - const Transform3d hole_matrix = Geometry::assemble_transform(drain_hole.pos.cast()) * instance_scaling_matrix_inverse; + const Transform3d hole_matrix = Geometry::translation_transform(drain_hole.pos.cast()) * instance_scaling_matrix_inverse; #else const_cast(&m_cylinder)->set_color(-1, render_color); // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. @@ -188,8 +244,8 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); const Eigen::AngleAxisd aa(q); #if ENABLE_LEGACY_OPENGL_REMOVAL - const Transform3d model_matrix = instance_matrix * hole_matrix * Transform3d(aa.toRotationMatrix()) * - Geometry::assemble_transform(-drain_hole.height * Vec3d::UnitZ(), Vec3d::Zero(), Vec3d(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); + const Transform3d model_matrix = trafo * hole_matrix * Transform3d(aa.toRotationMatrix()) * + Geometry::translation_transform(-drain_hole.height * Vec3d::UnitZ()) * Geometry::scale_transform(Vec3d(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); shader->set_uniform("view_model_matrix", view_matrix * model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); @@ -198,7 +254,11 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) glsafe(::glTranslated(0., 0., -drain_hole.height)); glsafe(::glScaled(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); #endif // ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + m_cylinder.model.render(); +#else m_cylinder.render(); +#endif // ENABLE_RAYCAST_PICKING if (vol->is_left_handed()) glsafe(::glFrontFace(GL_CCW)); @@ -317,7 +377,7 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos if (m_selection_empty) { std::pair pos_and_normal; if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add drainage hole")); mo->sla_drain_holes.emplace_back(pos_and_normal.first, -pos_and_normal.second, m_new_hole_radius, m_new_hole_height); @@ -325,6 +385,10 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos assert(m_selected.size() == mo->sla_drain_holes.size()); m_parent.set_as_dirty(); m_wait_for_up_event = true; +#if ENABLE_RAYCAST_PICKING + on_unregister_raycasters_for_picking(); + on_register_raycasters_for_picking(); +#endif // ENABLE_RAYCAST_PICKING } else return false; @@ -349,7 +413,12 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos // Now ask the rectangle which of the points are inside. std::vector points_inside; +#if ENABLE_RAYCAST_PICKING + std::vector points_idxs = m_selection_rectangle.contains(points); + m_selection_rectangle.stop_dragging(); +#else std::vector points_idxs = m_selection_rectangle.stop_dragging(m_parent, points); +#endif // ENABLE_RAYCAST_PICKING for (size_t idx : points_idxs) points_inside.push_back(points[idx].cast()); @@ -443,6 +512,10 @@ void GLGizmoHollow::delete_selected_points() } } +#if ENABLE_RAYCAST_PICKING + on_unregister_raycasters_for_picking(); + on_register_raycasters_for_picking(); +#endif // ENABLE_RAYCAST_PICKING select_point(NoPoints); } @@ -518,6 +591,49 @@ void GLGizmoHollow::hollow_mesh(bool postpone_error_messages) }); } +#if ENABLE_RAYCAST_PICKING +void GLGizmoHollow::set_sla_auxiliary_volumes_picking_state(bool state) +{ + std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); + if (raycasters != nullptr) { + const Selection& selection = m_parent.get_selection(); + const Selection::IndicesList ids = selection.get_volume_idxs(); + for (unsigned int id : ids) { + const GLVolume* v = selection.get_volume(id); + if (v->is_sla_pad() || v->is_sla_support()) { + auto it = std::find_if(raycasters->begin(), raycasters->end(), [v](std::shared_ptr item) { return item->get_raycaster() == v->mesh_raycaster.get(); }); + if (it != raycasters->end()) + (*it)->set_active(state); + } + } + } +} + +void GLGizmoHollow::update_raycasters_for_picking_transform() +{ + const CommonGizmosDataObjects::SelectionInfo* info = m_c->selection_info(); + if (info != nullptr) { + const sla::DrainHoles& drain_holes = info->model_object()->sla_drain_holes; + if (!drain_holes.empty()) { + assert(!m_raycasters.empty()); + + const GLVolume* vol = m_parent.get_selection().get_first_volume(); + const Transform3d instance_scaling_matrix_inverse = vol->get_instance_transformation().get_scaling_factor_matrix().inverse(); + + for (size_t i = 0; i < drain_holes.size(); ++i) { + const sla::DrainHole& drain_hole = drain_holes[i]; + const Transform3d hole_matrix = Geometry::translation_transform(drain_hole.pos.cast()) * instance_scaling_matrix_inverse; + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); + const Eigen::AngleAxisd aa(q); + const Transform3d matrix = vol->world_matrix() * hole_matrix * Transform3d(aa.toRotationMatrix()) * + Geometry::translation_transform(-drain_hole.height * Vec3d::UnitZ()) * Geometry::scale_transform(Vec3d(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); + m_raycasters[i]->set_transform(matrix); + } + } + } +} +#endif // ENABLE_RAYCAST_PICKING std::vector> GLGizmoHollow::get_config_options(const std::vector& keys) const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index feab896cc..3c16d9288 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -28,8 +28,7 @@ private: public: GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - virtual ~GLGizmoHollow() = default; - void data_changed() override; + void data_changed() override; bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); void delete_selected_points(); bool is_selection_rectangle_dragging() const { @@ -43,18 +42,37 @@ public: /// Keep information about mouse click /// Return True when use the information otherwise False. bool on_mouse(const wxMouseEvent &mouse_event) override; -private: + +protected: bool on_init() override; void on_render() override; +#if ENABLE_RAYCAST_PICKING + virtual void on_register_raycasters_for_picking() override; + virtual void on_unregister_raycasters_for_picking() override; +#else void on_render_for_picking() override; +#endif // ENABLE_RAYCAST_PICKING +private: +#if ENABLE_RAYCAST_PICKING + void render_points(const Selection& selection); +#else void render_points(const Selection& selection, bool picking = false); +#endif // ENABLE_RAYCAST_PICKING void hollow_mesh(bool postpone_error_messages = false); - bool unsaved_changes() const; +#if ENABLE_RAYCAST_PICKING + void set_sla_auxiliary_volumes_picking_state(bool state); + void update_raycasters_for_picking_transform(); +#endif // ENABLE_RAYCAST_PICKING ObjectID m_old_mo_id = -1; +#if ENABLE_RAYCAST_PICKING + PickingModel m_cylinder; + std::vector> m_raycasters; +#else GLModel m_cylinder; +#endif // ENABLE_RAYCAST_PICKING float m_new_hole_radius = 2.f; // Size of a new hole. float m_new_hole_height = 6.f; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 0fd6fb5e8..10b5c5870 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -61,15 +61,11 @@ bool GLGizmoMove3D::on_init() { for (int i = 0; i < 3; ++i) { m_grabbers.push_back(Grabber()); -#if ENABLE_GIZMO_GRABBER_REFACTOR m_grabbers.back().extensions = GLGizmoBase::EGrabberExtension::PosZ; -#endif // ENABLE_GIZMO_GRABBER_REFACTOR } -#if ENABLE_GIZMO_GRABBER_REFACTOR m_grabbers[0].angles = { 0.0, 0.5 * double(PI), 0.0 }; m_grabbers[1].angles = { -0.5 * double(PI), 0.0, 0.0 }; -#endif // ENABLE_GIZMO_GRABBER_REFACTOR m_shortcut_key = WXK_CONTROL_M; @@ -149,11 +145,6 @@ void GLGizmoMove3D::on_dragging(const UpdateData& data) void GLGizmoMove3D::on_render() { -#if !ENABLE_GIZMO_GRABBER_REFACTOR - if (!m_cone.is_initialized()) - m_cone.init_from(its_make_cone(1.0, 1.0, double(PI) / 18.0)); -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_DEPTH_TEST)); @@ -300,24 +291,8 @@ void GLGizmoMove3D::on_render() // draw grabbers #if ENABLE_WORLD_COORDINATE render_grabbers(m_bounding_box); -#if !ENABLE_GIZMO_GRABBER_REFACTOR - for (unsigned int i = 0; i < 3; ++i) { - if (m_grabbers[i].enabled) -#if ENABLE_LEGACY_OPENGL_REMOVAL - render_grabber_extension((Axis)i, base_matrix, m_bounding_box, false); -#else - render_grabber_extension((Axis)i, m_bounding_box, false); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - } -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR #else render_grabbers(box); -#if !ENABLE_GIZMO_GRABBER_REFACTOR - for (unsigned int i = 0; i < 3; ++i) { - if (m_grabbers[i].enabled) - render_grabber_extension((Axis)i, box, false); - } -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR #endif // ENABLE_WORLD_COORDINATE } else { @@ -376,17 +351,6 @@ void GLGizmoMove3D::on_render() m_grabbers[m_hover_id].render(true, mean_size); shader->stop_using(); } -#if !ENABLE_GIZMO_GRABBER_REFACTOR -#if ENABLE_WORLD_COORDINATE -#if ENABLE_LEGACY_OPENGL_REMOVAL - render_grabber_extension((Axis)m_hover_id, base_matrix, m_bounding_box, false); -#else - render_grabber_extension((Axis)m_hover_id, m_bounding_box, false); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -#else - render_grabber_extension((Axis)m_hover_id, box, false); -#endif // ENABLE_WORLD_COORDINATE -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR } #if ENABLE_WORLD_COORDINATE @@ -396,6 +360,18 @@ void GLGizmoMove3D::on_render() #endif // ENABLE_WORLD_COORDINATE } +#if ENABLE_RAYCAST_PICKING +void GLGizmoMove3D::on_register_raycasters_for_picking() +{ + // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(true); +} + +void GLGizmoMove3D::on_unregister_raycasters_for_picking() +{ + m_parent.set_raycaster_gizmos_on_top(false); +} +#else void GLGizmoMove3D::on_render_for_picking() { glsafe(::glDisable(GL_DEPTH_TEST)); @@ -412,29 +388,15 @@ void GLGizmoMove3D::on_render_for_picking() #endif // ENABLE_LEGACY_OPENGL_REMOVAL render_grabbers_for_picking(m_bounding_box); #if ENABLE_LEGACY_OPENGL_REMOVAL -#if !ENABLE_GIZMO_GRABBER_REFACTOR - render_grabber_extension(X, base_matrix, m_bounding_box, true); - render_grabber_extension(Y, base_matrix, m_bounding_box, true); - render_grabber_extension(Z, base_matrix, m_bounding_box, true); -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR #else -#if !ENABLE_GIZMO_GRABBER_REFACTOR - render_grabber_extension(X, m_bounding_box, true); - render_grabber_extension(Y, m_bounding_box, true); - render_grabber_extension(Z, m_bounding_box, true); -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR glsafe(::glPopMatrix()); #endif // ENABLE_LEGACY_OPENGL_REMOVAL #else const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); render_grabbers_for_picking(box); -#if !ENABLE_GIZMO_GRABBER_REFACTOR - render_grabber_extension(X, box, true); - render_grabber_extension(Y, box, true); - render_grabber_extension(Z, box, true); -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR #endif // ENABLE_WORLD_COORDINATE } +#endif // ENABLE_RAYCAST_PICKING double GLGizmoMove3D::calc_projection(const UpdateData& data) const { @@ -462,70 +424,6 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const return projection; } -#if !ENABLE_GIZMO_GRABBER_REFACTOR -#if ENABLE_WORLD_COORDINATE && ENABLE_LEGACY_OPENGL_REMOVAL -void GLGizmoMove3D::render_grabber_extension(Axis axis, const Transform3d& base_matrix, const BoundingBoxf3& box, bool picking) -#else -void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) -#endif // ENABLE_WORLD_COORDINATE && ENABLE_LEGACY_OPENGL_REMOVAL -{ - const Vec3d box_size = box.size(); - const float mean_size = float((box_size.x() + box_size.y() + box_size.z()) / 3.0); - const double size = m_dragging ? double(m_grabbers[axis].get_dragging_half_size(mean_size)) : double(m_grabbers[axis].get_half_size(mean_size)); - -#if ENABLE_LEGACY_OPENGL_REMOVAL - GLShaderProgram* shader = wxGetApp().get_shader(picking ? "flat" : "gouraud_light"); -#else - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - if (shader == nullptr) - return; - -#if ENABLE_LEGACY_OPENGL_REMOVAL - m_cone.set_color((!picking && m_hover_id != -1) ? complementary(m_grabbers[axis].color) : m_grabbers[axis].color); - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); - const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d& view_matrix = camera.get_view_matrix(); - Transform3d model_matrix = Geometry::assemble_transform(m_grabbers[axis].center); - if (axis == X) - model_matrix = model_matrix * Geometry::assemble_transform(Vec3d::Zero(), 0.5 * PI * Vec3d::UnitY()); - else if (axis == Y) - model_matrix = model_matrix * Geometry::assemble_transform(Vec3d::Zero(), -0.5 * PI * Vec3d::UnitX()); - model_matrix = model_matrix * Geometry::assemble_transform(2.0 * size * Vec3d::UnitZ(), Vec3d::Zero(), Vec3d(0.75 * size, 0.75 * size, 3.0 * size)); - - shader->set_uniform("view_model_matrix", view_matrix * model_matrix); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); -#else - m_cone.set_color(-1, (!picking && m_hover_id != -1) ? complementary(m_grabbers[axis].color) : m_grabbers[axis].color); - if (!picking) { - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); - } - - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_grabbers[axis].center.x(), m_grabbers[axis].center.y(), m_grabbers[axis].center.z())); - if (axis == X) - glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); - else if (axis == Y) - glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); - - glsafe(::glTranslated(0.0, 0.0, 2.0 * size)); - glsafe(::glScaled(0.75 * size, 0.75 * size, 3.0 * size)); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - - m_cone.render(); - -#if !ENABLE_LEGACY_OPENGL_REMOVAL - glsafe(::glPopMatrix()); - if (! picking) -#endif // !ENABLE_LEGACY_OPENGL_REMOVAL - shader->stop_using(); -} -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR - #if ENABLE_WORLD_COORDINATE #if ENABLE_LEGACY_OPENGL_REMOVAL Transform3d GLGizmoMove3D::local_transform(const Selection& selection) const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index 1fac329ff..b4414f401 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -25,9 +25,6 @@ class GLGizmoMove3D : public GLGizmoBase Vec3d m_starting_box_center{ Vec3d::Zero() }; Vec3d m_starting_box_bottom_center{ Vec3d::Zero() }; -#if !ENABLE_GIZMO_GRABBER_REFACTOR - GLModel m_cone; -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR #if ENABLE_LEGACY_OPENGL_REMOVAL struct GrabberConnection { @@ -65,7 +62,12 @@ protected: void on_stop_dragging() override; void on_dragging(const UpdateData& data) override; void on_render() override; +#if ENABLE_RAYCAST_PICKING + virtual void on_register_raycasters_for_picking() override; + virtual void on_unregister_raycasters_for_picking() override; +#else void on_render_for_picking() override; +#endif // ENABLE_RAYCAST_PICKING private: double calc_projection(const UpdateData& data) const; @@ -77,17 +79,9 @@ private: #endif // ENABLE_LEGACY_OPENGL_REMOVAL void calc_selection_box_and_center(); #endif // ENABLE_WORLD_COORDINATE -#if !ENABLE_GIZMO_GRABBER_REFACTOR -#if ENABLE_WORLD_COORDINATE && ENABLE_LEGACY_OPENGL_REMOVAL - void render_grabber_extension(Axis axis, const Transform3d& base_matrix, const BoundingBoxf3& box, bool picking); -#else - void render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking); -#endif // ENABLE_WORLD_COORDINATE && ENABLE_LEGACY_OPENGL_REMOVAL -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR }; - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index b65d5d531..04f24b20d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -138,7 +138,10 @@ private: ObjectID m_old_mo_id; size_t m_old_volumes_size = 0; void on_render() override {} +#if !ENABLE_RAYCAST_PICKING void on_render_for_picking() override {} +#endif // !ENABLE_RAYCAST_PICKING + public: GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); ~GLGizmoPainterBase() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index d938097b8..ea9ca078d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -94,9 +94,7 @@ void GLGizmoRotate::disable_grabber() { m_grabbers[0].enabled = false; } bool GLGizmoRotate::on_init() { m_grabbers.push_back(Grabber()); -#if ENABLE_GIZMO_GRABBER_REFACTOR m_grabbers.back().extensions = (GLGizmoBase::EGrabberExtension)(int(GLGizmoBase::EGrabberExtension::PosY) | int(GLGizmoBase::EGrabberExtension::NegY)); -#endif // ENABLE_GIZMO_GRABBER_REFACTOR return true; } @@ -152,11 +150,6 @@ void GLGizmoRotate::on_render() if (!m_grabbers.front().enabled) return; -#if !ENABLE_GIZMO_GRABBER_REFACTOR - if (!m_cone.is_initialized()) - m_cone.init_from(its_make_cone(1.0, 1.0, double(PI) / 12.0)); -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR - const Selection& selection = m_parent.get_selection(); #if !ENABLE_WORLD_COORDINATE const BoundingBoxf3& box = selection.get_bounding_box(); @@ -249,14 +242,8 @@ void GLGizmoRotate::on_render() #if ENABLE_WORLD_COORDINATE render_grabber(m_bounding_box); -#if !ENABLE_GIZMO_GRABBER_REFACTOR - render_grabber_extension(m_bounding_box, false); -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR #else render_grabber(box); -#if !ENABLE_GIZMO_GRABBER_REFACTOR - render_grabber_extension(box, false); -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR #endif // ENABLE_WORLD_COORDINATE #if !ENABLE_LEGACY_OPENGL_REMOVAL @@ -264,6 +251,7 @@ void GLGizmoRotate::on_render() #endif // !ENABLE_LEGACY_OPENGL_REMOVAL } +#if !ENABLE_RAYCAST_PICKING void GLGizmoRotate::on_render_for_picking() { const Selection& selection = m_parent.get_selection(); @@ -279,21 +267,16 @@ void GLGizmoRotate::on_render_for_picking() #if ENABLE_WORLD_COORDINATE render_grabbers_for_picking(m_bounding_box); -#if !ENABLE_GIZMO_GRABBER_REFACTOR - render_grabber_extension(m_bounding_box, true); -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR #else const BoundingBoxf3& box = selection.get_bounding_box(); render_grabbers_for_picking(box); -#if !ENABLE_GIZMO_GRABBER_REFACTOR - render_grabber_extension(box, true); -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR #endif // ENABLE_WORLD_COORDINATE #if !ENABLE_LEGACY_OPENGL_REMOVAL glsafe(::glPopMatrix()); #endif // !ENABLE_LEGACY_OPENGL_REMOVAL } +#endif // !ENABLE_RAYCAST_PICKING #if ENABLE_WORLD_COORDINATE void GLGizmoRotate::init_data_from_selection(const Selection& selection) @@ -662,81 +645,6 @@ void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) render_grabbers(box); } -#if !ENABLE_GIZMO_GRABBER_REFACTOR -void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool picking) -{ - const float mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 3.0); - const double size = m_dragging ? double(m_grabbers.front().get_dragging_half_size(mean_size)) : double(m_grabbers.front().get_half_size(mean_size)); - -#if ENABLE_LEGACY_OPENGL_REMOVAL - GLShaderProgram* shader = wxGetApp().get_shader(picking ? "flat" : "gouraud_light"); - if (shader == nullptr) - return; - - m_cone.set_color((!picking && m_hover_id != -1) ? complementary(m_grabbers.front().color) : m_grabbers.front().color); - - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); -#else - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); - if (shader == nullptr) - return; - - m_cone.set_color(-1, (!picking && m_hover_id != -1) ? complementary(m_grabbers.front().color) : m_grabbers.front().color); - if (!picking) { - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); - } -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - - const Vec3d& center = m_grabbers.front().center; - -#if ENABLE_LEGACY_OPENGL_REMOVAL - const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d& view_matrix = camera.get_view_matrix(); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - - Transform3d model_matrix = m_grabbers.front().matrix * Geometry::assemble_transform(center, Vec3d(0.5 * PI, 0.0, m_angle)) * - Geometry::assemble_transform(2.0 * size * Vec3d::UnitZ(), Vec3d::Zero(), Vec3d(0.75 * size, 0.75 * size, 3.0 * size)); - - shader->set_uniform("view_model_matrix", view_matrix * model_matrix); - Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); -#else - glsafe(::glPushMatrix()); - glsafe(::glTranslated(center.x(), center.y(), center.z())); - glsafe(::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0)); - glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); - glsafe(::glTranslated(0.0, 0.0, 2.0 * size)); - glsafe(::glScaled(0.75 * size, 0.75 * size, 3.0 * size)); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - m_cone.render(); -#if ENABLE_LEGACY_OPENGL_REMOVAL - model_matrix = m_grabbers.front().matrix * Geometry::assemble_transform(center, Vec3d(-0.5 * PI, 0.0, m_angle)) * - Geometry::assemble_transform(2.0 * size * Vec3d::UnitZ(), Vec3d::Zero(), Vec3d(0.75 * size, 0.75 * size, 3.0 * size)); - - shader->set_uniform("view_model_matrix", view_matrix * model_matrix); - view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); -#else - glsafe(::glPopMatrix()); - glsafe(::glPushMatrix()); - glsafe(::glTranslated(center.x(), center.y(), center.z())); - glsafe(::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0)); - glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); - glsafe(::glTranslated(0.0, 0.0, 2.0 * size)); - glsafe(::glScaled(0.75 * size, 0.75 * size, 3.0 * size)); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - m_cone.render(); - -#if !ENABLE_LEGACY_OPENGL_REMOVAL - glsafe(::glPopMatrix()); - if (! picking) -#endif // !ENABLE_LEGACY_OPENGL_REMOVAL - shader->stop_using(); -} -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR - #if ENABLE_LEGACY_OPENGL_REMOVAL Transform3d GLGizmoRotate::local_transform(const Selection& selection) const { @@ -974,6 +882,25 @@ void GLGizmoRotate3D::on_render() m_gizmos[Z].render(); } +#if ENABLE_RAYCAST_PICKING +void GLGizmoRotate3D::on_register_raycasters_for_picking() +{ + // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(true); + for (GLGizmoRotate& g : m_gizmos) { + g.register_raycasters_for_picking(); + } +} + +void GLGizmoRotate3D::on_unregister_raycasters_for_picking() +{ + for (GLGizmoRotate& g : m_gizmos) { + g.unregister_raycasters_for_picking(); + } + m_parent.set_raycaster_gizmos_on_top(false); +} +#endif // ENABLE_RAYCAST_PICKING + GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui, State & state, const Alignment &alignment) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index 2f66e3433..daf8283ff 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -6,6 +6,7 @@ namespace Slic3r { namespace GUI { class Selection; + class GLGizmoRotate : public GLGizmoBase { static const float Offset; @@ -39,9 +40,6 @@ private: Transform3d m_orient_matrix{ Transform3d::Identity() }; #endif // ENABLE_WORLD_COORDINATE -#if !ENABLE_GIZMO_GRABBER_REFACTOR - GLModel m_cone; -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR #if ENABLE_LEGACY_OPENGL_REMOVAL GLModel m_circle; GLModel m_scale; @@ -87,13 +85,16 @@ public: /// Return True when use the information otherwise False. bool on_mouse(const wxMouseEvent &mouse_event) override; void dragging(const UpdateData &data); + protected: bool on_init() override; std::string on_get_name() const override { return ""; } void on_start_dragging() override; void on_dragging(const UpdateData &data) override; void on_render() override; +#if !ENABLE_RAYCAST_PICKING void on_render_for_picking() override; +#endif // !ENABLE_RAYCAST_PICKING private: #if ENABLE_LEGACY_OPENGL_REMOVAL @@ -111,9 +112,6 @@ private: void render_angle() const; #endif // ENABLE_LEGACY_OPENGL_REMOVAL void render_grabber(const BoundingBoxf3& box); -#if !ENABLE_GIZMO_GRABBER_REFACTOR - void render_grabber_extension(const BoundingBoxf3& box, bool picking); -#endif // !ENABLE_GIZMO_GRABBER_REFACTOR #if ENABLE_LEGACY_OPENGL_REMOVAL Transform3d local_transform(const Selection& selection) const; @@ -181,11 +179,16 @@ protected: void on_dragging(const UpdateData &data) override; void on_render() override; +#if ENABLE_RAYCAST_PICKING + virtual void on_register_raycasters_for_picking() override; + virtual void on_unregister_raycasters_for_picking() override; +#else void on_render_for_picking() override { for (GLGizmoRotate& g : m_gizmos) { g.render_for_picking(); } } +#endif // ENABLE_RAYCAST_PICKING void on_render_input_window(float x, float y, float bottom_limit) override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index cb526c296..0f68cb761 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -643,6 +643,18 @@ void GLGizmoScale3D::on_render() #endif // ENABLE_WORLD_COORDINATE } +#if ENABLE_RAYCAST_PICKING +void GLGizmoScale3D::on_register_raycasters_for_picking() +{ + // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(true); +} + +void GLGizmoScale3D::on_unregister_raycasters_for_picking() +{ + m_parent.set_raycaster_gizmos_on_top(false); +} +#else void GLGizmoScale3D::on_render_for_picking() { glsafe(::glDisable(GL_DEPTH_TEST)); @@ -664,6 +676,7 @@ void GLGizmoScale3D::on_render_for_picking() render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); #endif // ENABLE_WORLD_COORDINATE } +#endif // !ENABLE_RAYCAST_PICKING #if ENABLE_LEGACY_OPENGL_REMOVAL void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int id_2, const ColorRGBA& color) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index d6bef4475..b66da89bc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -93,7 +93,12 @@ protected: virtual void on_stop_dragging() override; virtual void on_dragging(const UpdateData& data) override; virtual void on_render() override; +#if ENABLE_RAYCAST_PICKING + virtual void on_register_raycasters_for_picking() override; + virtual void on_unregister_raycasters_for_picking() override; +#else virtual void on_render_for_picking() override; +#endif // ENABLE_RAYCAST_PICKING private: #if ENABLE_LEGACY_OPENGL_REMOVAL diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 52269ba86..ae2814ee9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -38,7 +38,9 @@ protected: // must implement virtual bool on_init() override { return true;}; virtual void on_render() override; - virtual void on_render_for_picking() override{}; +#if !ENABLE_RAYCAST_PICKING + virtual void on_render_for_picking() override{}; +#endif // !ENABLE_RAYCAST_PICKING CommonGizmosDataID on_get_requirements() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index fd003bba1..265b994d8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -22,6 +22,10 @@ #include "libslic3r/PresetBundle.hpp" #include "libslic3r/SLAPrint.hpp" +#if ENABLE_RAYCAST_PICKING +static const double CONE_RADIUS = 0.25; +static const double CONE_HEIGHT = 0.75; +#endif // ENABLE_RAYCAST_PICKING namespace Slic3r { namespace GUI { @@ -47,9 +51,11 @@ bool GLGizmoSlaSupports::on_init() m_desc["clipping_of_view"] = _L("Clipping of view")+ ": "; m_desc["reset_direction"] = _L("Reset direction"); +#if !ENABLE_RAYCAST_PICKING m_cone.init_from(its_make_cone(1., 1., 2 * PI / 24)); m_cylinder.init_from(its_make_cylinder(1., 1., 2 * PI / 24.)); m_sphere.init_from(its_make_sphere(1., (2 * M_PI) / 24.)); +#endif // !ENABLE_RAYCAST_PICKING return true; } @@ -73,16 +79,36 @@ void GLGizmoSlaSupports::data_changed() if (mo->sla_points_status == sla::PointsStatus::Generating) get_data_from_backend(); } + +#if ENABLE_RAYCAST_PICKING + if (m_raycasters.empty()) + on_register_raycasters_for_picking(); + else + update_raycasters_for_picking_transform(); +#endif // ENABLE_RAYCAST_PICKING } void GLGizmoSlaSupports::on_render() { +#if ENABLE_RAYCAST_PICKING + if (!m_sphere.model.is_initialized()) { + indexed_triangle_set its = its_make_sphere(1.0, double(PI) / 12.0); + m_sphere.model.init_from(its); + m_sphere.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + } + if (!m_cone.model.is_initialized()) { + indexed_triangle_set its = its_make_cone(1.0, 1.0, double(PI) / 12.0); + m_cone.model.init_from(its); + m_cone.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + } +#else if (!m_cone.is_initialized()) m_cone.init_from(its_make_cone(1.0, 1.0, double(PI) / 12.0)); if (!m_sphere.is_initialized()) m_sphere.init_from(its_make_sphere(1.0, double(PI) / 12.0)); +#endif // ENABLE_RAYCAST_PICKING if (!m_cylinder.is_initialized()) m_cylinder.init_from(its_make_cylinder(1.0, 1.0, double(PI) / 12.0)); @@ -101,7 +127,11 @@ void GLGizmoSlaSupports::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); if (selection.is_from_single_instance()) +#if ENABLE_RAYCAST_PICKING + render_points(selection); +#else render_points(selection, false); +#endif // ENABLE_RAYCAST_PICKING m_selection_rectangle.render(m_parent); m_c->object_clipper()->render_cut(); @@ -110,15 +140,41 @@ void GLGizmoSlaSupports::on_render() glsafe(::glDisable(GL_BLEND)); } +#if ENABLE_RAYCAST_PICKING +void GLGizmoSlaSupports::on_register_raycasters_for_picking() +{ + assert(m_raycasters.empty()); + set_sla_auxiliary_volumes_picking_state(false); + if (m_editing_mode && !m_editing_cache.empty()) { + for (size_t i = 0; i < m_editing_cache.size(); ++i) { + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_sphere.mesh_raycaster, Transform3d::Identity()), + m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cone.mesh_raycaster, Transform3d::Identity())); + } + update_raycasters_for_picking_transform(); + } +} + +void GLGizmoSlaSupports::on_unregister_raycasters_for_picking() +{ + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); + m_raycasters.clear(); + set_sla_auxiliary_volumes_picking_state(true); +} +#else void GLGizmoSlaSupports::on_render_for_picking() { const Selection& selection = m_parent.get_selection(); //glsafe(::glEnable(GL_DEPTH_TEST)); render_points(selection, true); } +#endif // ENABLE_RAYCAST_PICKING +#if ENABLE_RAYCAST_PICKING +void GLGizmoSlaSupports::render_points(const Selection& selection) +#else void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) +#endif // ENABLE_RAYCAST_PICKING { const size_t cache_size = m_editing_mode ? m_editing_cache.size() : m_normal_cache.size(); @@ -130,7 +186,11 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) return; #if ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); +#else GLShaderProgram* shader = wxGetApp().get_shader(picking ? "flat" : "gouraud_light"); +#endif // ENABLE_RAYCAST_PICKING if (shader == nullptr) return; @@ -147,19 +207,16 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) #endif // ENABLE_LEGACY_OPENGL_REMOVAL const GLVolume* vol = selection.get_first_volume(); - Geometry::Transformation transformation(vol->get_instance_transformation().get_matrix() * vol->get_volume_transformation().get_matrix()); + const Geometry::Transformation transformation(vol->world_matrix()); #if ENABLE_WORLD_COORDINATE const Transform3d instance_scaling_matrix_inverse = transformation.get_scaling_factor_matrix().inverse(); #else const Transform3d& instance_scaling_matrix_inverse = transformation.get_matrix(true, true, false, true).inverse(); #endif // ENABLE_WORLD_COORDINATE #if ENABLE_LEGACY_OPENGL_REMOVAL - const Transform3d instance_matrix = Geometry::assemble_transform(m_c->selection_info()->get_sla_shift() * Vec3d::UnitZ()) * transformation.get_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); const Transform3d& view_matrix = camera.get_view_matrix(); - const Transform3d& projection_matrix = camera.get_projection_matrix(); - - shader->set_uniform("projection_matrix", projection_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #else const Transform3d& instance_matrix = transformation.get_matrix(); const float z_shift = m_c->selection_info()->get_sla_shift(); @@ -173,13 +230,25 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const sla::SupportPoint& support_point = m_editing_mode ? m_editing_cache[i].support_point : m_normal_cache[i]; const bool point_selected = m_editing_mode ? m_editing_cache[i].selected : false; +#if ENABLE_RAYCAST_PICKING + const bool clipped = is_mesh_point_clipped(support_point.pos.cast()); + if (!m_raycasters.empty()) { + m_raycasters[i].first->set_active(!clipped); + m_raycasters[i].second->set_active(!clipped); + } + if (clipped) + continue; +#else if (is_mesh_point_clipped(support_point.pos.cast())) continue; +#endif // ENABLE_RAYCAST_PICKING // First decide about the color of the point. +#if !ENABLE_RAYCAST_PICKING if (picking) render_color = picking_color_component(i); else { +#endif // !ENABLE_RAYCAST_PICKING if (size_t(m_hover_id) == i && m_editing_mode) // ignore hover state unless editing mode is active render_color = { 0.f, 1.f, 1.f, 1.f }; else { // neigher hover nor picking @@ -196,12 +265,21 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) else render_color = { 0.5f, 0.5f, 0.5f, 1.f }; } +#if !ENABLE_RAYCAST_PICKING } +#endif // !ENABLE_RAYCAST_PICKING #if ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + m_cone.model.set_color(render_color); + m_sphere.model.set_color(render_color); +#else m_cone.set_color(render_color); m_sphere.set_color(render_color); +#endif // ENABLE_RAYCAST_PICKING +#if !ENABLE_RAYCAST_PICKING if (!picking) +#endif // !ENABLE_RAYCAST_PICKING #else m_cone.set_color(-1, render_color); m_sphere.set_color(-1, render_color); @@ -211,7 +289,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. #if ENABLE_LEGACY_OPENGL_REMOVAL - const Transform3d support_matrix = Geometry::assemble_transform(support_point.pos.cast()) * instance_scaling_matrix_inverse; + const Transform3d support_matrix = Geometry::translation_transform(support_point.pos.cast()) * instance_scaling_matrix_inverse; #else glsafe(::glPushMatrix()); glsafe(::glTranslatef(support_point.pos.x(), support_point.pos.y(), support_point.pos.z())); @@ -231,12 +309,19 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); const Eigen::AngleAxisd aa(q); +#if !ENABLE_RAYCAST_PICKING const double cone_radius = 0.25; // mm const double cone_height = 0.75; +#endif // !ENABLE_RAYCAST_PICKING #if ENABLE_LEGACY_OPENGL_REMOVAL - const Transform3d model_matrix = instance_matrix * support_matrix * Transform3d(aa.toRotationMatrix()) * + const Transform3d model_matrix = vol->world_matrix() * support_matrix * Transform3d(aa.toRotationMatrix()) * +#if ENABLE_RAYCAST_PICKING + Geometry::assemble_transform((CONE_HEIGHT + support_point.head_front_radius * RenderPointScale) * Vec3d::UnitZ(), + Vec3d(PI, 0.0, 0.0), Vec3d(CONE_RADIUS, CONE_RADIUS, CONE_HEIGHT)); +#else Geometry::assemble_transform((cone_height + support_point.head_front_radius * RenderPointScale) * Vec3d::UnitZ(), Vec3d(PI, 0.0, 0.0), Vec3d(cone_radius, cone_radius, cone_height)); +#endif // ENABLE_RAYCAST_PICKING shader->set_uniform("view_model_matrix", view_matrix * model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); @@ -248,7 +333,11 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) glsafe(::glRotated(180., 1., 0., 0.)); glsafe(::glScaled(cone_radius, cone_radius, cone_height)); #endif // ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + m_cone.model.render(); +#else m_cone.render(); +#endif // ENABLE_RAYCAST_PICKING #if !ENABLE_LEGACY_OPENGL_REMOVAL glsafe(::glPopMatrix()); #endif // !ENABLE_LEGACY_OPENGL_REMOVAL @@ -256,9 +345,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const double radius = (double)support_point.head_front_radius * RenderPointScale; #if ENABLE_LEGACY_OPENGL_REMOVAL - const Transform3d model_matrix = instance_matrix * support_matrix * - Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), radius * Vec3d::Ones()); - + const Transform3d model_matrix = vol->world_matrix() * support_matrix * Geometry::scale_transform(radius); shader->set_uniform("view_model_matrix", view_matrix * model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); @@ -266,7 +353,11 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) glsafe(::glPushMatrix()); glsafe(::glScaled(radius, radius, radius)); #endif // ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + m_sphere.model.render(); +#else m_sphere.render(); +#endif // ENABLE_RAYCAST_PICKING #if !ENABLE_LEGACY_OPENGL_REMOVAL glsafe(::glPopMatrix()); #endif // !ENABLE_LEGACY_OPENGL_REMOVAL @@ -280,7 +371,11 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) } // Now render the drain holes: +#if ENABLE_RAYCAST_PICKING + if (has_holes) { +#else if (has_holes && ! picking) { +#endif // ENABLE_RAYCAST_PICKING render_color = { 0.7f, 0.7f, 0.7f, 0.7f }; #if ENABLE_LEGACY_OPENGL_REMOVAL m_cylinder.set_color(render_color); @@ -294,7 +389,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) continue; #if ENABLE_LEGACY_OPENGL_REMOVAL - const Transform3d hole_matrix = Geometry::assemble_transform(drain_hole.pos.cast()) * instance_scaling_matrix_inverse; + const Transform3d hole_matrix = Geometry::translation_transform(drain_hole.pos.cast()) * instance_scaling_matrix_inverse; #else // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. glsafe(::glPushMatrix()); @@ -310,9 +405,8 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); const Eigen::AngleAxisd aa(q); #if ENABLE_LEGACY_OPENGL_REMOVAL - const Transform3d model_matrix = instance_matrix * hole_matrix * Transform3d(aa.toRotationMatrix()) * - Geometry::assemble_transform(-drain_hole.height * Vec3d::UnitZ(), Vec3d::Zero(), Vec3d(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); - + const Transform3d model_matrix = vol->world_matrix() * hole_matrix * Transform3d(aa.toRotationMatrix()) * + Geometry::translation_transform(-drain_hole.height * Vec3d::UnitZ()) * Geometry::scale_transform(Vec3d(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); shader->set_uniform("view_model_matrix", view_matrix * model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); @@ -450,6 +544,10 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous m_editing_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second); m_parent.set_as_dirty(); m_wait_for_up_event = true; +#if ENABLE_RAYCAST_PICKING + on_unregister_raycasters_for_picking(); + on_register_raycasters_for_picking(); +#endif // ENABLE_RAYCAST_PICKING } else return false; @@ -474,7 +572,12 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous // Now ask the rectangle which of the points are inside. std::vector points_inside; +#if ENABLE_RAYCAST_PICKING + std::vector points_idxs = m_selection_rectangle.contains(points); + m_selection_rectangle.stop_dragging(); +#else std::vector points_idxs = m_selection_rectangle.stop_dragging(m_parent, points); +#endif // ENABLE_RAYCAST_PICKING for (size_t idx : points_idxs) points_inside.push_back(points[idx].cast()); @@ -604,6 +707,11 @@ void GLGizmoSlaSupports::delete_selected_points(bool force) } } +#if ENABLE_RAYCAST_PICKING + on_unregister_raycasters_for_picking(); + on_register_raycasters_for_picking(); +#endif // ENABLE_RAYCAST_PICKING + select_point(NoPoints); } @@ -1281,6 +1389,9 @@ void GLGizmoSlaSupports::switch_to_editing_mode() for (const sla::SupportPoint& sp : m_normal_cache) m_editing_cache.emplace_back(sp); select_point(NoPoints); +#if ENABLE_RAYCAST_PICKING + on_register_raycasters_for_picking(); +#endif // ENABLE_RAYCAST_PICKING m_c->instances_hider()->show_supports(false); m_parent.set_as_dirty(); @@ -1294,6 +1405,9 @@ void GLGizmoSlaSupports::disable_editing_mode() wxGetApp().plater()->leave_gizmos_stack(); m_c->instances_hider()->show_supports(true); m_parent.set_as_dirty(); +#if ENABLE_RAYCAST_PICKING + on_unregister_raycasters_for_picking(); +#endif // ENABLE_RAYCAST_PICKING } wxGetApp().plater()->get_notification_manager()->close_notification_of_type(NotificationType::QuitSLAManualMode); } @@ -1312,6 +1426,54 @@ bool GLGizmoSlaSupports::unsaved_changes() const return false; } +#if ENABLE_RAYCAST_PICKING +void GLGizmoSlaSupports::set_sla_auxiliary_volumes_picking_state(bool state) +{ + std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); + if (raycasters != nullptr) { + const Selection& selection = m_parent.get_selection(); + const Selection::IndicesList ids = selection.get_volume_idxs(); + for (unsigned int id : ids) { + const GLVolume* v = selection.get_volume(id); + if (v->is_sla_pad() || v->is_sla_support()) { + auto it = std::find_if(raycasters->begin(), raycasters->end(), [v](std::shared_ptr item) { return item->get_raycaster() == v->mesh_raycaster.get(); }); + if (it != raycasters->end()) + (*it)->set_active(state); + } + } + } +} + +void GLGizmoSlaSupports::update_raycasters_for_picking_transform() +{ + if (!m_editing_cache.empty()) { + assert(!m_raycasters.empty()); + + const GLVolume* vol = m_parent.get_selection().get_first_volume(); + const Geometry::Transformation transformation(vol->world_matrix()); + const Transform3d instance_scaling_matrix_inverse = transformation.get_scaling_factor_matrix().inverse(); + for (size_t i = 0; i < m_editing_cache.size(); ++i) { + const Transform3d support_matrix = Geometry::translation_transform(m_editing_cache[i].support_point.pos.cast()) * instance_scaling_matrix_inverse; + + if (m_editing_cache[i].normal == Vec3f::Zero()) + m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); + + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); + const Eigen::AngleAxisd aa(q); + const Transform3d cone_matrix = vol->world_matrix() * support_matrix * Transform3d(aa.toRotationMatrix()) * + Geometry::assemble_transform((CONE_HEIGHT + m_editing_cache[i].support_point.head_front_radius * RenderPointScale) * Vec3d::UnitZ(), + Vec3d(PI, 0.0, 0.0), Vec3d(CONE_RADIUS, CONE_RADIUS, CONE_HEIGHT)); + m_raycasters[i].second->set_transform(cone_matrix); + + const double radius = (double)m_editing_cache[i].support_point.head_front_radius * RenderPointScale; + const Transform3d sphere_matrix = vol->world_matrix() * support_matrix * Geometry::scale_transform(radius); + m_raycasters[i].first->set_transform(sphere_matrix); + } + } +} +#endif // ENABLE_RAYCAST_PICKING + SlaGizmoHelpDialog::SlaGizmoHelpDialog() : wxDialog(nullptr, wxID_ANY, _L("SLA gizmo keyboard shortcuts"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 5eb29823e..ed48b6e5e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -77,13 +77,27 @@ public: /// Keep information about mouse click /// Return True when use the information otherwise False. bool on_mouse(const wxMouseEvent &mouse_event) override; + private: bool on_init() override; void on_render() override; +#if ENABLE_RAYCAST_PICKING + virtual void on_register_raycasters_for_picking() override; + virtual void on_unregister_raycasters_for_picking() override; +#else void on_render_for_picking() override; +#endif // ENABLE_RAYCAST_PICKING +#if ENABLE_RAYCAST_PICKING + void render_points(const Selection& selection); +#else void render_points(const Selection& selection, bool picking = false); +#endif // ENABLE_RAYCAST_PICKING bool unsaved_changes() const; +#if ENABLE_RAYCAST_PICKING + void set_sla_auxiliary_volumes_picking_state(bool state); + void update_raycasters_for_picking_transform(); +#endif // ENABLE_RAYCAST_PICKING bool m_lock_unique_islands = false; bool m_editing_mode = false; // Is editing mode active? @@ -96,9 +110,15 @@ private: std::vector m_normal_cache; // to restore after discarding changes or undo/redo ObjectID m_old_mo_id; +#if ENABLE_RAYCAST_PICKING + PickingModel m_sphere; + PickingModel m_cone; + std::vector, std::shared_ptr>> m_raycasters; +#else GLModel m_cone; - GLModel m_cylinder; GLModel m_sphere; +#endif // ENABLE_RAYCAST_PICKING + GLModel m_cylinder; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 9e2822dac..19d073151 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/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 228f5b58c..7dd2c110e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -73,7 +73,7 @@ enum class CommonGizmosDataID { // by GLGizmoManager, the gizmos keep a pointer to it. class CommonGizmosDataPool { public: - CommonGizmosDataPool(GLCanvas3D* canvas); + explicit CommonGizmosDataPool(GLCanvas3D* canvas); // Update all resources and release what is not used. // Accepts a bitmask of currently required resources. diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index eddcfd3a5..8759e880d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -331,6 +331,7 @@ void GLGizmosManager::render_painter_gizmo() gizmo->render_painter_gizmo(); } +#if !ENABLE_RAYCAST_PICKING void GLGizmosManager::render_current_gizmo_for_picking_pass() const { if (! m_enabled || m_current == Undefined) @@ -339,6 +340,7 @@ void GLGizmosManager::render_current_gizmo_for_picking_pass() const m_gizmos[m_current]->render_for_picking(); } +#endif // !ENABLE_RAYCAST_PICKING void GLGizmosManager::render_overlay() { @@ -1117,6 +1119,10 @@ bool GLGizmosManager::activate_gizmo(EType type) if (old_gizmo.get_state() != GLGizmoBase::Off) return false; // gizmo refused to be turned off, do nothing. +#if ENABLE_RAYCAST_PICKING + old_gizmo.unregister_raycasters_for_picking(); +#endif // ENABLE_RAYCAST_PICKING + if (!m_serializing && old_gizmo.wants_enter_leave_snapshots()) Plater::TakeSnapshot snapshot(wxGetApp().plater(), @@ -1146,6 +1152,10 @@ bool GLGizmosManager::activate_gizmo(EType type) return false; // gizmo refused to be turned on. } +#if ENABLE_RAYCAST_PICKING + new_gizmo.register_raycasters_for_picking(); +#endif // ENABLE_RAYCAST_PICKING + // sucessful activation of gizmo return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 083bfad97..8a708f62a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -213,7 +213,9 @@ public: bool is_hiding_instances() const; void render_current_gizmo() const; +#if !ENABLE_RAYCAST_PICKING void render_current_gizmo_for_picking_pass() const; +#endif // !ENABLE_RAYCAST_PICKING void render_painter_gizmo(); void render_overlay(); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index ba3f6675f..d19925f31 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -172,10 +172,6 @@ void KBShortcutsDialog::fill_shortcuts() { ctrl + "M", L("Show/Hide 3Dconnexion devices settings dialog") }, #endif // __APPLE__ #endif // _WIN32 -#if ENABLE_RENDER_PICKING_PASS - // Don't localize debugging texts. - { "P", "Toggle picking pass texture rendering on/off" }, -#endif // ENABLE_RENDER_PICKING_PASS }; m_full_shortcuts.push_back({ { _L("Plater"), "" }, plater_shortcuts }); diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 2e73434a3..818b8b51a 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -234,8 +234,13 @@ Vec3f MeshRaycaster::get_triangle_normal(size_t facet_idx) const return m_normals[facet_idx]; } +#if ENABLE_RAYCAST_PICKING +void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, + Vec3d& point, Vec3d& direction) +#else void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3d& point, Vec3d& direction) const +#endif // ENABLE_RAYCAST_PICKING { Matrix4d modelview = camera.get_view_matrix().matrix(); Matrix4d projection= camera.get_projection_matrix().matrix(); @@ -243,9 +248,9 @@ void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3 Vec3d pt1; Vec3d pt2; - igl::unproject(Vec3d(mouse_pos(0), viewport[3] - mouse_pos(1), 0.), + igl::unproject(Vec3d(mouse_pos.x(), viewport[3] - mouse_pos.y(), 0.), modelview, projection, viewport, pt1); - igl::unproject(Vec3d(mouse_pos(0), viewport[3] - mouse_pos(1), 1.), + igl::unproject(Vec3d(mouse_pos.x(), viewport[3] - mouse_pos.y(), 1.), modelview, projection, viewport, pt2); Transform3d inv = trafo.inverse(); @@ -350,6 +355,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 3b8d247c1..4ffced40a 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,18 @@ 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)) + { + assert(m_mesh != nullptr); + } + + static void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, + Vec3d& point, Vec3d& direction); +#else // The class references extern TriangleMesh, which must stay alive // during MeshRaycaster existence. MeshRaycaster(const TriangleMesh& mesh) @@ -132,7 +151,8 @@ public: } void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, - Vec3d& point, Vec3d& direction) const; + Vec3d& point, Vec3d& direction) const; +#endif // ENABLE_RAYCAST_PICKING // Given a mouse position, this returns true in case it is on the mesh. bool unproject_on_mesh( @@ -155,10 +175,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 +201,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 000000000..4c6780fc5 --- /dev/null +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -0,0 +1,209 @@ +#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 +} + +std::shared_ptr SceneRaycaster::add_raycaster(EType type, int id, const MeshRaycaster& raycaster, const Transform3d& trafo) +{ + switch (type) { + case EType::Bed: { return m_bed.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo)); } + case EType::Volume: { return m_volumes.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo)); } + case EType::Gizmo: { return m_gizmos.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo)); } + default: { assert(false); return nullptr; } + }; +} + +void SceneRaycaster::remove_raycasters(EType type, int 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::remove_raycaster(std::shared_ptr item) +{ + for (auto it = m_bed.begin(); it != m_bed.end(); ++it) { + if (*it == item) { + m_bed.erase(it); + return; + } + } + for (auto it = m_volumes.begin(); it != m_volumes.end(); ++it) { + if (*it == item) { + m_volumes.erase(it); + return; + } + } + for (auto it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { + if (*it == item) { + m_gizmos.erase(it); + return; + } + } +} + +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 = [this, is_closest, clipping_plane](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) { + const ClippingPlane* clip_plane = (clipping_plane != nullptr && type == EType::Volume) ? clipping_plane : nullptr; + std::vector>* raycasters = get_raycasters(type); + HitResult current_hit = { type }; + for (std::shared_ptr 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 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, mouse_pos, camera, ret); + + if (!m_gizmos_on_top || !ret.is_valid()) { + if (camera.is_looking_downward() && !m_bed.empty()) + test_raycasters(EType::Bed, mouse_pos, camera, ret); + if (!m_volumes.empty()) + test_raycasters(EType::Volume, mouse_pos, camera, ret); + } + + 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).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).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).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; } + default: { break; } + } + assert(ret != nullptr); + return ret; +} + +int SceneRaycaster::base_id(EType type) +{ + switch (type) + { + case EType::Bed: { return int(EIdBase::Bed); } + case EType::Volume: { return int(EIdBase::Volume); } + case EType::Gizmo: { return int(EIdBase::Gizmo); } + default: { break; } + }; + + assert(false); + return -1; +} + +int SceneRaycaster::encode_id(EType type, int id) { return base_id(type) + id; } +int SceneRaycaster::decode_id(EType type, int 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 000000000..c5c52c4b7 --- /dev/null +++ b/src/slic3r/GUI/SceneRaycaster.hpp @@ -0,0 +1,113 @@ +#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; + +class SceneRaycasterItem +{ + int m_id{ -1 }; + bool m_active{ true }; + const MeshRaycaster* m_raycaster; + Transform3d m_trafo; + +public: + SceneRaycasterItem(int id, const MeshRaycaster& raycaster, const Transform3d& trafo) + : m_id(id), m_raycaster(&raycaster), m_trafo(trafo) + {} + + int 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 EIdBase + { + Bed = 0, + Volume = 1000, + Gizmo = 1000000 + }; + + struct HitResult + { + EType type{ EType::None }; + int 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(); + + std::shared_ptr add_raycaster(EType type, int picking_id, const MeshRaycaster& raycaster, const Transform3d& trafo); + void remove_raycasters(EType type, int id); + void remove_raycasters(EType type); + void remove_raycaster(std::shared_ptr item); + + std::vector>* get_raycasters(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: + static int encode_id(EType type, int id); + static int decode_id(EType type, int id); + static int base_id(EType type); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // ENABLE_RAYCAST_PICKING + +#endif // slic3r_SceneRaycaster_hpp_ diff --git a/tests/libslic3r/test_color.cpp b/tests/libslic3r/test_color.cpp index c74e5642d..632691669 100644 --- a/tests/libslic3r/test_color.cpp +++ b/tests/libslic3r/test_color.cpp @@ -20,6 +20,7 @@ SCENARIO("Color encoding/decoding cycle", "[Color]") { } } +#if !ENABLE_RAYCAST_PICKING SCENARIO("Color picking encoding/decoding cycle", "[Color]") { GIVEN("Picking color") { const ColorRGB src_rgb(static_cast(255), static_cast(127), static_cast(63)); @@ -33,5 +34,6 @@ SCENARIO("Color picking encoding/decoding cycle", "[Color]") { } } } +#endif // !ENABLE_RAYCAST_PICKING