Tech ENABLE_RAYCAST_PICKING - Raytraced picking of volumes

This commit is contained in:
enricoturri1966 2023-10-28 17:54:09 +08:00 committed by Noisyfox
parent 02f83f29c7
commit 3577a259d5
19 changed files with 690 additions and 152 deletions

View file

@ -6233,7 +6233,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
continue;
volume_count++;
if (m_share_mesh) {
auto iter = m_shared_meshes.find(volume->mesh_ptr());
auto iter = m_shared_meshes.find(volume->mesh_ptr().get());
if (iter != m_shared_meshes.end())
{
const ModelVolume* shared_volume = iter->second.second;
@ -6248,7 +6248,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
continue;
}
}
const_cast<_BBS_3MF_Exporter *>(this)->m_shared_meshes.insert({volume->mesh_ptr(), {&object_data, volume}});
const_cast<_BBS_3MF_Exporter *>(this)->m_shared_meshes.insert({volume->mesh_ptr().get(), {&object_data, volume}});
}
if (m_from_backup_save)
volume_id = (volume_count << 16 | backup_id);

View file

@ -320,6 +320,67 @@ Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation,
return transform;
}
void assemble_transform(Transform3d& transform, const Transform3d& translation, const Transform3d& rotation, const Transform3d& scale, const Transform3d& mirror)
{
transform = translation * rotation * scale * mirror;
}
Transform3d assemble_transform(const Transform3d& translation, const Transform3d& rotation, const Transform3d& scale, const Transform3d& mirror)
{
Transform3d transform;
assemble_transform(transform, translation, rotation, scale, mirror);
return transform;
}
void translation_transform(Transform3d& transform, const Vec3d& translation)
{
transform = Transform3d::Identity();
transform.translate(translation);
}
Transform3d translation_transform(const Vec3d& translation)
{
Transform3d transform;
translation_transform(transform, translation);
return transform;
}
void rotation_transform(Transform3d& transform, const Vec3d& rotation)
{
transform = Transform3d::Identity();
transform.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ()) * Eigen::AngleAxisd(rotation.y(), Vec3d::UnitY()) * Eigen::AngleAxisd(rotation.x(), Vec3d::UnitX()));
}
Transform3d rotation_transform(const Vec3d& rotation)
{
Transform3d transform;
rotation_transform(transform, rotation);
return transform;
}
void scale_transform(Transform3d& transform, double scale)
{
return scale_transform(transform, scale * Vec3d::Ones());
}
void scale_transform(Transform3d& transform, const Vec3d& scale)
{
transform = Transform3d::Identity();
transform.scale(scale);
}
Transform3d scale_transform(double scale)
{
return scale_transform(scale * Vec3d::Ones());
}
Transform3d scale_transform(const Vec3d& scale)
{
Transform3d transform;
scale_transform(transform, scale);
return transform;
}
Vec3d extract_euler_angles(const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>& rotation_matrix)
{
// reference: http://www.gregslabaugh.net/publications/euler.pdf
@ -405,20 +466,6 @@ void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d& rotation_axis, doubl
}
}
Transform3d translation_transform(const Vec3d &translation)
{
Transform3d transform = Transform3d::Identity();
transform.translate(translation);
return transform;
}
Transform3d rotation_transform(const Vec3d& rotation)
{
Transform3d transform = Transform3d::Identity();
transform.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ()) * Eigen::AngleAxisd(rotation.y(), Vec3d::UnitY()) * Eigen::AngleAxisd(rotation.x(), Vec3d::UnitX()));
return transform;
}
bool Transformation::Flags::needs_update(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const
{
return (this->dont_translate != dont_translate) || (this->dont_rotate != dont_rotate) || (this->dont_scale != dont_scale) || (this->dont_mirror != dont_mirror);

View file

@ -324,7 +324,8 @@ bool arrange(
// 4) rotate Y
// 5) rotate Z
// 6) translate
void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones());
void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(),
const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones());
// Returns the transform obtained by assembling the given transformations in the following order:
// 1) mirror
@ -333,7 +334,45 @@ void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d
// 4) rotate Y
// 5) rotate Z
// 6) translate
Transform3d assemble_transform(const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones());
Transform3d assemble_transform(const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(),
const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones());
// Sets the given transform by multiplying the given transformations in the following order:
// T = translation * rotation * scale * mirror
void assemble_transform(Transform3d& transform, const Transform3d& translation = Transform3d::Identity(),
const Transform3d& rotation = Transform3d::Identity(), const Transform3d& scale = Transform3d::Identity(),
const Transform3d& mirror = Transform3d::Identity());
// Returns the transform obtained by multiplying the given transformations in the following order:
// T = translation * rotation * scale * mirror
Transform3d assemble_transform(const Transform3d& translation = Transform3d::Identity(), const Transform3d& rotation = Transform3d::Identity(),
const Transform3d& scale = Transform3d::Identity(), const Transform3d& mirror = Transform3d::Identity());
// Sets the given transform by assembling the given translation
void translation_transform(Transform3d& transform, const Vec3d& translation);
// Returns the transform obtained by assembling the given translation
Transform3d translation_transform(const Vec3d& translation);
// Sets the given transform by assembling the given rotations in the following order:
// 1) rotate X
// 2) rotate Y
// 3) rotate Z
void rotation_transform(Transform3d& transform, const Vec3d& rotation);
// Returns the transform obtained by assembling the given rotations in the following order:
// 1) rotate X
// 2) rotate Y
// 3) rotate Z
Transform3d rotation_transform(const Vec3d& rotation);
// Sets the given transform by assembling the given scale factors
void scale_transform(Transform3d& transform, double scale);
void scale_transform(Transform3d& transform, const Vec3d& scale);
// Returns the transform obtained by assembling the given scale factors
Transform3d scale_transform(double scale);
Transform3d scale_transform(const Vec3d& scale);
// Returns the euler angles extracted from the given rotation matrix
// Warning -> The matrix should not contain any scale or shear !!!
@ -348,15 +387,6 @@ Vec3d extract_euler_angles(const Transform3d& transform);
// Euler angles can be obtained by extract_euler_angles()
void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d &rotation_axis, double &phi, Matrix3d *rotation_matrix = nullptr);
// Returns the transform obtained by assembling the given translation
Transform3d translation_transform(const Vec3d &translation);
// Returns the transform obtained by assembling the given rotations in the following order:
// 1) rotate X
// 2) rotate Y
// 3) rotate Z
Transform3d rotation_transform(const Vec3d &rotation);
class Transformation
{
struct Flags

View file

@ -883,7 +883,7 @@ public:
// The triangular model.
const TriangleMesh& mesh() const { return *m_mesh.get(); }
const TriangleMesh* mesh_ptr() const { return m_mesh.get(); }
std::shared_ptr<const TriangleMesh> mesh_ptr() const { return m_mesh; }
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<const TriangleMesh>(std::move(mesh)); }
void set_mesh(const indexed_triangle_set &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }

