Use already existing AABB trees for cast into scene.

Remove dependency on camera for RayCastManager.
This commit is contained in:
Filip Sykala - NTB T15p 2023-02-21 16:38:39 +01:00
parent 6ee384557e
commit 42857d8ecb
3 changed files with 103 additions and 46 deletions

View file

@ -167,6 +167,19 @@ static void start_create_volume_job(const ModelObject *object,
static GLVolume *get_hovered_gl_volume(const GLCanvas3D &canvas);
/// <summary>
/// Unproject on mesh by Mesh raycasters
/// </summary>
/// <param name="mouse_pos">Position of mouse on screen</param>
/// <param name="camera">Projection params</param>
/// <param name="skip">Define which caster will be skipped, null mean no skip</param>
/// <returns>Position on surface, normal direction in world coorinate
/// + key, to know hitted instance and volume</returns>
static std::optional<RaycastManager::Hit> ray_from_camera(const RaycastManager &raycaster,
const Vec2d &mouse_pos,
const Camera &camera,
const RaycastManager::ISkip *skip);
/// <summary>
/// Start job for add new volume on surface of object defined by screen coor
/// </summary>
@ -175,12 +188,14 @@ static GLVolume *get_hovered_gl_volume(const GLCanvas3D &canvas);
/// <param name="screen_coor">Mouse position which define position</param>
/// <param name="gl_volume">Volume to find surface for create</param>
/// <param name="raycaster">Ability to ray cast to model</param>
/// <param name="canvas">Contain already used scene RayCasters</param>
/// <returns>True when start creation, False when there is no hit surface by screen coor</returns>
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);
/// <summary>
/// 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<double> 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<std::shared_ptr<SceneRaycasterItem>> &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<SceneRaycasterItem> &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<AABBMesh>(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<double>();
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<Vec3d> 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<RaycastManager::Hit> hit_opt = raycast_manager.unproject(point, direction, &cond);
std::optional<RaycastManager::Hit> 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<RaycastManager::Hit> 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<RaycastManager::Hit> hit = raycaster.ray_from_camera(screen_coor, camera, &cond);
std::optional<RaycastManager::Hit> 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

View file

@ -1,16 +1,11 @@
#include "RaycastManager.hpp"
#include <utility>
// 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<bool> 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<bool> 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::Hit> RaycastManager::ray_from_camera(
const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const
std::optional<RaycastManager::Hit> 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::Hit> 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::Hit> 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<AABBMesh::hit_result> hits = mesh->query_ray_hits(point, direction);
// transform input into mesh world
Vec3d point_ = inv * point;
Vec3d direction_= inv.linear() * direction;
std::vector<AABBMesh::hit_result> 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::Hit> 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::Hit> RaycastManager::ray_from_camera(
return RaycastManager::Hit{point_world, *result.key, result.squared_distance};
}
std::optional<RaycastManager::Hit> RaycastManager::unproject(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const
std::optional<RaycastManager::Hit> RaycastManager::closest_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const
{
std::optional<Hit> 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<bool> 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<AABBMesh>(volume->mesh(), calculate_epsilon);

View file

@ -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:
/// </summary>
/// <param name="object">Model representation</param>
/// <param name="skip">Condifiton for skip actualization</param>
void actualize(const ModelObject *object, const ISkip *skip = nullptr);
void actualize(const ModelInstance *instance, const ISkip *skip = nullptr);
/// <param name="meshes">Speed up for already created AABBtrees</param>
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:
};
/// <summary>
/// Unproject on mesh by Mesh raycasters
/// Unproject on mesh and return closest hit to point in given direction
/// </summary>
/// <param name="mouse_pos">Position of mouse on screen</param>
/// <param name="camera">Projection params</param>
/// <param name="point">Position in space</param>
/// <param name="direction">Casted ray direction</param>
/// <param name="skip">Define which caster will be skipped, null mean no skip</param>
/// <returns>Position on surface, normal direction in world coorinate
/// + key, to know hitted instance and volume</returns>
std::optional<Hit> ray_from_camera(const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip = nullptr) const;
std::optional<Hit> first_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const;
/// <summary>
/// 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 !!
/// </summary>
/// <param name="point">Start point for ray</param>
/// <param name="direction">Direction of ray</param>
/// <param name="direction">Direction of ray, orientation doesn't matter, both are used</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;
std::optional<Hit> closest_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const;
/// <summary>
/// Search of closest point