Tech ENABLE_RAYCAST_PICKING - 1st installment - Raytraced picking of printbed

This commit is contained in:
enricoturri1966 2022-06-10 13:20:52 +02:00
parent 8f40270f93
commit cfc3988b9f
13 changed files with 741 additions and 2 deletions

View file

@ -351,12 +351,22 @@ Transform3d rotation_transform(const Vec3d& 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;

View file

@ -366,9 +366,11 @@ void rotation_transform(Transform3d& transform, const Vec3d& rotation);
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

View file

@ -83,5 +83,9 @@
#define ENABLE_GIZMO_GRABBER_REFACTOR (1 && ENABLE_2_5_0_ALPHA1)
// Enable copy of custom bed model and texture
#define ENABLE_COPY_CUSTOM_BED_MODEL_AND_TEXTURE (1 && ENABLE_2_5_0_ALPHA1)
// Enable picking using raytracing
#define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL)
#define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING)
#endif // _prusaslicer_technologies_h_

View file

@ -27,6 +27,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

@ -24,7 +24,9 @@
static const float GROUND_Z = -0.02f;
static const Slic3r::ColorRGBA DEFAULT_MODEL_COLOR = Slic3r::ColorRGBA::DARK_GRAY();
#if !ENABLE_RAYCAST_PICKING
static const Slic3r::ColorRGBA PICKING_MODEL_COLOR = Slic3r::ColorRGBA::BLACK();
#endif // !ENABLE_RAYCAST_PICKING
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 };
@ -249,6 +251,11 @@ bool Bed3D::set_shape(const Pointfs& bed_shape, const double max_print_height, c
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()));
#if ENABLE_RAYCAST_PICKING
// unregister from picking
wxGetApp().plater()->canvas3D()->remove_all_picking_raycasters(SceneRaycaster::EType::Bed);
#endif // ENABLE_RAYCAST_PICKING
// Let the calee to update the UI.
return true;
}
@ -266,13 +273,19 @@ Point Bed3D::point_projection(const Point& point) const
#if ENABLE_LEGACY_OPENGL_REMOVAL
void Bed3D::render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes, bool show_texture)
{
#if ENABLE_RAYCAST_PICKING
render_internal(canvas, view_matrix, projection_matrix, bottom, scale_factor, show_axes, show_texture);
#else
render_internal(canvas, view_matrix, projection_matrix, bottom, scale_factor, show_axes, show_texture, false);
#endif // ENABLE_RAYCAST_PICKING
}
#if !ENABLE_RAYCAST_PICKING
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);
}
#endif // !ENABLE_RAYCAST_PICKING
#else
void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes, bool show_texture)
{
@ -286,8 +299,13 @@ void Bed3D::render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_fact
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_RAYCAST_PICKING
void Bed3D::render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor,
bool show_axes, bool show_texture)
#else
void Bed3D::render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor,
bool show_axes, bool show_texture, bool picking)
#endif // ENABLE_RAYCAST_PICKING
#else
void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
bool show_axes, bool show_texture, bool picking)
@ -301,7 +319,9 @@ void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
glsafe(::glEnable(GL_DEPTH_TEST));
#if ENABLE_LEGACY_OPENGL_REMOVAL
#if !ENABLE_RAYCAST_PICKING
m_model.set_color(picking ? PICKING_MODEL_COLOR : DEFAULT_MODEL_COLOR);
#endif // !ENABLE_RAYCAST_PICKING
#else
m_model.set_color(-1, picking ? PICKING_MODEL_COLOR : DEFAULT_MODEL_COLOR);
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
@ -311,7 +331,11 @@ void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
#if ENABLE_LEGACY_OPENGL_REMOVAL
case Type::System: { render_system(canvas, view_matrix, projection_matrix, bottom, show_texture); break; }
default:
#if ENABLE_RAYCAST_PICKING
case Type::Custom: { render_custom(canvas, view_matrix, projection_matrix, bottom, show_texture); break; }
#else
case Type::Custom: { render_custom(canvas, view_matrix, projection_matrix, bottom, show_texture, picking); break; }
#endif // ENABLE_RAYCAST_PICKING
#else
case Type::System: { render_system(canvas, bottom, show_texture); break; }
default:
@ -341,7 +365,11 @@ BoundingBoxf3 Bed3D::calc_extended_bounding_box() const
out.merge(out.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, out.max.z()));
#endif // ENABLE_WORLD_COORDINATE
// extend to contain model, if any
#if ENABLE_RAYCAST_PICKING
BoundingBoxf3 model_bb = m_model.model.get_bounding_box();
#else
BoundingBoxf3 model_bb = m_model.get_bounding_box();
#endif // ENABLE_RAYCAST_PICKING
if (model_bb.defined) {
model_bb.translate(m_model_offset);
out.merge(model_bb);
@ -391,7 +419,16 @@ void Bed3D::init_triangles()
init_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
}
#if ENABLE_RAYCAST_PICKING
if (m_model.model.get_filename().empty() && m_model.mesh_raycaster == nullptr)
// register for picking
register_raycasters_for_picking(init_data, Transform3d::Identity());
#endif // ENABLE_RAYCAST_PICKING
m_triangles.init_from(std::move(init_data));
#if ENABLE_RAYCAST_PICKING
m_triangles.set_color(DEFAULT_MODEL_COLOR);
#endif // ENABLE_RAYCAST_PICKING
}
void Bed3D::init_gridlines()
@ -581,7 +618,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
if (m_texture_filename.empty()) {
m_texture.reset();
#if ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_RAYCAST_PICKING
render_default(bottom, true, view_matrix, projection_matrix);
#else
render_default(bottom, false, true, view_matrix, projection_matrix);
#endif // ENABLE_RAYCAST_PICKING
#else
render_default(bottom, false, true);
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
@ -598,7 +639,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
if (!m_temp_texture.load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8)) {
#if ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_RAYCAST_PICKING
render_default(bottom, true, view_matrix, projection_matrix);
#else
render_default(bottom, false, true, view_matrix, projection_matrix);
#endif // ENABLE_RAYCAST_PICKING
#else
render_default(bottom, false, true);
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
@ -610,7 +655,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
// starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_svg_file(m_texture_filename, true, true, true, max_tex_size)) {
#if ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_RAYCAST_PICKING
render_default(bottom, true, view_matrix, projection_matrix);
#else
render_default(bottom, false, true, view_matrix, projection_matrix);
#endif // ENABLE_RAYCAST_PICKING
#else
render_default(bottom, false, true);
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
@ -622,7 +671,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
if (m_temp_texture.get_id() == 0 || m_temp_texture.get_source() != m_texture_filename) {
if (!m_temp_texture.load_from_file(m_texture_filename, false, GLTexture::None, false)) {
#if ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_RAYCAST_PICKING
render_default(bottom, true, view_matrix, projection_matrix);
#else
render_default(bottom, false, true, view_matrix, projection_matrix);
#endif // ENABLE_RAYCAST_PICKING
#else
render_default(bottom, false, true);
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
@ -634,7 +687,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
// starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_file(m_texture_filename, true, GLTexture::MultiThreaded, true)) {
#if ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_RAYCAST_PICKING
render_default(bottom, true, view_matrix, projection_matrix);
#else
render_default(bottom, false, true, view_matrix, projection_matrix);
#endif // ENABLE_RAYCAST_PICKING
#else
render_default(bottom, false, true);
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
@ -643,7 +700,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
}
else {
#if ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_RAYCAST_PICKING
render_default(bottom, true, view_matrix, projection_matrix);
#else
render_default(bottom, false, true, view_matrix, projection_matrix);
#endif // ENABLE_RAYCAST_PICKING
#else
render_default(bottom, false, true);
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
@ -771,9 +832,17 @@ void Bed3D::render_model()
if (m_model_filename.empty())
return;
#if ENABLE_RAYCAST_PICKING
if (m_model.model.get_filename() != m_model_filename && m_model.model.init_from_file(m_model_filename)) {
#else
if (m_model.get_filename() != m_model_filename && m_model.init_from_file(m_model_filename)) {
#endif // ENABLE_RAYCAST_PICKING
#if ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_RAYCAST_PICKING
m_model.model.set_color(DEFAULT_MODEL_COLOR);
#else
m_model.set_color(DEFAULT_MODEL_COLOR);
#endif // ENABLE_RAYCAST_PICKING
#else
m_model.set_color(-1, DEFAULT_MODEL_COLOR);
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
@ -781,11 +850,20 @@ void Bed3D::render_model()
// move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad
m_model_offset = to_3d(m_build_volume.bounding_volume2d().center(), -0.03);
#if ENABLE_RAYCAST_PICKING
// register for picking
register_raycasters_for_picking(m_model.model.get_geometry(), Geometry::assemble_transform(m_model_offset));
#endif // ENABLE_RAYCAST_PICKING
// update extended bounding box
m_extended_bounding_box = this->calc_extended_bounding_box();
}
#if ENABLE_RAYCAST_PICKING
if (!m_model.model.get_filename().empty()) {
#else
if (!m_model.get_filename().empty()) {
#endif // ENABLE_RAYCAST_PICKING
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader != nullptr) {
shader->start_using();
@ -800,7 +878,11 @@ void Bed3D::render_model()
glsafe(::glPushMatrix());
glsafe(::glTranslated(m_model_offset.x(), m_model_offset.y(), m_model_offset.z()));
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_RAYCAST_PICKING
m_model.model.render();
#else
m_model.render();
#endif // ENABLE_RAYCAST_PICKING
#if !ENABLE_LEGACY_OPENGL_REMOVAL
glsafe(::glPopMatrix());
#endif // !ENABLE_LEGACY_OPENGL_REMOVAL
@ -810,14 +892,22 @@ void Bed3D::render_model()
}
#if ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_RAYCAST_PICKING
void Bed3D::render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture)
#else
void Bed3D::render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture, bool picking)
#endif // ENABLE_RAYCAST_PICKING
#else
void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture, bool picking)
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
{
if (m_texture_filename.empty() && m_model_filename.empty()) {
#if ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_RAYCAST_PICKING
render_default(bottom, show_texture, view_matrix, projection_matrix);
#else
render_default(bottom, picking, show_texture, view_matrix, projection_matrix);
#endif // ENABLE_RAYCAST_PICKING
#else
render_default(bottom, picking, show_texture);
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
@ -844,7 +934,11 @@ void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture, bo
}
#if ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_RAYCAST_PICKING
void Bed3D::render_default(bool bottom, bool show_texture, const Transform3d& view_matrix, const Transform3d& projection_matrix)
#else
void Bed3D::render_default(bool bottom, bool picking, bool show_texture, const Transform3d& view_matrix, const Transform3d& projection_matrix)
#endif // ENABLE_RAYCAST_PICKING
#else
void Bed3D::render_default(bool bottom, bool picking, bool show_texture)
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
@ -866,16 +960,35 @@ void Bed3D::render_default(bool bottom, bool picking, bool show_texture)
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
#if ENABLE_RAYCAST_PICKING
const bool has_model = !m_model.model.get_filename().empty();
#else
const bool has_model = !m_model.get_filename().empty();
#endif // ENABLE_RAYCAST_PICKING
if (!has_model && !bottom) {
// draw background
glsafe(::glDepthMask(GL_FALSE));
#if !ENABLE_RAYCAST_PICKING
m_triangles.set_color(picking ? PICKING_MODEL_COLOR : DEFAULT_MODEL_COLOR);
#endif // !ENABLE_RAYCAST_PICKING
m_triangles.render();
glsafe(::glDepthMask(GL_TRUE));
}
#if ENABLE_RAYCAST_PICKING
if (show_texture) {
// draw grid
#if ENABLE_GL_CORE_PROFILE
if (!OpenGLManager::get_gl_info().is_core_profile())
#endif // ENABLE_GL_CORE_PROFILE
glsafe(::glLineWidth(1.5f * m_scale_factor));
m_gridlines.set_color(has_model && !bottom ? DEFAULT_SOLID_GRID_COLOR : DEFAULT_TRANSPARENT_GRID_COLOR);
m_gridlines.render();
}
else
render_contour(view_matrix, projection_matrix);
#else
if (!picking && show_texture) {
// draw grid
#if ENABLE_GL_CORE_PROFILE
@ -887,6 +1000,7 @@ void Bed3D::render_default(bool bottom, bool picking, bool show_texture)
}
else if (!show_texture)
render_contour(view_matrix, projection_matrix);
#endif // ENABLE_RAYCAST_PICKING
glsafe(::glDisable(GL_BLEND));
@ -979,5 +1093,26 @@ void Bed3D::release_VBOs()
}
#endif // !ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_RAYCAST_PICKING
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);
}
#endif // ENABLE_RAYCAST_PICKING
} // GUI
} // Slic3r