View file

@ -61,6 +61,8 @@
#define ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT (1 && ENABLE_2_4_0_BETA2)
// Enable fit print volume command for circular printbeds
#define ENABLE_ENHANCED_PRINT_VOLUME_FIT (1 && ENABLE_2_4_0_BETA2)
// Enable picking using raytracing
#define ENABLE_RAYCAST_PICKING_DEBUG (1)
#endif // _prusaslicer_technologies_h_

View file

@ -99,6 +99,8 @@ set(SLIC3R_GUI_SOURCES
GUI/GLShader.hpp
GUI/GLCanvas3D.hpp
GUI/GLCanvas3D.cpp
GUI/SceneRaycaster.hpp
GUI/SceneRaycaster.cpp
GUI/OpenGLManager.hpp
GUI/OpenGLManager.cpp
GUI/Selection.hpp

View file

@ -30,7 +30,6 @@
static const float GROUND_Z = -0.04f;
static const Slic3r::ColorRGBA DEFAULT_MODEL_COLOR = { 0.3255f, 0.337f, 0.337f, 1.0f };
static const Slic3r::ColorRGBA DEFAULT_MODEL_COLOR_DARK = { 0.255f, 0.255f, 0.283f, 1.0f };
static const Slic3r::ColorRGBA PICKING_MODEL_COLOR = Slic3r::ColorRGBA::BLACK();
static const Slic3r::ColorRGBA DEFAULT_SOLID_GRID_COLOR = { 0.9f, 0.9f, 0.9f, 1.0f };
static const Slic3r::ColorRGBA DEFAULT_TRANSPARENT_GRID_COLOR = { 0.9f, 0.9f, 0.9f, 0.6f };
@ -326,6 +325,10 @@ bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_heig
m_axes.set_origin({ 0.0, 0.0, static_cast<double>(GROUND_Z) });
m_axes.set_stem_length(0.1f * static_cast<float>(m_build_volume.bounding_volume().max_size()));
// unregister from picking
// BBS: remove the bed picking logic
// wxGetApp().plater()->canvas3D()->remove_raycasters_for_picking(SceneRaycaster::EType::Bed);
// Let the calee to update the UI.
return true;
}
@ -366,11 +369,6 @@ void Bed3D::render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Tra
render_internal(canvas, view_matrix, projection_matrix, bottom, scale_factor, show_axes);
}
/*void Bed3D::render_for_picking(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor)
{
render_internal(canvas, view_matrix, projection_matrix, bottom, scale_factor, false, false, true);
}*/
void Bed3D::render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor,
bool show_axes)
{
@ -669,6 +667,9 @@ void Bed3D::render_model(const Transform3d& view_matrix, const Transform3d& proj
m_model.set_color(m_is_dark ? DEFAULT_MODEL_COLOR_DARK : DEFAULT_MODEL_COLOR);
update_model_offset();
// BBS: remove the bed picking logic
//register_raycasters_for_picking(m_model.model.get_geometry(), Geometry::assemble_transform(m_model_offset));
}
if (!m_model.get_filename().empty()) {
@ -722,7 +723,7 @@ void Bed3D::render_default(bool bottom, const Transform3d& view_matrix, const Tr
if (m_model.get_filename().empty() && !bottom) {
// draw background
glsafe(::glDepthMask(GL_FALSE));
m_triangles.set_color(picking ? PICKING_MODEL_COLOR : DEFAULT_MODEL_COLOR);
m_triangles.set_color(DEFAULT_MODEL_COLOR);
m_triangles.render();
glsafe(::glDepthMask(GL_TRUE));
}
@ -740,5 +741,26 @@ void Bed3D::render_default(bool bottom, const Transform3d& view_matrix, const Tr
}
}
// BBS: remove the bed picking logic
/*
void Bed3D::register_raycasters_for_picking(const GLModel::Geometry& geometry, const Transform3d& trafo)
{
assert(m_model.mesh_raycaster == nullptr);
indexed_triangle_set its;
its.vertices.reserve(geometry.vertices_count());
for (size_t i = 0; i < geometry.vertices_count(); ++i) {
its.vertices.emplace_back(geometry.extract_position_3(i));
}
its.indices.reserve(geometry.indices_count() / 3);
for (size_t i = 0; i < geometry.indices_count() / 3; ++i) {
const size_t tri_id = i * 3;
its.indices.emplace_back(geometry.extract_index(tri_id), geometry.extract_index(tri_id + 1), geometry.extract_index(tri_id + 2));
}
m_model.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
wxGetApp().plater()->canvas3D()->add_raycaster_for_picking(SceneRaycaster::EType::Bed, 0, *m_model.mesh_raycaster, trafo);
}
*/
} // GUI
} // Slic3r

View file

@ -147,7 +147,6 @@ public:
Point point_projection(const Point& point) const;
void render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes);
//void render_for_picking(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor);
void on_change_color_mode(bool is_dark);
@ -167,6 +166,9 @@ private:
void render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix);
void render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom);
void render_default(bool bottom, const Transform3d& view_matrix, const Transform3d& projection_matrix);
// BBS: remove the bed picking logic
// void register_raycasters_for_picking(const GLModel::Geometry& geometry, const Transform3d& trafo);
};
} // GUI

View file

@ -623,7 +623,7 @@ int GLVolumeCollection::load_object_volume(
const ModelVolume *model_volume = model_object->volumes[volume_idx];
const int extruder_id = model_volume->extruder_id();
const ModelInstance *instance = model_object->instances[instance_idx];
const TriangleMesh &mesh = model_volume->mesh();
std::shared_ptr<const TriangleMesh> mesh = model_volume->mesh_ptr();
this->volumes.emplace_back(new GLVolume());
GLVolume& v = *this->volumes.back();
v.set_color(color_from_model_volume(*model_volume));
@ -632,7 +632,8 @@ int GLVolumeCollection::load_object_volume(
#if ENABLE_SMOOTH_NORMALS
v.model.init_from(mesh, true);
#else
v.model.init_from(mesh);
v.model.init_from(*mesh);
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh);
#endif // ENABLE_SMOOTH_NORMALS
v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx);
if (model_volume->is_model_part()) {
@ -686,8 +687,9 @@ void GLVolumeCollection::load_object_auxiliary(
v.model.init_from(mesh, true);
#else
v.model.init_from(mesh);
#endif // ENABLE_SMOOTH_NORMALS
v.model.set_color((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR);
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<const TriangleMesh>(mesh));
#endif // ENABLE_SMOOTH_NORMALS
v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first);
v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
// Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
@ -726,13 +728,6 @@ int GLVolumeCollection::load_wipe_tower_preview(
colors.push_back(extruder_colors[0]);
}
#if 0
// We'll make another mesh to show the brim (fixed layer height):
TriangleMesh brim_mesh = make_cube(width + 2.f * brim_width, depth + 2.f * brim_width, 0.2f);
brim_mesh.translate(-brim_width, -brim_width, 0.f);
mesh.merge(brim_mesh);
#endif
// Orca: make it transparent
for(auto& color : colors)
color.a(0.66f);
@ -745,6 +740,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
v.model_per_colors[i].init_from(color_part);
}
v.model.init_from(wipe_tower_shell);
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<const TriangleMesh>(wipe_tower_shell));
v.set_convex_hull(wipe_tower_shell);
v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));

View file

@ -13,6 +13,7 @@
#include "GLModel.hpp"
#include "GLShader.hpp"
#include "MeshUtils.hpp"
#include <functional>
#include <optional>
@ -202,6 +203,8 @@ public:
EHoverState hover;
GUI::GLModel model;
// raycaster used for picking
std::unique_ptr<GUI::MeshRaycaster> mesh_raycaster;
// BBS
mutable std::vector<GUI::GLModel> mmuseg_models;
mutable ObjectBase::Timestamp mmuseg_ts;

View file

