RaycastManager use directly AABBMesh instead of MeshRayCaster

This commit is contained in:
Filip Sykala - NTB T15p 2023-02-16 13:09:29 +01:00
parent 6d0d24eecf
commit 67155e8da0
3 changed files with 215 additions and 166 deletions

View File

@ -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<Vec3d> priv::calc_surface_offset(const ModelVolume &volume, Raycas
std::optional<RaycastManager::Hit> 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<RaycastManager::ClosePoint> 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<double>();
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<float>(), hit->normal.cast<float>());
const FontProp &font_prop = emboss_data.text_configuration.style.prop;
apply_transformation(font_prop, surface_trmat);
Transform3d world_new = hit_to_world * surface_trmat;

View File

@ -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<size_t, std::unique_ptr<MeshRaycaster> >;
// 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<Raycaster>;
// Copy functionality from MeshRaycaster::unproject_on_mesh without filtering
using SurfacePoint = RaycastManager::SurfacePoint<double>;
static std::optional<SurfacePoint> 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<bool> 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<MeshRaycaster>(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<bool> 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<bool> 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<RaycastManager::SurfacePoint> 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<AABBMesh::hit_result> 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<float>(),
hit.normal().cast<float>()
);
}
} // namespace priv
std::optional<RaycastManager::Hit> RaycastManager::unproject(
const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const
{
std::optional<Hit> 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<SurfacePoint> 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<double>();
Vec3d act_hit_tr = transformation * surface_point_opt->position.cast<double>();
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::Hit> RaycastManager::unproject(const Vec3d &point,
std::optional<Hit> 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::Hit> RaycastManager::unproject(const Vec3d &point,
Vec3d point_negative = mesh_point + mesh_direction;
// Throw ray to both directions of ray
std::vector<AABBMesh::hit_result> hits = mesh.query_ray_hits(point_positive, mesh_direction);
std::vector<AABBMesh::hit_result> hits_neg = mesh.query_ray_hits(point_negative, -mesh_direction);
std::vector<AABBMesh::hit_result> hits = mesh->query_ray_hits(point_positive, mesh_direction);
std::vector<AABBMesh::hit_result> 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<float>(), hit.normal().cast<float>());
closest = Hit(key, surface_point, squared_distance);
closest = Hit{{hit.position(), hit.normal()}, key, squared_distance};
}
}
return closest;
}
std::optional<RaycastManager::Hit> RaycastManager::closest(const Vec3d &point, const ISkip *skip) const {
std::optional<Hit> closest;
std::optional<RaycastManager::ClosePoint> RaycastManager::closest(const Vec3d &point, const ISkip *skip) const
{
std::optional<ClosePoint> 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<float>();
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<double>();
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;
}
}
void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip)
{
// check if volume was removed
std::vector<bool> 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<AABBMesh>(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::SurfacePoint> 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<AABBMesh::hit_result> 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;
}

View File

@ -2,12 +2,12 @@
#define slic3r_RaycastManager_hpp_
#include <memory> // unique_ptr
#include <optional> // unique_ptr
#include <map>
#include "slic3r/GUI/MeshUtils.hpp" // MeshRaycaster
#include <optional>
#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{
/// </summary>
class RaycastManager
{
// ModelVolume.id
using Raycaster = std::pair<size_t, std::unique_ptr<MeshRaycaster> >;
std::vector<Raycaster> m_raycasters;
// Public structures used by RaycastManager
public:
// Key for transformation consist of unique volume and instance
// ModelVolume.id
using Mesh = std::pair<size_t, std::unique_ptr<AABBMesh> >;
using Meshes = std::vector<Mesh>;
// Key for transformation consist of unique volume and instance id ... ObjectId()
// ModelInstance, ModelVolume
using TrKey = std::pair<size_t, size_t>;
using TrItem = std::pair<TrKey, Transform3d>;
std::vector<TrItem> m_transformations;
using TrItems = std::vector<TrItem>;
// should contain shared pointer to camera but it is not shared pointer so it need it every time when casts rays
public:
/// <summary>
/// Interface for identify allowed volumes to cast rays.
/// </summary>
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<typename T>
struct SurfacePoint {
using Vec3 = Eigen::Matrix<T, 3, 1, Eigen::DontAlign>;
Vec3 position = Vec3::Zero();
Vec3 normal = Vec3::UnitZ();
};
struct Hit : public SurfacePoint<double>
{
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:
/// <summary>
/// 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:
/// <summary>
/// Unproject on mesh by Mesh raycasters
/// Note: Function use current camera position from wxGetApp()
/// </summary>
/// <param name="mouse_pos">Position of mouse on screen</param>
/// <param name="camera">Projection params</param>
@ -121,7 +135,7 @@ public:
/// <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;
std::optional<ClosePoint> closest(const Vec3d &point, const ISkip *skip = nullptr) const;
/// <summary>
/// Getter on transformation from hitted volume to world