View file

@ -8,6 +8,9 @@
#else
#include "GLModel.hpp"
#endif // ENABLE_WORLD_COORDINATE
#if ENABLE_RAYCAST_PICKING
#include "MeshUtils.hpp"
#endif // ENABLE_RAYCAST_PICKING
#include "libslic3r/BuildVolume.hpp"
#if ENABLE_LEGACY_OPENGL_REMOVAL
@ -108,7 +111,11 @@ private:
GLTexture m_texture;
// temporary texture shown until the main texture has still no levels compressed
GLTexture m_temp_texture;
#if ENABLE_RAYCAST_PICKING
PickingModel m_model;
#else
GLModel m_model;
#endif // ENABLE_RAYCAST_PICKING
Vec3d m_model_offset{ Vec3d::Zero() };
#if !ENABLE_LEGACY_OPENGL_REMOVAL
unsigned int m_vbo_id{ 0 };
@ -153,7 +160,9 @@ public:
#if ENABLE_LEGACY_OPENGL_REMOVAL
void render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes, bool show_texture);
#if !ENABLE_RAYCAST_PICKING
void render_for_picking(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor);
#endif // !ENABLE_RAYCAST_PICKING
#else
void render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes, bool show_texture);
void render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_factor);
@ -173,8 +182,13 @@ private:
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
static std::tuple<Type, std::string, std::string> detect_type(const Pointfs& shape);
#if ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_RAYCAST_PICKING
void render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor,
bool show_axes, bool show_texture);
#else
void render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor,
bool show_axes, bool show_texture, bool picking);
#endif // ENABLE_RAYCAST_PICKING
#else
void render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
bool show_axes, bool show_texture, bool picking);
@ -184,8 +198,13 @@ private:
void render_system(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture);
void render_texture(bool bottom, GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix);
void render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix);
#if ENABLE_RAYCAST_PICKING
void render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture);
void render_default(bool bottom, bool show_texture, const Transform3d& view_matrix, const Transform3d& projection_matrix);
#else
void render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture, bool picking);
void render_default(bool bottom, bool picking, bool show_texture, const Transform3d& view_matrix, const Transform3d& projection_matrix);
#endif // ENABLE_RAYCAST_PICKING
void render_contour(const Transform3d& view_matrix, const Transform3d& projection_matrix);
#else
void render_system(GLCanvas3D& canvas, bool bottom, bool show_texture);
@ -197,6 +216,10 @@ private:
void release_VBOs();
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_RAYCAST_PICKING
void register_raycasters_for_picking(const GLModel::Geometry& geometry, const Transform3d& trafo);
#endif // ENABLE_RAYCAST_PICKING
};
} // GUI