@ -1820,9 +1820,17 @@ void GLCanvas3D::render(bool only_init)
_rectangular_selection_picking_pass();
//BBS: enable picking when no volumes for partplate logic
//else if (!m_volumes.empty())
else
else {
// regular picking pass
_picking_pass();
#if ENABLE_RAYCAST_PICKING_DEBUG
ImGuiWrapper& imgui = *wxGetApp().imgui();
imgui.begin(std::string("Hit result"), ImGuiWindowFlags_AlwaysAutoResize);
imgui.text("Picking disabled");
imgui.end();
#endif // ENABLE_RAYCAST_PICKING_DEBUG
}
}
#if ENABLE_RENDER_PICKING_PASS
@ -1896,6 +1904,11 @@ void GLCanvas3D::render(bool only_init)
}
#endif // ENABLE_RENDER_PICKING_PASS
#if ENABLE_RAYCAST_PICKING_DEBUG
if (m_picking_enabled && !m_mouse.dragging)
m_scene_raycaster.render_hit(camera);
#endif // ENABLE_RAYCAST_PICKING_DEBUG
#if ENABLE_SHOW_CAMERA_TARGET
_render_camera_target();
#endif // ENABLE_SHOW_CAMERA_TARGET
@ -2516,6 +2529,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
volume.model.init_from(mesh, true);
#else
volume.model.init_from(mesh);
volume.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<TriangleMesh>(mesh));
#endif // ENABLE_SMOOTH_NORMALS
}
else {
@ -2523,7 +2537,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
#if ENABLE_SMOOTH_NORMALS
volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true);
#else
volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh());
const TriangleMesh& new_mesh = m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh();
volume.model.init_from(new_mesh);
volume.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<TriangleMesh>(new_mesh));
#endif // ENABLE_SMOOTH_NORMALS
}
}
@ -2688,6 +2704,13 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
#endif
}
// refresh volume raycasters for picking
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Volume);
for (size_t i = 0; i < m_volumes.volumes.size(); ++i) {
assert(m_volumes.volumes[i]->mesh_raycaster != nullptr);
add_raycaster_for_picking(SceneRaycaster::EType::Volume, i, *m_volumes.volumes[i]->mesh_raycaster, m_volumes.volumes[i]->world_matrix());
}
// and force this canvas to be redrawn.
m_dirty = true;
}
@ -4205,6 +4228,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) {
m_mouse.position = pos.cast<double>();
if (evt.LeftUp()) {
m_selection.stop_dragging();
m_rotation_center(0) = m_rotation_center(1) = m_rotation_center(2) = 0.f;
@ -4247,7 +4272,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
deselect_all();
}
else if (evt.RightUp() && !is_layers_editing_enabled()) {
m_mouse.position = pos.cast<double>();
// forces a frame render to ensure that m_hover_volume_idxs is updated even when the user right clicks while
// the context menu is already shown
render();
@ -6396,88 +6420,112 @@ void GLCanvas3D::_refresh_if_shown_on_screen()
void GLCanvas3D::_picking_pass()
{
std::vector<int>* hover_volume_idxs = const_cast<std::vector<int>*>(&m_hover_volume_idxs);
std::vector<int>* hover_plate_idxs = const_cast<std::vector<int>*>(&m_hover_plate_idxs);
if (m_picking_enabled && !m_mouse.dragging && m_mouse.position != Vec2d(DBL_MAX, DBL_MAX)) {
hover_volume_idxs->clear();
hover_plate_idxs->clear();
// Render the object for picking.
// FIXME This cannot possibly work in a multi - sampled context as the color gets mangled by the anti - aliasing.
// Better to use software ray - casting on a bounding - box hierarchy.
if (m_multisample_allowed)
// This flag is often ignored by NVIDIA drivers if rendering into a screen buffer.
glsafe(::glDisable(GL_MULTISAMPLE));
glsafe(::glDisable(GL_BLEND));
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
//BBS: only render plate in view 3D
if (m_canvas_type == ECanvasType::CanvasView3D) {
const Camera &camera = wxGetApp().plater()->get_camera();
_render_plates_for_picking(camera.get_view_matrix(), camera.get_projection_matrix());
if (!m_picking_enabled || m_mouse.dragging || m_mouse.position == Vec2d(DBL_MAX, DBL_MAX)) {
ImGuiWrapper& imgui = *wxGetApp().imgui();
imgui.begin(std::string("Hit result"), ImGuiWindowFlags_AlwaysAutoResize);
imgui.text("Picking disabled");
imgui.end();
return;
}
_render_volumes_for_picking();
m_hover_volume_idxs.clear();
m_hover_plate_idxs.clear();
//BBS: remove the bed picking logic
//_render_bed_for_picking(!wxGetApp().plater()->get_camera().is_looking_downward());
// TODO: Support plate picking
m_gizmos.render_current_gizmo_for_picking_pass();
if (m_multisample_allowed)
glsafe(::glEnable(GL_MULTISAMPLE));
int volume_id = -1;
int gizmo_id = -1;
std::array<GLubyte, 4> color = { 0, 0, 0, 0 };
const Size& cnv_size = get_canvas_size();
bool inside = 0 <= m_mouse.position(0) && m_mouse.position(0) < cnv_size.get_width() && 0 <= m_mouse.position(1) && m_mouse.position(1) < cnv_size.get_height();
if (inside) {
glsafe(::glReadPixels(m_mouse.position(0), cnv_size.get_height() - m_mouse.position.y() - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color.data()));
if (picking_checksum_alpha_channel(color[0], color[1], color[2]) == color[3]) {
// Only non-interpolated colors are valid, those have their lowest three bits zeroed.
// we reserve color = (0,0,0) for occluders (as the printbed)
// volumes' id are shifted by 1
// see: _render_volumes_for_picking()
unsigned int id = picking_encode(color[0], color[1], color[2]);
//BBS: remove the bed picking logic
//volume_id = id - 1;
volume_id = id;
// gizmos' id are instead properly encoded by the color
gizmo_id = id;
}
}
else
m_gizmos.set_hover_id(inside && (unsigned int)gizmo_id <= GLGizmoBase::BASE_ID ? ((int)GLGizmoBase::BASE_ID - gizmo_id) : -1);
//BBS: add plate picking logic
int plate_hover_id = PartPlate::PLATE_BASE_ID - volume_id;
if (plate_hover_id >= 0 && plate_hover_id < PartPlateList::MAX_PLATES_COUNT * PartPlate::GRABBER_COUNT) {
wxGetApp().plater()->get_partplate_list().set_hover_id(plate_hover_id);
hover_plate_idxs->emplace_back(plate_hover_id);
const_cast<GLGizmosManager*>(&m_gizmos)->set_hover_id(-1);
}
else {
wxGetApp().plater()->get_partplate_list().reset_hover_id();
if (0 <= volume_id && volume_id < (int)m_volumes.volumes.size()) {
const ClippingPlane clipping_plane = m_gizmos.get_clipping_plane().inverted_normal();
const SceneRaycaster::HitResult hit = m_scene_raycaster.hit(m_mouse.position, wxGetApp().plater()->get_camera(), &clipping_plane);
if (hit.is_valid()) {
switch (hit.type)
{
case SceneRaycaster::EType::Volume:
{
if (0 <= hit.raycaster_id && hit.raycaster_id < (int)m_volumes.volumes.size()) {
const GLVolume* volume = m_volumes.volumes[hit.raycaster_id];
if (volume->is_active && !volume->disabled && (volume->composite_id.volume_id >= 0 || m_render_sla_auxiliaries)) {
// do not add the volume id if any gizmo is active and CTRL is pressed
if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL))
hover_volume_idxs->emplace_back(volume_id);
const_cast<GLGizmosManager*>(&m_gizmos)->set_hover_id(-1);
if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL)) {
m_hover_volume_idxs.emplace_back(hit.raycaster_id);
m_gizmos.set_hover_id(-1);
}
}
}
else
const_cast<GLGizmosManager*>(&m_gizmos)->set_hover_id(inside && (unsigned int)volume_id <= GLGizmoBase::BASE_ID ? ((int)GLGizmoBase::BASE_ID - volume_id) : -1);
assert(false);
break;
}
case SceneRaycaster::EType::Gizmo:
{
const Size& cnv_size = get_canvas_size();
bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() &&
0 <= m_mouse.position.y() && m_mouse.position.y() < cnv_size.get_height();
m_gizmos.set_hover_id(inside ? hit.raycaster_id : -1);
break;
}
case SceneRaycaster::EType::Bed:
{
m_gizmos.set_hover_id(-1);
break;
}
default:
{
assert(false);
break;
}
}
}
else
m_gizmos.set_hover_id(-1);
_update_volumes_hover_state();
#if ENABLE_RAYCAST_PICKING_DEBUG
ImGuiWrapper& imgui = *wxGetApp().imgui();
imgui.begin(std::string("Hit result"), ImGuiWindowFlags_AlwaysAutoResize);
std::string object_type = "None";
switch (hit.type)
{
case SceneRaycaster::EType::Bed: { object_type = "Bed"; break; }
case SceneRaycaster::EType::Gizmo: { object_type = "Gizmo element"; break; }
case SceneRaycaster::EType::Volume:
{
if (m_volumes.volumes[hit.raycaster_id]->is_wipe_tower)
object_type = "Wipe tower";
else if (m_volumes.volumes[hit.raycaster_id]->volume_idx() == -int(slaposPad))
object_type = "SLA pad";
else if (m_volumes.volumes[hit.raycaster_id]->volume_idx() == -int(slaposSupportTree))
object_type = "SLA supports";
else
object_type = "Volume";
break;
}
}
char buf[1024];
if (hit.type != SceneRaycaster::EType::None) {
sprintf(buf, "Object ID: %d", hit.raycaster_id);
imgui.text(std::string(buf));
sprintf(buf, "Type: %s", object_type.c_str());
imgui.text(std::string(buf));
sprintf(buf, "Position: %.3f, %.3f, %.3f", hit.position.x(), hit.position.y(), hit.position.z());
imgui.text(std::string(buf));
sprintf(buf, "Normal: %.3f, %.3f, %.3f", hit.normal.x(), hit.normal.y(), hit.normal.z());
imgui.text(std::string(buf));
}
else
imgui.text("NO HIT");
ImGui::Separator();
imgui.text("Registered for picking:");
sprintf(buf, "Beds: %d", (int)m_scene_raycaster.beds_count());
imgui.text(std::string(buf));
sprintf(buf, "Volumes: %d", (int)m_scene_raycaster.volumes_count());
imgui.text(std::string(buf));
sprintf(buf, "Gizmo elements: %d", (int)m_scene_raycaster.gizmos_count());
imgui.text(std::string(buf));
imgui.end();
#endif // ENABLE_RAYCAST_PICKING_DEBUG
}
void GLCanvas3D::_rectangular_selection_picking_pass()
{
@ -6643,26 +6691,11 @@ void GLCanvas3D::_render_bed(const Transform3d& view_matrix, const Transform3d&
m_bed.render(*this, view_matrix, projection_matrix, bottom, scale_factor, show_axes);
}
void GLCanvas3D::_render_bed_for_picking(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom)
{
float scale_factor = 1.0;
#if ENABLE_RETINA_GL
scale_factor = m_retina_helper->get_scale_factor();
#endif // ENABLE_RETINA_GL
//m_bed.render_for_picking(*this, view_matrix, projection_matrix, bottom, scale_factor);
}
void GLCanvas3D::_render_platelist(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali)
{
wxGetApp().plater()->get_partplate_list().render(view_matrix, projection_matrix, bottom, only_current, only_body, hover_id, render_cali);
}
void GLCanvas3D::_render_plates_for_picking(const Transform3d &view_matrix, const Transform3d &projection_matrix)
{
wxGetApp().plater()->get_partplate_list().render_for_picking_pass(view_matrix, projection_matrix);
}
void GLCanvas3D::_render_plane() const
{
;//TODO render assemble plane

View file

@ -16,6 +16,7 @@
#include "libslic3r/GCode/GCodeProcessor.hpp"
#include "GCodeViewer.hpp"
#include "Camera.hpp"
#include "SceneRaycaster.hpp"
#include "IMToolbar.hpp"
#include "libslic3r/Slicing.hpp"
@ -501,6 +502,7 @@ private:
bool m_is_dark = false;
wxGLCanvas* m_canvas;
wxGLContext* m_context;
SceneRaycaster m_scene_raycaster;
Bed3D &m_bed;
#if ENABLE_RETINA_GL
std::unique_ptr<RetinaHelper> m_retina_helper;
@ -727,6 +729,16 @@ public:
bool init();
void post_event(wxEvent &&event);
void add_raycaster_for_picking(SceneRaycaster::EType type, PickingId id, const MeshRaycaster& raycaster, const Transform3d& trafo) {
m_scene_raycaster.add_raycaster(type, id, raycaster, trafo);
}
void remove_raycasters_for_picking(SceneRaycaster::EType type, PickingId id) {
m_scene_raycaster.remove_raycasters(type, id);
}
void remove_raycasters_for_picking(SceneRaycaster::EType type) {
m_scene_raycaster.remove_raycasters(type);
}
void reset_explosion_ratio() { m_explosion_ratio = 1.0; }
void on_change_color_mode(bool is_dark, bool reinit = true);
const bool get_dark_mode_status() { return m_is_dark; }
@ -1116,10 +1128,8 @@ private:
void _rectangular_selection_picking_pass();
void _render_background();
void _render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes);
void _render_bed_for_picking(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom);
//BBS: add part plate related logic
void _render_platelist(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current, bool only_body = false, int hover_id = -1, bool render_cali = false);
void _render_plates_for_picking(const Transform3d& view_matrix, const Transform3d& projection_matrix);
//BBS: add outline drawing logic
void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true);
//BBS: GUI refactor: add canvas size as parameters

View file

@ -50,7 +50,7 @@ bool GLGizmoMeshBoolean::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
// Cast a ray on all meshes, pick the closest hit and save it for the respective mesh
for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) {
MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh());
MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh_ptr());
if (mesh_raycaster.unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal,
m_c->object_clipper()->get_clipping_plane(), &facet)) {
// Is this hit the closest to the camera so far?

View file

@ -287,7 +287,7 @@ bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_posit
// Cast a ray on all meshes, pick the closest hit and save it for the respective mesh
for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) {
MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh());
MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh_ptr());
if (mesh_raycaster.unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal,
m_c->object_clipper()->get_clipping_plane(), &facet)) {
@ -543,7 +543,7 @@ void GLGizmoText::on_update(const UpdateData &data)
if (mesh_id == m_volume_idx)
continue;
MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh());
MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh_ptr());
if (mesh_raycaster.unproject_on_mesh(mouse_pos, trafo_matrices[mesh_id], camera, hit, normal, m_c->object_clipper()->get_clipping_plane(),
&facet)) {

View file

@ -331,7 +331,7 @@ void Raycaster::on_update()
if (meshes != m_old_meshes) {
m_raycasters.clear();
for (const TriangleMesh* mesh : meshes)
m_raycasters.emplace_back(new MeshRaycaster(*mesh));
m_raycasters.emplace_back(new MeshRaycaster(std::make_shared<const TriangleMesh>(*mesh)));
m_old_meshes = meshes;
}
}

