Use already existing AABB trees for cast into scene.
Remove dependency on camera for RayCastManager.
This commit is contained in:
parent
6ee384557e
commit
42857d8ecb
3 changed files with 103 additions and 46 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue