From 42857d8ecbcc12bf478c86473763855ed47c2d93 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 21 Feb 2023 16:38:39 +0100 Subject: [PATCH] Use already existing AABB trees for cast into scene. Remove dependency on camera for RayCastManager. --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 73 +++++++++++++++++++++---- src/slic3r/Utils/RaycastManager.cpp | 56 ++++++++++--------- src/slic3r/Utils/RaycastManager.hpp | 20 +++---- 3 files changed, 103 insertions(+), 46 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index cbbe279346..6a4aa40955 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -167,6 +167,19 @@ static void start_create_volume_job(const ModelObject *object, static GLVolume *get_hovered_gl_volume(const GLCanvas3D &canvas); +/// +/// Unproject on mesh by Mesh raycasters +/// +/// Position of mouse on screen +/// Projection params +/// Define which caster will be skipped, null mean no skip +/// Position on surface, normal direction in world coorinate +/// + key, to know hitted instance and volume +static std::optional ray_from_camera(const RaycastManager &raycaster, + const Vec2d &mouse_pos, + const Camera &camera, + const RaycastManager::ISkip *skip); + /// /// Start job for add new volume on surface of object defined by screen coor /// @@ -175,12 +188,14 @@ static GLVolume *get_hovered_gl_volume(const GLCanvas3D &canvas); /// Mouse position which define position /// Volume to find surface for create /// Ability to ray cast to model +/// Contain already used scene RayCasters /// True when start creation, False when there is no hit surface by screen coor static bool start_create_volume_on_surface_job(DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, - RaycastManager &raycaster); + RaycastManager &raycaster, + GLCanvas3D &canvas); /// /// Find volume in selected object with closest convex hull to screen center. @@ -235,7 +250,7 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel); if (gl_volume != nullptr) { // Try to cast ray into scene and find object for add volume - if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, mouse_pos, gl_volume, m_raycast_manager)) { + if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, mouse_pos, gl_volume, m_raycast_manager, m_parent)) { // When model is broken. It could appear that hit miss the object. // So add part near by in simmilar manner as right panel do create_volume(volume_type); @@ -276,7 +291,7 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type) priv::find_closest_volume(selection, screen_center, camera, objects, &coor, &vol); if (vol == nullptr) { priv::start_create_object_job(emboss_data, screen_center); - } else if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager)) { + } else if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager, m_parent)) { // in centroid of convex hull is not hit with object // soo create transfomation on border of object @@ -499,6 +514,29 @@ static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to return {}; // no scale return sqrt(from_scale_sq / to_scale_sq); }; + +RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::AllowVolumes& condition) +{ + SceneRaycaster::EType type = SceneRaycaster::EType::Volume; + auto scene_casters = canvas.get_raycasters_for_picking(type); + const std::vector> &casters = *scene_casters; + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + const ModelObjectPtrs &objects = canvas.get_model()->objects; + + RaycastManager::Meshes meshes; + for (const std::shared_ptr &caster : casters) { + int index = SceneRaycaster::decode_id(type, caster->get_id()); + if (index < 0 || index >= gl_volumes.size()) continue; + const GLVolume *gl_volume = gl_volumes[index]; + const ModelVolume *volume = priv::get_model_volume(gl_volume, objects); + size_t id = volume->id().id; + if (condition.skip(id)) + continue; + auto mesh = std::make_unique(caster->get_raycaster()->get_aabb_mesh()); + meshes.emplace_back(std::make_pair(id, std::move(mesh))); + } + return meshes; +} } bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) @@ -568,12 +606,12 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) allowed_volumes_id.emplace_back(v->id().id); } } - RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); - + RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); + RaycastManager::Meshes meshes = priv::create_meshes(m_parent, condition); // initialize raycasters // INFO: It could slows down for big objects // (may be move to thread and do not show drag until it finish) - m_raycast_manager.actualize(instance, &condition); + m_raycast_manager.actualize(instance, &condition, &meshes); // wxCoord == int --> wx/types.h Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); @@ -609,7 +647,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec2d mouse_pos = mouse_coord.cast(); Vec2d offseted_mouse = mouse_pos + m_surface_drag->mouse_offset; const Camera &camera = wxGetApp().plater()->get_camera(); - auto hit = m_raycast_manager.ray_from_camera(offseted_mouse, camera, &m_surface_drag->condition); + auto hit = priv::ray_from_camera(m_raycast_manager, offseted_mouse, camera, &m_surface_drag->condition); m_surface_drag->exist_hit = hit.has_value(); if (!hit.has_value()) { // cross hair need redraw @@ -3255,7 +3293,7 @@ std::optional priv::calc_surface_offset(const ModelVolume &volume, Raycas Vec3d direction = to_world.linear() * (-Vec3d::UnitZ()); // ray in direction of text projection(from volume zero to z-dir) - std::optional hit_opt = raycast_manager.unproject(point, direction, &cond); + std::optional hit_opt = raycast_manager.closest_hit(point, direction, &cond); // Try to find closest point when no hit object in emboss direction if (!hit_opt.has_value()) { @@ -3981,8 +4019,19 @@ GLVolume * priv::get_hovered_gl_volume(const GLCanvas3D &canvas) { return volumes[hovered_id]; } +std::optional priv::ray_from_camera(const RaycastManager &raycaster, + const Vec2d &mouse_pos, + const Camera &camera, + const RaycastManager::ISkip *skip) +{ + Vec3d point; + Vec3d direction; + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); + return raycaster.first_hit(point, direction, skip); +} + bool priv::start_create_volume_on_surface_job( - DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster) + DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster, GLCanvas3D& canvas) { assert(gl_volume != nullptr); if (gl_volume == nullptr) return false; @@ -3995,10 +4044,12 @@ bool priv::start_create_volume_on_surface_job( ModelObject *obj = objects[object_idx]; size_t vol_id = obj->volumes[gl_volume->volume_idx()]->id().id; auto cond = RaycastManager::AllowVolumes({vol_id}); - raycaster.actualize(obj, &cond); + + RaycastManager::Meshes meshes = priv::create_meshes(canvas, cond); + raycaster.actualize(obj, &cond, &meshes); const Camera &camera = plater->get_camera(); - std::optional hit = raycaster.ray_from_camera(screen_coor, camera, &cond); + std::optional hit = priv::ray_from_camera(raycaster, screen_coor, camera, &cond); // context menu for add text could be open only by right click on an // object. After right click, object is selected and object_idx is set diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 3f34c37fcd..3b0ace2a34 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -1,16 +1,11 @@ #include "RaycastManager.hpp" #include -// include for earn camera -#include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/Plater.hpp" -#include "slic3r/GUI/CameraUtils.hpp" - using namespace Slic3r::GUI; namespace priv { using namespace Slic3r; -static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip); +static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes *input = nullptr); static const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id); static RaycastManager::TrKey create_key(const ModelVolume* volume, const ModelInstance* instance){ return std::make_pair(instance->id().id, volume->id().id); } @@ -21,10 +16,10 @@ static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrI return is_lower_key(i1.first, i2.first); }; } -void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) +void RaycastManager::actualize(const ModelObject *object, const ISkip *skip, Meshes *meshes) { // actualize MeshRaycaster - priv::actualize(m_meshes, object->volumes, skip); + priv::actualize(m_meshes, object->volumes, skip, meshes); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); @@ -61,11 +56,12 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } -void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) { +void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip, Meshes *meshes) +{ const ModelVolumePtrs &volumes = instance->get_object()->volumes; // actualize MeshRaycaster - priv::actualize(m_meshes, volumes, skip); + priv::actualize(m_meshes, volumes, skip, meshes); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); @@ -101,11 +97,9 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } -std::optional RaycastManager::ray_from_camera( - const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const +std::optional RaycastManager::first_hit(const Vec3d& point, const Vec3d& direction, const ISkip *skip) const { - // Improve it is not neccessaru to use AABBMesh and calc normal in - + // Improve: it is not neccessaru to use AABBMesh and calc normal for every hit struct Result { const AABBMesh *mesh = nullptr; @@ -115,6 +109,7 @@ std::optional RaycastManager::ray_from_camera( const Transform3d *tramsformation; const TrKey *key; }result; + for (const auto &item : m_transformations) { const TrKey &key = item.first; size_t volume_id = key.second; @@ -122,24 +117,23 @@ std::optional RaycastManager::ray_from_camera( const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); if (mesh == nullptr) continue; const Transform3d &transformation = item.second; - - Vec3d point; - Vec3d direction; - CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); Transform3d inv = transformation.inverse(); - point = inv * point; - direction = inv.linear() * direction; - std::vector hits = mesh->query_ray_hits(point, direction); + + // transform input into mesh world + Vec3d point_ = inv * point; + Vec3d direction_= inv.linear() * direction; + + std::vector hits = mesh->query_ray_hits(point_, direction_); if (hits.empty()) continue; // no intersection found const AABBMesh::hit_result &hit = hits.front(); // convert to world Vec3d hit_world = transformation * hit.position(); - double squared_distance = (camera.get_position() - hit_world).squaredNorm(); + double squared_distance = (point - hit_world).squaredNorm(); if (result.mesh != nullptr && result.squared_distance < squared_distance) - continue; + continue; // exist closer one result.mesh = mesh; result.squared_distance = squared_distance; @@ -152,6 +146,8 @@ std::optional RaycastManager::ray_from_camera( if (result.mesh == nullptr) return {}; + // Calculate normal from transformed triangle + // NOTE: Anisotropic transformation of normal is not perpendiculat to triangle const Vec3i tri = result.mesh->indices(result.face); Vec3d pts[3]; auto tr = result.tramsformation->linear(); @@ -164,7 +160,7 @@ std::optional RaycastManager::ray_from_camera( return RaycastManager::Hit{point_world, *result.key, result.squared_distance}; } -std::optional RaycastManager::unproject(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const +std::optional RaycastManager::closest_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const { std::optional closest; for (const auto &item : m_transformations) { @@ -236,7 +232,7 @@ Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) cons return item->second; } -void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip) +void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes* inputs) { // check if volume was removed std::vector removed_meshes(meshes.size(), {true}); @@ -248,6 +244,16 @@ void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volu continue; auto item = std::find_if(meshes.begin(), meshes.end(), [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; }); if (item == meshes.end()) { + // exist AABB in inputs ? + if (inputs != nullptr) { + auto input = std::find_if(inputs->begin(), inputs->end(), + [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; }); + if (input != inputs->end()) { + meshes.emplace_back(std::move(*input)); + continue; + } + } + // add new raycaster bool calculate_epsilon = true; auto mesh = std::make_unique(volume->mesh(), calculate_epsilon); diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index 5bd28d00a6..4ef9b7ca76 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -7,7 +7,6 @@ #include "libslic3r/Point.hpp" // Transform3d #include "libslic3r/ObjectID.hpp" #include "libslic3r/Model.hpp" // ModelObjectPtrs, ModelObject, ModelInstance, ModelVolume -#include "slic3r/GUI/Camera.hpp" namespace Slic3r::GUI{ @@ -86,8 +85,9 @@ public: /// /// Model representation /// Condifiton for skip actualization - void actualize(const ModelObject *object, const ISkip *skip = nullptr); - void actualize(const ModelInstance *instance, const ISkip *skip = nullptr); + /// Speed up for already created AABBtrees + void actualize(const ModelObject *object, const ISkip *skip = nullptr, Meshes *meshes = nullptr); + void actualize(const ModelInstance *instance, const ISkip *skip = nullptr, Meshes* meshes = nullptr); class SkipVolume: public ISkip { @@ -109,24 +109,24 @@ public: }; /// - /// Unproject on mesh by Mesh raycasters + /// Unproject on mesh and return closest hit to point in given direction /// - /// Position of mouse on screen - /// Projection params + /// Position in space + /// Casted ray direction /// Define which caster will be skipped, null mean no skip /// Position on surface, normal direction in world coorinate /// + key, to know hitted instance and volume - std::optional ray_from_camera(const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip = nullptr) const; + std::optional first_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const; /// - /// Unproject Ray(point direction) on mesh by MeshRaycasters + /// Unproject Ray(point direction) on mesh to find closest hit of surface in given direction /// NOTE: It inspect also oposit direction of ray !! /// /// Start point for ray - /// Direction of ray + /// Direction of ray, orientation doesn't matter, both are used /// Define which caster will be skipped, null mean no skip /// Position on surface, normal direction and transformation key, which define hitted object instance - std::optional unproject(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const; + std::optional closest_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const; /// /// Search of closest point