View file

@ -357,6 +357,38 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
return out;
}
bool MeshRaycaster::closest_hit(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);
const std::vector<sla::IndexedMesh::hit_result> hits = m_emesh.query_ray_hits(point, direction.normalized());
if (hits.empty())
return false; // no intersection found
size_t hit_id = 0;
if (clipping_plane != nullptr) {
while (hit_id < hits.size() && clipping_plane->is_point_clipped(trafo * hits[hit_id].position())) {
++hit_id;
}
}
if (hit_id == hits.size())
return false; // all points are obscured or cut by the clipping plane.
const sla::IndexedMesh::hit_result& hit = hits[hit_id];
position = hit.position().cast<float>();
normal = hit.normal().cast<float>();
if (facet_idx != nullptr)
*facet_idx = hit.face();
return true;
}
Vec3f MeshRaycaster::get_closest_point(const Vec3f& point, Vec3f* normal) const
{

View file

@ -9,6 +9,7 @@
#include "slic3r/GUI/GLModel.hpp"
#include <cfloat>
#include <memory>
namespace Slic3r {
@ -52,6 +53,8 @@ public:
void set_offset(double offset) { m_data[3] = offset; }
double get_offset() const { return m_data[3]; }
Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); }
void invert_normal() { m_data[0] *= -1.0; m_data[1] *= -1.0; m_data[2] *= -1.0; }
ClippingPlane inverted_normal() const { return ClippingPlane(-get_normal(), get_offset()); }
bool is_active() const { return m_data[3] != DBL_MAX; }
static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); }
const std::array<double, 4>& get_data() const { return m_data; }
@ -125,11 +128,10 @@ private:
// whether certain points are visible or obscured by the mesh etc.
class MeshRaycaster {
public:
// The class references extern TriangleMesh, which must stay alive
// during MeshRaycaster existence.
MeshRaycaster(const TriangleMesh& mesh)
: m_emesh(mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length
, m_normals(its_face_normals(mesh.its))
explicit MeshRaycaster(std::shared_ptr<const TriangleMesh> mesh)
: m_mesh(mesh)
, m_emesh(*mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length
, m_normals(its_face_normals(mesh->its))
{
}
@ -161,10 +163,22 @@ public:
const ClippingPlane* clipping_plane = nullptr // clipping plane (if active)
) const;
// Returns true if the ray, built from mouse position and camera direction, intersects the mesh.
// In this case, position and normal contain the position and normal, in model coordinates, of the intersection closest to the camera,
// depending on the position/orientation of the clipping_plane, if specified
bool closest_hit(
const Vec2d& mouse_pos,
const Transform3d& trafo, // how to get the mesh into world coords
const Camera& camera, // current camera position
Vec3f& position, // where to save the positibon of the hit (mesh coords)
Vec3f& normal, // normal of the triangle that was hit
const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active)
size_t* facet_idx = nullptr // index of the facet hit
) const;
// Given a point in world coords, the method returns closest point on the mesh.
// The output is in mesh coords.
// normal* can be used to also get normal of the respective triangle.
Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const;
// Given a point in mesh coords, the method returns the closest facet from mesh.
@ -173,10 +187,21 @@ public:
Vec3f get_triangle_normal(size_t facet_idx) const;
private:
std::shared_ptr<const TriangleMesh> m_mesh;
sla::IndexedMesh m_emesh;
std::vector<stl_normal> m_normals;
};
struct PickingModel
{
GLModel model;
std::unique_ptr<MeshRaycaster> mesh_raycaster;
void reset() {
model.reset();
mesh_raycaster.reset();
}
};
} // namespace GUI
} // namespace Slic3r

