diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 44e664f8f8..c6ce9da8fe 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -559,7 +559,7 @@ void reset_skew_respect_z(Transform3d &m) Transform3d rot; // = Transform3d::Identity(); if (priv::allign_vec(z_after, z_before, rot)) - m = rot * m; + m = m * rot; } // Multiply from right @@ -3489,21 +3489,33 @@ std::optional priv::calc_surface_offset(const ModelVolume &volume, Raycas std::optional hit_opt = raycast_manager.unproject(point, direction, &cond); // Try to find closest point when no hit object in emboss direction - if (!hit_opt.has_value()) - hit_opt = raycast_manager.closest(point); + if (!hit_opt.has_value()) { + std::optional close_point_opt = raycast_manager.closest(point); - // It should NOT appear. Closest point always exists. - if (!hit_opt.has_value()) - return {}; + // It should NOT appear. Closest point always exists. + assert(close_point_opt.has_value()); + if (!close_point_opt.has_value()) + return {}; + + // It is no neccesary to move with origin by very small value + if (close_point_opt->squared_distance < EPSILON) + return {}; + + const RaycastManager::ClosePoint &close_point = *close_point_opt; + Transform3d hit_tr = raycast_manager.get_transformation(close_point.tr_key); + Vec3d hit_world = hit_tr * close_point.point; + Vec3d offset_world = hit_world - point; // vector in world + Vec3d offset_volume = to_world.inverse().linear() * offset_world; + return offset_volume; + } // It is no neccesary to move with origin by very small value - if (hit_opt->squared_distance < EPSILON) - return {}; - const RaycastManager::Hit &hit = *hit_opt; - Transform3d hit_tr = raycast_manager.get_transformation(hit.tr_key); - Vec3d hit_world = hit_tr * hit.position.cast(); - Vec3d offset_world = hit_world - point; // vector in world + if (hit.squared_distance < EPSILON) + return {}; + Transform3d hit_tr = raycast_manager.get_transformation(hit.tr_key); + Vec3d hit_world = hit_tr * hit.position; + Vec3d offset_world = hit_world - point; // vector in world // TIP: It should be close to only z move Vec3d offset_volume = to_world.inverse().linear() * offset_world; return offset_volume; @@ -4246,7 +4258,7 @@ bool priv::start_create_volume_on_surface_job( Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); // Create result volume transformation - Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); + Transform3d surface_trmat = create_transformation_onto_surface(hit->position.cast(), hit->normal.cast()); const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); Transform3d world_new = hit_to_world * surface_trmat; diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index be11c7f894..fad41424f9 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -3,69 +3,50 @@ // include for earn camera #include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/Plater.hpp" -#include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/CameraUtils.hpp" using namespace Slic3r::GUI; namespace priv { - using namespace Slic3r; -// copied from private part of RaycastManager.hpp -using Raycaster = std::pair >; -// ModelVolume.id +static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip); +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); } +static RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key); +static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) { + return k1.first < k2.first || k1.first == k2.first && k1.second < k2.second; } +static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) { + return is_lower_key(i1.first, i2.first); }; -using Raycasters = std::vector; + // Copy functionality from MeshRaycaster::unproject_on_mesh without filtering +using SurfacePoint = RaycastManager::SurfacePoint; +static std::optional unproject_on_mesh(const AABBMesh &aabb_mesh, + const Vec2d &mouse_pos, + const Transform3d &transformation, + const Camera &camera); -static void actualize(Raycasters &casters, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip) -{ - // check if volume was removed - std::vector removed_casters(casters.size(), {true}); - // actualize MeshRaycaster - for (const ModelVolume *volume : volumes) { - size_t oid = volume->id().id; - if (skip != nullptr && skip->skip(oid)) - continue; - auto item = std::find_if(casters.begin(), casters.end(), - [oid](const Raycaster &it) -> bool { return oid == it.first; }); - if (item == casters.end()) { - // add new raycaster - auto raycaster = std::make_unique(volume->get_mesh_shared_ptr()); - casters.emplace_back(std::make_pair(oid, std::move(raycaster))); - } else { - size_t index = item - casters.begin(); - removed_casters[index] = false; - } - } - - // clean other raycasters - for (int i = removed_casters.size() - 1; i >= 0; --i) - if (removed_casters[i]) - casters.erase(casters.begin() + i); -} } void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) { // actualize MeshRaycaster - priv::actualize(m_raycasters, object->volumes, skip); + priv::actualize(m_meshes, object->volumes, skip); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); - + + bool need_sort = false; // actualize transformation matrices for (const ModelVolume *volume : object->volumes) { if (skip != nullptr && skip->skip(volume->id().id)) continue; const Transform3d &volume_tr = volume->get_matrix(); for (const ModelInstance *instance : object->instances) { - const Transform3d &instrance_tr = instance->get_matrix(); - Transform3d transformation = instrance_tr * volume_tr; - TrKey tr_key = std::make_pair(instance->id().id, volume->id().id); - auto item = std::find_if(m_transformations.begin(), - m_transformations.end(), - [&tr_key](const TrItem &it) -> bool { - return it.first == tr_key; - }); + const Transform3d &instrance_tr = instance->get_matrix(); + Transform3d transformation = instrance_tr * volume_tr; + TrKey key = priv::create_key(volume, instance); + auto item = priv::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time item->second = transformation; @@ -73,8 +54,8 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) removed_transf[index] = false; } else { // add new transformation - m_transformations.emplace_back( - std::make_pair(tr_key, transformation)); + m_transformations.emplace_back(std::make_pair(key, transformation)); + need_sort = true; } } } @@ -83,17 +64,21 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) for (int i = removed_transf.size() - 1; i >= 0; --i) if (removed_transf[i]) m_transformations.erase(m_transformations.begin() + i); + + if (need_sort) + std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) { const ModelVolumePtrs &volumes = instance->get_object()->volumes; // actualize MeshRaycaster - priv::actualize(m_raycasters, volumes, skip); + priv::actualize(m_meshes, volumes, skip); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); + bool need_sort = false; // actualize transformation matrices for (const ModelVolume *volume : volumes) { if (skip != nullptr && skip->skip(volume->id().id)) @@ -101,9 +86,8 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) const Transform3d &volume_tr = volume->get_matrix(); const Transform3d &instrance_tr = instance->get_matrix(); Transform3d transformation = instrance_tr * volume_tr; - TrKey tr_key = std::make_pair(instance->id().id, volume->id().id); - auto item = std::find_if(m_transformations.begin(), m_transformations.end(), - [&tr_key](const TrItem &it) -> bool { return it.first == tr_key; }); + TrKey key = priv::create_key(volume, instance); + auto item = priv::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time item->second = transformation; @@ -111,7 +95,8 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) removed_transf[index] = false; } else { // add new transformation - m_transformations.emplace_back(std::make_pair(tr_key, transformation)); + m_transformations.emplace_back(std::make_pair(key, transformation)); + need_sort = true; } } @@ -119,65 +104,33 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) for (int i = removed_transf.size() - 1; i >= 0; --i) if (removed_transf[i]) m_transformations.erase(m_transformations.begin() + i); + + if (need_sort) + std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } -#include "slic3r/GUI/CameraUtils.hpp" -namespace priv { - -// Copy functionality from MeshRaycaster::unproject_on_mesh without filtering -static std::optional unproject_on_mesh(const MeshRaycaster &raycaster, - const Vec2d &mouse_pos, const Transform3d &transformation, const Camera &camera) { - - 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; - - const AABBMesh &aabb_mesh = raycaster.get_aabb_mesh(); - std::vector hits = aabb_mesh.query_ray_hits(point, direction); - - if (hits.empty()) - return {}; // no intersection found - - const AABBMesh::hit_result &hit = hits.front(); - return RaycastManager::SurfacePoint( - hit.position().cast(), - hit.normal().cast() - ); -} -} // namespace priv - std::optional RaycastManager::unproject( const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const { std::optional closest; for (const auto &item : m_transformations) { const TrKey &key = item.first; - size_t volume_id = key.second; + size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; + const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + if (mesh == nullptr) continue; const Transform3d &transformation = item.second; - auto raycaster_it = - std::find_if(m_raycasters.begin(), m_raycasters.end(), - [volume_id](const RaycastManager::Raycaster &it) - -> bool { return volume_id == it.first; }); - if (raycaster_it == m_raycasters.end()) continue; - const MeshRaycaster &raycaster = *(raycaster_it->second); - std::optional surface_point_opt = priv::unproject_on_mesh( - raycaster, mouse_pos, transformation, camera); + auto surface_point_opt = + priv::unproject_on_mesh(*mesh, mouse_pos, transformation, camera); if (!surface_point_opt.has_value()) continue; - const SurfacePoint &surface_point = *surface_point_opt; - Vec3d act_hit_tr = transformation * surface_point.position.cast(); + Vec3d act_hit_tr = transformation * surface_point_opt->position.cast(); double squared_distance = (camera.get_position() - act_hit_tr).squaredNorm(); if (closest.has_value() && closest->squared_distance < squared_distance) continue; - closest = Hit(key, surface_point, squared_distance); + closest = Hit{*surface_point_opt, key, squared_distance}; } - - //if (!closest.has_value()) return {}; return closest; } @@ -186,16 +139,11 @@ std::optional RaycastManager::unproject(const Vec3d &point, std::optional closest; for (const auto &item : m_transformations) { const TrKey &key = item.first; - size_t volume_id = key.second; + size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; const Transform3d &transformation = item.second; - auto raycaster_it = - std::find_if(m_raycasters.begin(), m_raycasters.end(), - [volume_id](const RaycastManager::Raycaster &it) - -> bool { return volume_id == it.first; }); - if (raycaster_it == m_raycasters.end()) continue; - const MeshRaycaster &raycaster = *(raycaster_it->second); - const AABBMesh& mesh = raycaster.get_aabb_mesh(); + const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + if (mesh == nullptr) continue; Transform3d tr_inv = transformation.inverse(); Vec3d mesh_point = tr_inv * point; Vec3d mesh_direction = tr_inv.linear() * direction; @@ -205,49 +153,50 @@ std::optional RaycastManager::unproject(const Vec3d &point, Vec3d point_negative = mesh_point + mesh_direction; // Throw ray to both directions of ray - std::vector hits = mesh.query_ray_hits(point_positive, mesh_direction); - std::vector hits_neg = mesh.query_ray_hits(point_negative, -mesh_direction); + std::vector hits = mesh->query_ray_hits(point_positive, mesh_direction); + std::vector hits_neg = mesh->query_ray_hits(point_negative, -mesh_direction); hits.insert(hits.end(), std::make_move_iterator(hits_neg.begin()), std::make_move_iterator(hits_neg.end())); for (const AABBMesh::hit_result &hit : hits) { double squared_distance = (mesh_point - hit.position()).squaredNorm(); if (closest.has_value() && closest->squared_distance < squared_distance) continue; - SurfacePoint surface_point(hit.position().cast(), hit.normal().cast()); - closest = Hit(key, surface_point, squared_distance); + closest = Hit{{hit.position(), hit.normal()}, key, squared_distance}; } } return closest; } -std::optional RaycastManager::closest(const Vec3d &point, const ISkip *skip) const { - std::optional closest; +std::optional RaycastManager::closest(const Vec3d &point, const ISkip *skip) const +{ + std::optional closest; for (const auto &item : m_transformations) { const TrKey &key = item.first; size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; - auto raycaster_it = std::find_if(m_raycasters.begin(), m_raycasters.end(), - [volume_id](const RaycastManager::Raycaster &it) -> bool { return volume_id == it.first; }); - if (raycaster_it == m_raycasters.end()) - continue; - const MeshRaycaster &raycaster = *(raycaster_it->second); + const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + if (mesh == nullptr) continue; const Transform3d &transformation = item.second; Transform3d tr_inv = transformation.inverse(); - Vec3d mesh_point_d = tr_inv * point; - Vec3f mesh_point_f = mesh_point_d.cast(); - Vec3f n; - Vec3f p = raycaster.get_closest_point(mesh_point_f, &n); - double squared_distance = (mesh_point_f - p).squaredNorm(); + Vec3d mesh_point = tr_inv * point; + + int face_idx = 0; + Vec3d closest_point; + Vec3d pointd = point.cast(); + mesh->squared_distance(pointd, face_idx, closest_point); + + double squared_distance = (mesh_point - closest_point).squaredNorm(); if (closest.has_value() && closest->squared_distance < squared_distance) continue; - SurfacePoint surface_point(p,n); - closest = Hit(key, surface_point, squared_distance); + + closest = ClosePoint{key, closest_point, squared_distance}; } return closest; } Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) const { + // TODO: transformations are sorted use lower bound auto item = std::find_if(m_transformations.begin(), m_transformations.end(), [&tr_key](const TrItem &it) -> bool { @@ -255,4 +204,78 @@ Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) cons }); if (item == m_transformations.end()) return Transform3d::Identity(); return item->second; -} \ No newline at end of file +} + +void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip) +{ + // check if volume was removed + std::vector removed_meshes(meshes.size(), {true}); + bool need_sort = false; + // actualize MeshRaycaster + for (const ModelVolume *volume : volumes) { + size_t oid = volume->id().id; + if (skip != nullptr && skip->skip(oid)) + continue; + auto item = std::find_if(meshes.begin(), meshes.end(), [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; }); + if (item == meshes.end()) { + // add new raycaster + bool calculate_epsilon = true; + auto mesh = std::make_unique(volume->mesh(), calculate_epsilon); + meshes.emplace_back(std::make_pair(oid, std::move(mesh))); + need_sort = true; + } else { + size_t index = item - meshes.begin(); + removed_meshes[index] = false; + } + } + + // clean other raycasters + for (int i = removed_meshes.size() - 1; i >= 0; --i) + if (removed_meshes[i]) + meshes.erase(meshes.begin() + i); + + // All the time meshes must be sorted by volume id - for faster search + if (need_sort) { + auto is_lower = [](const RaycastManager::Mesh &m1, const RaycastManager::Mesh &m2) { return m1.first < m2.first; }; + std::sort(meshes.begin(), meshes.end(), is_lower); + } +} + +std::optional priv::unproject_on_mesh(const AABBMesh &aabb_mesh, + const Vec2d &mouse_pos, + const Transform3d &transformation, + const Camera &camera) +{ + 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 = aabb_mesh.query_ray_hits(point, direction); + + if (hits.empty()) + return {}; // no intersection found + + const AABBMesh::hit_result &hit = hits.front(); + return priv::SurfacePoint{hit.position(), hit.normal()}; +} + +const Slic3r::AABBMesh *priv::get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id) +{ + auto is_lower_index = [](const RaycastManager::Mesh &m, size_t i) -> bool { return m.first < i; }; + auto it = std::lower_bound(meshes.begin(), meshes.end(), volume_id, is_lower_index); + if (it == meshes.end() || it->first != volume_id) + return nullptr; + return &(*(it->second)); +} + +RaycastManager::TrItems::iterator priv::find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key) { + auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &key)->bool { + return priv::is_lower_key(it.first, key); + }; + auto it = std::lower_bound(items.begin(), items.end(), key, fnc); + if (it == items.end() || it->first != key) + return items.end(); + return it; +} diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index 5451c4e92e..406e51c86c 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -2,12 +2,12 @@ #define slic3r_RaycastManager_hpp_ #include // unique_ptr -#include // unique_ptr -#include -#include "slic3r/GUI/MeshUtils.hpp" // MeshRaycaster +#include +#include "libslic3r/AABBMesh.hpp" // Structure to cast rays #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{ @@ -17,19 +17,22 @@ namespace Slic3r::GUI{ /// class RaycastManager { - // ModelVolume.id - using Raycaster = std::pair >; - std::vector m_raycasters; +// Public structures used by RaycastManager +public: - // Key for transformation consist of unique volume and instance + // ModelVolume.id + using Mesh = std::pair >; + using Meshes = std::vector; + + // Key for transformation consist of unique volume and instance id ... ObjectId() // ModelInstance, ModelVolume using TrKey = std::pair; using TrItem = std::pair; - std::vector m_transformations; + using TrItems = std::vector; - // should contain shared pointer to camera but it is not shared pointer so it need it every time when casts rays - -public: + /// + /// Interface for identify allowed volumes to cast rays. + /// class ISkip{ public: virtual ~ISkip() = default; @@ -42,6 +45,39 @@ public: virtual bool skip(const size_t &model_volume_id) const { return false; } }; + // TODO: it is more general object move outside of this class + template + struct SurfacePoint { + using Vec3 = Eigen::Matrix; + Vec3 position = Vec3::Zero(); + Vec3 normal = Vec3::UnitZ(); + }; + + struct Hit : public SurfacePoint + { + TrKey tr_key; + double squared_distance; + }; + + struct ClosePoint + { + TrKey tr_key; + Vec3d point; + double squared_distance; + }; + +// Members +private: + // Keep structure to fast cast rays + // meshes are sorted by volume_id for faster search + Meshes m_meshes; + + // Keep transformation of meshes + TrItems m_transformations; + // Note: one mesh could have more transformations ... instances + +public: + /// /// Actualize raycasters + transformation /// Detection of removed object @@ -53,27 +89,6 @@ public: void actualize(const ModelObject *object, const ISkip *skip = nullptr); void actualize(const ModelInstance *instance, const ISkip *skip = nullptr); - // TODO: it is more general object move outside of this class - struct SurfacePoint - { - Vec3f position = Vec3f::Zero(); - Vec3f normal = Vec3f::UnitZ(); - SurfacePoint() = default; - SurfacePoint(Vec3f position, Vec3f normal) - : position(position), normal(normal) - {} - }; - - struct Hit: public SurfacePoint - { - using Key = TrKey; - Key tr_key; - double squared_distance; - Hit(const Key& tr_key, const SurfacePoint& surface_point, double squared_distance) - : SurfacePoint(surface_point), tr_key(tr_key), squared_distance(squared_distance) - {} - }; - class SkipVolume: public ISkip { size_t volume_id; @@ -95,7 +110,6 @@ public: /// /// Unproject on mesh by Mesh raycasters - /// Note: Function use current camera position from wxGetApp() /// /// Position of mouse on screen /// Projection params @@ -121,7 +135,7 @@ public: /// Point /// Define which caster will be skipped, null mean no skip /// - std::optional closest(const Vec3d &point, const ISkip *skip = nullptr) const; + std::optional closest(const Vec3d &point, const ISkip *skip = nullptr) const; /// /// Getter on transformation from hitted volume to world