View file

@ -1664,6 +1664,14 @@ void GLCanvas3D::render()
else if (!m_volumes.empty())
// regular picking pass
_picking_pass();
#if ENABLE_RAYCAST_PICKING_DEBUG
else {
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
@ -1718,6 +1726,11 @@ void GLCanvas3D::render()
}
#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
@ -5392,6 +5405,108 @@ void GLCanvas3D::_refresh_if_shown_on_screen()
}
}
#if ENABLE_RAYCAST_PICKING
void GLCanvas3D::_picking_pass()
{
if (!m_picking_enabled || m_mouse.dragging || m_mouse.position == Vec2d(DBL_MAX, DBL_MAX))
return;
m_hover_volume_idxs.clear();
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)) {
m_hover_volume_idxs.emplace_back(hit.raycaster_id);
m_gizmos.set_hover_id(-1);
}
}
}
else
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
}
#else
void GLCanvas3D::_picking_pass()
{
if (m_picking_enabled && !m_mouse.dragging && m_mouse.position != Vec2d(DBL_MAX, DBL_MAX)) {
@ -5466,6 +5581,7 @@ void GLCanvas3D::_picking_pass()
_update_volumes_hover_state();
}
}
#endif // ENABLE_RAYCAST_PICKING
void GLCanvas3D::_rectangular_selection_picking_pass()
{
@ -5485,8 +5601,10 @@ void GLCanvas3D::_rectangular_selection_picking_pass()
_render_volumes_for_picking();
#if ENABLE_LEGACY_OPENGL_REMOVAL
#if !ENABLE_RAYCAST_PICKING
const Camera& camera = wxGetApp().plater()->get_camera();
_render_bed_for_picking(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward());
#endif // !ENABLE_RAYCAST_PICKING
#else
_render_bed_for_picking(!wxGetApp().plater()->get_camera().is_looking_downward());
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
@ -5651,6 +5769,7 @@ void GLCanvas3D::_render_bed(bool bottom, bool show_axes)
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
}
#if !ENABLE_RAYCAST_PICKING
#if ENABLE_LEGACY_OPENGL_REMOVAL
void GLCanvas3D::_render_bed_for_picking(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom)
#else
@ -5668,6 +5787,7 @@ void GLCanvas3D::_render_bed_for_picking(bool bottom)
m_bed.render_for_picking(*this, bottom, scale_factor);
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
}
#endif // !ENABLE_RAYCAST_PICKING
void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
{

View file

@ -16,6 +16,9 @@
#include "libslic3r/GCode/GCodeProcessor.hpp"
#include "GCodeViewer.hpp"
#include "Camera.hpp"
#if ENABLE_RAYCAST_PICKING
#include "SceneRaycaster.hpp"
#endif // ENABLE_RAYCAST_PICKING
#include "libslic3r/Slicing.hpp"
@ -479,6 +482,9 @@ public:
private:
wxGLCanvas* m_canvas;
wxGLContext* m_context;
#if ENABLE_RAYCAST_PICKING
SceneRaycaster m_scene_raycaster;
#endif // ENABLE_RAYCAST_PICKING
Bed3D &m_bed;
#if ENABLE_RETINA_GL
std::unique_ptr<RetinaHelper> m_retina_helper;
@ -656,6 +662,15 @@ public:
bool init();
void post_event(wxEvent &&event);
#if ENABLE_RAYCAST_PICKING
int add_raycaster_for_picking(SceneRaycaster::EType type, int id, const MeshRaycaster& raycaster, const Transform3d& trafo) {
return m_scene_raycaster.add_raycaster(type, id, raycaster, trafo);
}
void remove_all_picking_raycasters(SceneRaycaster::EType type) {
m_scene_raycaster.reset(type);
}
#endif // ENABLE_RAYCAST_PICKING
void set_as_dirty();
void requires_check_outside_state() { m_requires_check_outside_state = true; }
@ -958,7 +973,9 @@ private:
void _render_background();
#if ENABLE_LEGACY_OPENGL_REMOVAL
void _render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes);
#if !ENABLE_RAYCAST_PICKING
void _render_bed_for_picking(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom);
#endif // !ENABLE_RAYCAST_PICKING
#else
void _render_bed(bool bottom, bool show_axes);
void _render_bed_for_picking(bool bottom);