View file

@ -0,0 +1,220 @@
#include "libslic3r/libslic3r.h"
#include "SceneRaycaster.hpp"
#include "Camera.hpp"
#include "GUI_App.hpp"
namespace Slic3r {
namespace GUI {
SceneRaycaster::SceneRaycaster() {
#if ENABLE_RAYCAST_PICKING_DEBUG
// hit point
m_sphere.init_from(its_make_sphere(1.0, double(PI) / 16.0));
m_sphere.set_color(ColorRGBA::YELLOW());
// hit normal
GLModel::Geometry init_data;
init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 };
init_data.color = ColorRGBA::YELLOW();
init_data.reserve_vertices(2);
init_data.reserve_indices(2);
// vertices
init_data.add_vertex((Vec3f)Vec3f::Zero());
init_data.add_vertex((Vec3f)Vec3f::UnitZ());
// indices
init_data.add_line(0, 1);
m_line.init_from(std::move(init_data));
#endif // ENABLE_RAYCAST_PICKING_DEBUG
}
void SceneRaycaster::add_raycaster(EType type, PickingId id, const MeshRaycaster& raycaster, const Transform3d& trafo)
{
switch (type) {
case EType::Bed: {
m_bed.emplace_back(encode_id(type, id), raycaster, trafo);
break;
}
case EType::Volume: {
m_volumes.emplace_back(encode_id(type, id), raycaster, trafo);
break;
}
case EType::Gizmo: {
m_gizmos.emplace_back(encode_id(type, id), raycaster, trafo);
break;
}
default: { break; }
};
}
void SceneRaycaster::remove_raycasters(EType type, PickingId id)
{
std::vector<SceneRaycasterItem>* raycasters = get_raycasters(type);
auto it = raycasters->begin();
while (it != raycasters->end()) {
if (it->get_id() == encode_id(type, id))
it = raycasters->erase(it);
else
++it;
}
}
void SceneRaycaster::remove_raycasters(EType type)
{
switch (type) {
case EType::Bed: { m_bed.clear(); break; }
case EType::Volume: { m_volumes.clear(); break; }
case EType::Gizmo: { m_gizmos.clear(); break; }
default: { break; }
};
}
void SceneRaycaster::set_raycaster_active_state(EType type, PickingId id, bool active)
{
std::vector<SceneRaycasterItem>* raycasters = get_raycasters(type);
for (SceneRaycasterItem& item : *raycasters) {
if (item.get_id() == encode_id(type, id)) {
item.set_active(active);
break;
}
}
}
void SceneRaycaster::set_raycaster_transform(EType type, PickingId id, const Transform3d& trafo)
{
std::vector<SceneRaycasterItem>* raycasters = get_raycasters(type);
for (SceneRaycasterItem& item : *raycasters) {
if (item.get_id() == encode_id(type, id)) {
item.set_transform(trafo);
break;
}
}
}
SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane)
{
double closest_hit_squared_distance = std::numeric_limits<double>::max();
auto is_closest = [&closest_hit_squared_distance](const Camera& camera, const Vec3f& hit) {
const double hit_squared_distance = (camera.get_position() - hit.cast<double>()).squaredNorm();
const bool ret = hit_squared_distance < closest_hit_squared_distance;
if (ret)
closest_hit_squared_distance = hit_squared_distance;
return ret;
};
#if ENABLE_RAYCAST_PICKING_DEBUG
m_last_hit.reset();
#endif // ENABLE_RAYCAST_PICKING_DEBUG
HitResult ret;
auto test_raycasters = [&](EType type) {
const ClippingPlane* clip_plane = (clipping_plane != nullptr && type == EType::Volume) ? clipping_plane : nullptr;
const std::vector<SceneRaycasterItem>* raycasters = get_raycasters(type);
HitResult current_hit = { type };
for (const SceneRaycasterItem& item : *raycasters) {
if (!item.is_active())
continue;
current_hit.raycaster_id = item.get_id();
const Transform3d& trafo = item.get_transform();
if (item.get_raycaster()->closest_hit(mouse_pos, trafo, camera, current_hit.position, current_hit.normal, clip_plane)) {
current_hit.position = (trafo * current_hit.position.cast<double>()).cast<float>();
if (is_closest(camera, current_hit.position)) {
const Transform3d matrix = camera.get_view_matrix() * trafo;
const Matrix3d normal_matrix = (Matrix3d)trafo.matrix().block(0, 0, 3, 3).inverse().transpose();
current_hit.normal = (normal_matrix * current_hit.normal.cast<double>()).normalized().cast<float>();
ret = current_hit;
}
}
}
};
if (!m_gizmos.empty())
test_raycasters(EType::Gizmo);
if (!m_gizmos_on_top || !ret.is_valid()) {
if (camera.is_looking_downward() && !m_bed.empty())
test_raycasters(EType::Bed);
if (!m_volumes.empty())
test_raycasters(EType::Volume);
}
if (ret.is_valid())
ret.raycaster_id = decode_id(ret.type, ret.raycaster_id);
#if ENABLE_RAYCAST_PICKING_DEBUG
m_last_hit = ret;
#endif // ENABLE_RAYCAST_PICKING_DEBUG
return ret;
}
#if ENABLE_RAYCAST_PICKING_DEBUG
void SceneRaycaster::render_hit(const Camera& camera)
{
if (!m_last_hit.has_value() || !m_last_hit.value().is_valid())
return;
GLShaderProgram* shader = wxGetApp().get_shader("flat");
shader->start_using();
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
const Transform3d sphere_view_model_matrix = camera.get_view_matrix() * Geometry::translation_transform(m_last_hit.value().position.cast<double>()) *
Geometry::scale_transform(4.0 * camera.get_inv_zoom());
shader->set_uniform("view_model_matrix", sphere_view_model_matrix);
m_sphere.render();
Eigen::Quaterniond q;
Transform3d m = Transform3d::Identity();
m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3d::UnitZ(), m_last_hit.value().normal.cast<double>()).toRotationMatrix();
const Transform3d line_view_model_matrix = sphere_view_model_matrix * m * Geometry::scale_transform(10.0);
shader->set_uniform("view_model_matrix", line_view_model_matrix);
m_line.render();
shader->stop_using();
}
#endif // ENABLE_RAYCAST_PICKING_DEBUG
std::vector<SceneRaycasterItem>* SceneRaycaster::get_raycasters(EType type)
{
std::vector<SceneRaycasterItem>* ret = nullptr;
switch (type)
{
case EType::Bed: { ret = &m_bed; break; }
case EType::Volume: { ret = &m_volumes; break; }
case EType::Gizmo: { ret = &m_gizmos; break; }
}
assert(ret != nullptr);
return ret;
}
PickingId SceneRaycaster::base_id(EType type)
{
switch (type)
{
case EType::Bed: { return PickingId(EPickingIdBase::Bed); }
case EType::Volume: { return PickingId(EPickingIdBase::Volume); }
case EType::Gizmo: { return PickingId(EPickingIdBase::Gizmo); }
};
assert(false);
return -1;
}
PickingId SceneRaycaster::encode_id(EType type, PickingId id)
{
return base_id(type) + id;
}
PickingId SceneRaycaster::decode_id(EType type, PickingId id)
{
return id - base_id(type);
}
} // namespace GUI
} // namespace Slic3r

