diff --git a/src/slic3r/GUI/CameraUtils.cpp b/src/slic3r/GUI/CameraUtils.cpp index c99616b34..41425dd23 100644 --- a/src/slic3r/GUI/CameraUtils.cpp +++ b/src/slic3r/GUI/CameraUtils.cpp @@ -1,5 +1,6 @@ #include "CameraUtils.hpp" #include // projecting points +#include #include "slic3r/GUI/3DScene.hpp" // GLVolume #include "libslic3r/Geometry/ConvexHull.hpp" @@ -79,37 +80,48 @@ Slic3r::Polygon CameraUtils::create_hull2d(const Camera & camera, return Geometry::convex_hull(vertices_2d); } -#include -Vec3d CameraUtils::create_ray(const Camera &camera, const Vec2d &coor) { - if (camera.get_type() == Camera::EType::Ortho) - return camera.get_dir_forward(); - // check that it is known camera no other tha ORTHO or Persepective - assert(camera.get_type() == Camera::EType::Perspective); +void CameraUtils::ray_from_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction) { + switch (camera.get_type()) { + case Camera::EType::Ortho: return ray_from_ortho_screen_pos(camera, position, point, direction); + case Camera::EType::Perspective: return ray_from_persp_screen_pos(camera, position, point, direction); + default: break; + } +} +Vec3d CameraUtils::screen_point(const Camera &camera, const Vec2d &position) +{ + double height = camera.get_viewport().data()[3]; + // Y coordinate has opposit direction + return Vec3d(position.x(), height - position.y(), 0.); +} + +void CameraUtils::ray_from_ortho_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction) +{ + assert(camera.get_type() == Camera::EType::Ortho); + Matrix4d modelview = camera.get_view_matrix().matrix(); + Matrix4d projection = camera.get_projection_matrix().matrix(); + Vec4i viewport(camera.get_viewport().data()); + igl::unproject(screen_point(camera,position), modelview, projection, viewport, point); + direction = camera.get_dir_forward(); +} +void CameraUtils::ray_from_persp_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction) +{ + assert(camera.get_type() == Camera::EType::Perspective); Matrix4d modelview = camera.get_view_matrix().matrix(); Matrix4d projection = camera.get_projection_matrix().matrix(); Vec4i viewport(camera.get_viewport().data()); - Vec3d scene_point(coor.x(), viewport[3] - coor.y(), 0.); Vec3d unprojected_point; - igl::unproject(scene_point, modelview, projection, viewport, unprojected_point); + igl::unproject(screen_point(camera, position), modelview, projection, viewport, unprojected_point); - Vec3d p0 = camera.get_position(); - Vec3d dir = unprojected_point - p0; - dir.normalize(); - return dir; + point = camera.get_position(); + direction = unprojected_point - point; } Vec2d CameraUtils::get_z0_position(const Camera &camera, const Vec2d & coor) { - Vec3d dir = CameraUtils::create_ray(camera, coor); - Vec3d p0 = camera.get_position(); - if (camera.get_type() == Camera::EType::Ortho) { - Matrix4d modelview = camera.get_view_matrix().matrix(); - Matrix4d projection = camera.get_projection_matrix().matrix(); - Vec4i viewport(camera.get_viewport().data()); - igl::unproject(Vec3d(coor.x(), viewport[3] - coor.y(), 0.), modelview, projection, viewport, p0); - } + Vec3d p0, dir; + ray_from_screen_pos(camera, coor, p0, dir); // is approx zero if ((fabs(dir.z()) - 1e-4) < 0) @@ -117,7 +129,7 @@ Vec2d CameraUtils::get_z0_position(const Camera &camera, const Vec2d & coor) std::numeric_limits::max()); // find position of ray cross plane(z = 0) - double t = p0.z() / dir.z(); + double t = p0.z() / dir.z(); Vec3d p = p0 - t * dir; return Vec2d(p.x(), p.y()); } diff --git a/src/slic3r/GUI/CameraUtils.hpp b/src/slic3r/GUI/CameraUtils.hpp index 7d1b43d64..c3e938ec4 100644 --- a/src/slic3r/GUI/CameraUtils.hpp +++ b/src/slic3r/GUI/CameraUtils.hpp @@ -36,12 +36,15 @@ public: static Polygon create_hull2d(const Camera &camera, const GLVolume &volume); /// - /// Unproject screen coordinate to scene direction start from camera position + /// Create ray(point and direction) for screen coordinate /// - /// Projection params - /// Coordinate on screen - /// Scene direction - static Vec3d create_ray(const Camera &camera, const Vec2d &coor); + /// Definition of camera + /// Position on screen(aka mouse position) + /// OUT start of ray + /// OUT direction of ray + static void ray_from_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction); + static void ray_from_ortho_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction); + static void ray_from_persp_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction); /// /// Unproject mouse coordinate to get position in space where z coor is zero @@ -52,6 +55,14 @@ public: /// Position on platter under mouse static Vec2d get_z0_position(const Camera &camera, const Vec2d &coor); + /// + /// Create 3d screen point from 2d position + /// + /// Define camera viewport + /// Position on screen(aka mouse position) + /// Point represented screen coor in 3d + static Vec3d screen_point(const Camera &camera, const Vec2d &position); + }; } // Slic3r::GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index e26ab39dd..522f9997b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -506,7 +506,8 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (!hit.has_value()) return false; TextConfiguration &tc = *m_volume->text_configuration; - // hide common dragging of object + // INFO: GLVolume is transformed by common movement but we need move over surface + // so hide common dragging of object m_parent.toggle_model_objects_visibility(false, m_volume->get_object(), gl_volume->instance_idx(), m_volume); // Calculate temporary position @@ -519,7 +520,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (tc.fix_3mf_tr.has_value()) trmat = trmat * (*tc.fix_3mf_tr); - // temp is in wolrld coors + // temp is in world coors m_temp_transformation = object_trmat * trmat; } else if (mouse_event.LeftUp()) { // Added because of weird case after double click into scene @@ -2849,6 +2850,44 @@ void GLGizmoEmboss::do_rotate(float relative_z_angle) // snapshot_name = L("Set text rotation"); m_parent.do_rotate(snapshot_name); } +namespace priv { +bool transform_on_surface(ModelVolume &volume, RaycastManager &raycast_manager, const Selection &selection) +{ + // Move object on surface + auto cond = RaycastManager::SkipVolume({volume.id().id}); + raycast_manager.actualize(volume.get_object(), &cond); + + //const Selection &selection = m_parent.get_selection(); + const GLVolume *gl_volume = priv::get_gl_volume(selection); + Transform3d to_world = priv::world_matrix(gl_volume, selection.get_model()); + Vec3d point = to_world * Vec3d::Zero(); + Vec3d direction = to_world.linear() * (-Vec3d::UnitZ()); + //direction.normalize(); + + // ray in direction of text projection(from volume zero to z-dir) + std::optional hit_opt = raycast_manager.unproject(point, direction, &cond); + if (!hit_opt.has_value()) + hit_opt = raycast_manager.closest(point); + + if (!hit_opt.has_value()) + return false; + + 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 + // TIP: It should be close to only z move + Vec3d offset_volume = to_world.inverse().linear() * offset_world; + + // when try to use surface on just loaded text from 3mf + auto fix = volume.text_configuration->fix_3mf_tr; + if (fix.has_value()) + offset_volume = fix->linear() * offset_volume; + + volume.set_transformation(volume.get_matrix() * Eigen::Translation(offset_volume)); + return true; +} +} // namespace priv void GLGizmoEmboss::draw_advanced() { @@ -2903,7 +2942,7 @@ void GLGizmoEmboss::draw_advanced() if (font_prop.emboss < 0.1) font_prop.emboss = 1; - // TODO: project an origin on surface + priv::transform_on_surface(*m_volume, m_raycast_manager, m_parent.get_selection()); } process(); } diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 9246c4cdf..428a83a64 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -9,6 +9,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/CameraUtils.hpp" #include @@ -340,36 +341,24 @@ Vec3f MeshRaycaster::get_triangle_normal(size_t facet_idx) const return m_normals[facet_idx]; } -void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, - Vec3d& point, Vec3d& direction) +void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3d& point, Vec3d& direction) { - Matrix4d modelview = camera.get_view_matrix().matrix(); - Matrix4d projection= camera.get_projection_matrix().matrix(); - Vec4i viewport(camera.get_viewport().data()); - - Vec3d pt1; - Vec3d pt2; - igl::unproject(Vec3d(mouse_pos.x(), viewport[3] - mouse_pos.y(), 0.), - modelview, projection, viewport, pt1); - igl::unproject(Vec3d(mouse_pos.x(), viewport[3] - mouse_pos.y(), 1.), - modelview, projection, viewport, pt2); - + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); Transform3d inv = trafo.inverse(); - pt1 = inv * pt1; - pt2 = inv * pt2; - - point = pt1; - direction = pt2-pt1; + point = inv*point; + direction = inv.linear()*direction; } - bool MeshRaycaster::unproject_on_mesh(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); + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); + Transform3d inv = trafo.inverse(); + point = inv*point; + direction = inv.linear()*direction; std::vector hits = m_emesh.query_ray_hits(point, direction); diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 401ba22f1..7c07b97c6 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -145,6 +145,7 @@ public: assert(m_mesh != nullptr); } + // DEPRICATED - use CameraUtils::ray_from_screen_pos static void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3d& point, Vec3d& direction); @@ -158,6 +159,8 @@ public: const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active) size_t* facet_idx = nullptr // index of the facet hit ) const; + + const AABBMesh &get_aabb_mesh() const { return m_emesh; } bool is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const; diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 084cf83a6..ab5720d8a 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -73,20 +73,23 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) m_transformations.erase(m_transformations.begin() + i); } +namespace priv { +struct HitWithDistance : public RaycastManager::Hit +{ + double squared_distance; + HitWithDistance(double squared_distance, + const Hit::Key &key, + const SurfacePoint &surface_point) + : Hit(key, surface_point.position, surface_point.normal) + , squared_distance(squared_distance) + {} +}; +} + std::optional RaycastManager::unproject( const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const { - struct HitWithDistance: public Hit - { - double squared_distance; - HitWithDistance(double squared_distance, - const TrKey & key, - const SurfacePoint &surface_point) - : Hit(key, surface_point.position, surface_point.normal) - , squared_distance(squared_distance) - {} - }; - std::optional closest; + std::optional closest; for (const auto &item : m_transformations) { const TrKey &key = item.first; size_t volume_id = key.second; @@ -109,13 +112,69 @@ std::optional RaycastManager::unproject( if (closest.has_value() && closest->squared_distance < squared_distance) continue; - closest = HitWithDistance(squared_distance, key, surface_point); + closest = priv::HitWithDistance(squared_distance, key, surface_point); } //if (!closest.has_value()) return {}; return closest; } +std::optional RaycastManager::unproject(const Vec3d &point, const Vec3d &direction, 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; + 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 Transform3d & tr_inv = transformation.inverse(); + Vec3d mesh_point = tr_inv * point; + Vec3d mesh_direction = tr_inv.linear() * direction; + std::vector hits = mesh.query_ray_hits(mesh_point, mesh_direction); + 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 = priv::HitWithDistance(squared_distance, key, surface_point); + } + } + return closest; +} + +std::optional RaycastManager::closest(const Vec3d &point, const ISkip *skip) const { + Vec3f point_f = point.cast(); + 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; + 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); + Vec3f n; + Vec3f p = raycaster.get_closest_point(point_f, &n); + double squared_distance = (point_f - p).squaredNorm(); + if (closest.has_value() && closest->squared_distance < squared_distance) + continue; + SurfacePoint surface_point(p,n); + closest = priv::HitWithDistance(squared_distance, key, surface_point); + } + return closest; +} + Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) const { auto item = std::find_if(m_transformations.begin(), m_transformations.end(), diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index 031cc6acd..e1e72e845 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -65,8 +65,9 @@ public: struct Hit: public SurfacePoint { - TrKey tr_key; - Hit(TrKey tr_key, Vec3f position, Vec3f normal) + using Key = TrKey; + Key tr_key; + Hit(Key tr_key, Vec3f position, Vec3f normal) : SurfacePoint(position, normal), tr_key(tr_key) {} }; @@ -102,6 +103,23 @@ public: const Camera &camera, const ISkip *skip = nullptr) const; + /// + /// Unproject Ray(point direction) on mesh by MeshRaycasters + /// + /// Start point for ray + /// Direction of ray + /// 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; + + /// + /// Search of closest point + /// + /// Point + /// Define which caster will be skipped, null mean no skip + /// + std::optional closest(const Vec3d &point, const ISkip *skip = nullptr) const; + /// /// Getter on transformation ///