View file

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

View file

@ -350,6 +350,40 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
return out;
}
#if ENABLE_RAYCAST_PICKING
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;
}
#endif // ENABLE_RAYCAST_PICKING
Vec3f MeshRaycaster::get_closest_point(const Vec3f& point, Vec3f* normal) const
{

View file

@ -14,6 +14,9 @@
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
#include <cfloat>
#if ENABLE_RAYCAST_PICKING
#include <memory>
#endif // ENABLE_RAYCAST_PICKING
namespace Slic3r {
@ -57,6 +60,10 @@ 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]); }
#if ENABLE_RAYCAST_PICKING
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()); }
#endif // ENABLE_RAYCAST_PICKING
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; }
@ -123,6 +130,14 @@ private:
// whether certain points are visible or obscured by the mesh etc.
class MeshRaycaster {
public:
#if ENABLE_RAYCAST_PICKING
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))
{
}
#else
// The class references extern TriangleMesh, which must stay alive
// during MeshRaycaster existence.
MeshRaycaster(const TriangleMesh& mesh)
@ -130,6 +145,7 @@ public:
, m_normals(its_face_normals(mesh.its))
{
}
#endif // ENABLE_RAYCAST_PICKING
void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3d& point, Vec3d& direction) const;
@ -155,10 +171,24 @@ public:
const ClippingPlane* clipping_plane = nullptr // clipping plane (if active)
) const;
#if ENABLE_RAYCAST_PICKING
// 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;
#endif // ENABLE_RAYCAST_PICKING
// 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.
@ -167,11 +197,26 @@ public:
Vec3f get_triangle_normal(size_t facet_idx) const;
private:
#if ENABLE_RAYCAST_PICKING
std::shared_ptr<const TriangleMesh> m_mesh;
#endif // ENABLE_RAYCAST_PICKING
sla::IndexedMesh m_emesh;
std::vector<stl_normal> m_normals;
};
#if ENABLE_RAYCAST_PICKING
struct PickingModel
{
GLModel model;
std::unique_ptr<MeshRaycaster> mesh_raycaster;
void reset() {
model.reset();
mesh_raycaster.reset();
}
};
#endif // ENABLE_RAYCAST_PICKING
} // namespace GUI
} // namespace Slic3r