View file

@ -0,0 +1,114 @@
#ifndef slic3r_SceneRaycaster_hpp_
#define slic3r_SceneRaycaster_hpp_
#include "MeshUtils.hpp"
#include "GLModel.hpp"
#include <vector>
#include <string>
#include <optional>
namespace Slic3r {
namespace GUI {
struct Camera;
using PickingId = int;
class SceneRaycasterItem
{
PickingId m_id{ -1 };
bool m_active{ true };
const MeshRaycaster* m_raycaster;
Transform3d m_trafo;
public:
SceneRaycasterItem(PickingId id, const MeshRaycaster& raycaster, const Transform3d& trafo)
: m_id(id), m_raycaster(&raycaster), m_trafo(trafo)
{}
PickingId get_id() const { return m_id; }
bool is_active() const { return m_active; }
void set_active(bool active) { m_active = active; }
const MeshRaycaster* get_raycaster() const { return m_raycaster; }
const Transform3d& get_transform() const { return m_trafo; }
void set_transform(const Transform3d& trafo) { m_trafo = trafo; }
};
class SceneRaycaster
{
public:
enum class EType
{
None,
Bed,
Volume,
Gizmo
};
enum class EPickingIdBase
{
Bed = 0,
Volume = 1000,
Gizmo = 1000000
};
struct HitResult
{
EType type{ EType::None };
PickingId raycaster_id{ -1 };
Vec3f position{ Vec3f::Zero() };
Vec3f normal{ Vec3f::Zero() };
bool is_valid() const { return raycaster_id != -1; }
};
private:
std::vector<SceneRaycasterItem> m_bed;
std::vector<SceneRaycasterItem> m_volumes;
std::vector<SceneRaycasterItem> m_gizmos;
// When set to true, if checking gizmos returns a valid hit,
// the search is not performed on other types
bool m_gizmos_on_top{ false };
#if ENABLE_RAYCAST_PICKING_DEBUG
GLModel m_sphere;
GLModel m_line;
std::optional<HitResult> m_last_hit;
#endif // ENABLE_RAYCAST_PICKING_DEBUG
public:
SceneRaycaster();
void add_raycaster(EType type, PickingId picking_id, const MeshRaycaster& raycaster, const Transform3d& trafo);
void remove_raycasters(EType type, PickingId id);
void remove_raycasters(EType type);
void set_raycaster_active_state(EType type, PickingId picking_id, bool active);
void set_raycaster_transform(EType type, PickingId picking_id, const Transform3d& trafo);
void set_gizmos_on_top(bool value) { m_gizmos_on_top = value; }
HitResult hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane = nullptr);
#if ENABLE_RAYCAST_PICKING_DEBUG
void render_hit(const Camera& camera);
size_t beds_count() const { return m_bed.size(); }
size_t volumes_count() const { return m_volumes.size(); }
size_t gizmos_count() const { return m_gizmos.size(); }
#endif // ENABLE_RAYCAST_PICKING_DEBUG
private:
std::vector<SceneRaycasterItem>* get_raycasters(EType type);
static PickingId encode_id(EType type, PickingId id);
static PickingId decode_id(EType type, PickingId id);
static PickingId base_id(EType type);
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_SceneRaycaster_hpp_