Project text origin on object surface when use surface is set. When no intersection thans closest point is selected

(@vojta wants it)
This commit is contained in:
Filip Sykala - NTB T15p 2022-12-15 19:27:58 +01:00
parent 043c4c5e90
commit e8e50b50f1
7 changed files with 194 additions and 63 deletions

View File

@ -1,5 +1,6 @@
#include "CameraUtils.hpp"
#include <igl/project.h> // projecting points
#include <igl/unproject.h>
#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 <igl/unproject.h>
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)

View File

@ -36,12 +36,15 @@ public:
static Polygon create_hull2d(const Camera &camera, const GLVolume &volume);
/// <summary>
/// Unproject screen coordinate to scene direction start from camera position
/// Create ray(point and direction) for screen coordinate
/// </summary>
/// <param name="camera">Projection params</param>
/// <param name="coor">Coordinate on screen</param>
/// <returns>Scene direction</returns>
static Vec3d create_ray(const Camera &camera, const Vec2d &coor);
/// <param name="camera">Definition of camera</param>
/// <param name="position">Position on screen(aka mouse position) </param>
/// <param name="point">OUT start of ray</param>
/// <param name="direction">OUT direction of ray</param>
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);
/// <summary>
/// Unproject mouse coordinate to get position in space where z coor is zero
@ -52,6 +55,14 @@ public:
/// <returns>Position on platter under mouse</returns>
static Vec2d get_z0_position(const Camera &camera, const Vec2d &coor);
/// <summary>
/// Create 3d screen point from 2d position
/// </summary>
/// <param name="camera">Define camera viewport</param>
/// <param name="position">Position on screen(aka mouse position)</param>
/// <returns>Point represented screen coor in 3d</returns>
static Vec3d screen_point(const Camera &camera, const Vec2d &position);
};
} // Slic3r::GUI

View File

@ -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<RaycastManager::Hit> 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<double>();
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<double, 3>(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();
}

View File

@ -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 <GL/glew.h>
@ -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<AABBMesh::hit_result> hits = m_emesh.query_ray_hits(point, direction);

View File

@ -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);
@ -159,6 +160,8 @@ public:
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;
// Given a vector of points in woorld coordinates, this returns vector

View File

@ -73,20 +73,23 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip)
m_transformations.erase(m_transformations.begin() + i);
}
std::optional<RaycastManager::Hit> RaycastManager::unproject(
const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const
{
struct HitWithDistance: public Hit
namespace priv {
struct HitWithDistance : public RaycastManager::Hit
{
double squared_distance;
HitWithDistance(double squared_distance,
const TrKey & key,
const Hit::Key &key,
const SurfacePoint &surface_point)
: Hit(key, surface_point.position, surface_point.normal)
, squared_distance(squared_distance)
{}
};
std::optional<HitWithDistance> closest;
}
std::optional<RaycastManager::Hit> RaycastManager::unproject(
const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const
{
std::optional<priv::HitWithDistance> 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::Hit> 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::Hit> RaycastManager::unproject(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const
{
std::optional<priv::HitWithDistance> 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<AABBMesh::hit_result> 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<float>(), hit.normal().cast<float>());
closest = priv::HitWithDistance(squared_distance, key, surface_point);
}
}
return closest;
}
std::optional<RaycastManager::Hit> RaycastManager::closest(const Vec3d &point, const ISkip *skip) const {
Vec3f point_f = point.cast<float>();
std::optional<priv::HitWithDistance> 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(),

View File

@ -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;
/// <summary>
/// Unproject Ray(point direction) on mesh by MeshRaycasters
/// </summary>
/// <param name="point">Start point for ray</param>
/// <param name="direction">Direction of ray</param>
/// <param name="skip">Define which caster will be skipped, null mean no skip</param>
/// <returns>Position on surface, normal direction and transformation key, which define hitted object instance</returns>
std::optional<Hit> unproject(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const;
/// <summary>
/// Search of closest point
/// </summary>
/// <param name="point">Point</param>
/// <param name="skip">Define which caster will be skipped, null mean no skip</param>
/// <returns></returns>
std::optional<Hit> closest(const Vec3d &point, const ISkip *skip = nullptr) const;
/// <summary>
/// Getter on transformation
/// </summary>