View file

@ -0,0 +1,222 @@
#include "libslic3r/libslic3r.h"
#include "SceneRaycaster.hpp"
#include "Camera.hpp"
#include "GUI_App.hpp"
#if ENABLE_RAYCAST_PICKING
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
}
int 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);
return m_bed.size() - 1;
}
case EType::Volume: {
m_volumes.emplace_back(encode_id(type, id), raycaster, trafo);
return m_volumes.size() - 1;
}
case EType::Gizmo: {
m_gizmos.emplace_back(encode_id(type, id), raycaster, trafo);
return m_gizmos.size() - 1;
}
};
// signal error
return -1;
}
void SceneRaycaster::set_raycaster_active_state(EType type, int 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, int 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;
}
}
}
void SceneRaycaster::remove_raycaster(EType type, int id)
{
std::vector<SceneRaycasterItem>* raycasters = get_raycasters(type);
if (0 <= id && id < raycasters->size())
raycasters->erase(raycasters->begin() + id);
}
void SceneRaycaster::reset(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;
}
};
}
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;
};
m_last_hit.reset();
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;
}
}
}
};
test_raycasters(EType::Gizmo);
if (!m_gizmos_on_top || ret.is_valid()) {
if (camera.is_looking_downward())
test_raycasters(EType::Bed);
test_raycasters(EType::Volume);
}
if (ret.is_valid())
ret.raycaster_id = decode_id(ret.type, ret.raycaster_id);
m_last_hit = ret;
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(6.25);
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
#endif // ENABLE_RAYCAST_PICKING

View file

@ -0,0 +1,121 @@
#ifndef slic3r_SceneRaycaster_hpp_
#define slic3r_SceneRaycaster_hpp_
#if ENABLE_RAYCAST_PICKING
#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();
// returns the internal index of the added raycaster
// can be used with remove_raycaster()
int add_raycaster(EType type, PickingId picking_id, const MeshRaycaster& raycaster, const Transform3d& trafo);
void set_raycaster_active_state(EType type, PickingId picking_id, bool active);
void set_raycaster_transform(EType type, PickingId picking_id, const Transform3d& trafo);
// id: the value returned by add_raycaster()
void remove_raycaster(EType type, int id);
void reset(EType type);
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 // ENABLE_RAYCAST_PICKING
#endif // slic3r_SceneRaycaster_hpp_