mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-08 23:46:24 -06:00
Tech ENABLE_RAYCAST_PICKING - Raytraced picking of volumes
This commit is contained in:
parent
02f83f29c7
commit
3577a259d5
19 changed files with 690 additions and 152 deletions
|
@ -6233,7 +6233,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||||
continue;
|
continue;
|
||||||
volume_count++;
|
volume_count++;
|
||||||
if (m_share_mesh) {
|
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())
|
if (iter != m_shared_meshes.end())
|
||||||
{
|
{
|
||||||
const ModelVolume* shared_volume = iter->second.second;
|
const ModelVolume* shared_volume = iter->second.second;
|
||||||
|
@ -6248,7 +6248,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||||
continue;
|
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)
|
if (m_from_backup_save)
|
||||||
volume_id = (volume_count << 16 | backup_id);
|
volume_id = (volume_count << 16 | backup_id);
|
||||||
|
|
|
@ -320,6 +320,67 @@ Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation,
|
||||||
return transform;
|
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)
|
Vec3d extract_euler_angles(const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>& rotation_matrix)
|
||||||
{
|
{
|
||||||
// reference: http://www.gregslabaugh.net/publications/euler.pdf
|
// 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
|
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);
|
return (this->dont_translate != dont_translate) || (this->dont_rotate != dont_rotate) || (this->dont_scale != dont_scale) || (this->dont_mirror != dont_mirror);
|
||||||
|
|
|
@ -324,7 +324,8 @@ bool arrange(
|
||||||
// 4) rotate Y
|
// 4) rotate Y
|
||||||
// 5) rotate Z
|
// 5) rotate Z
|
||||||
// 6) translate
|
// 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:
|
// Returns the transform obtained by assembling the given transformations in the following order:
|
||||||
// 1) mirror
|
// 1) mirror
|
||||||
|
@ -333,7 +334,45 @@ void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d
|
||||||
// 4) rotate Y
|
// 4) rotate Y
|
||||||
// 5) rotate Z
|
// 5) rotate Z
|
||||||
// 6) translate
|
// 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
|
// Returns the euler angles extracted from the given rotation matrix
|
||||||
// Warning -> The matrix should not contain any scale or shear !!!
|
// Warning -> The matrix should not contain any scale or shear !!!
|
||||||
|
@ -346,16 +385,7 @@ Vec3d extract_euler_angles(const Transform3d& transform);
|
||||||
// get rotation from two vectors.
|
// get rotation from two vectors.
|
||||||
// Default output is axis-angle. If rotation_matrix pointer is provided, also output rotation matrix
|
// Default output is axis-angle. If rotation_matrix pointer is provided, also output rotation matrix
|
||||||
// Euler angles can be obtained by extract_euler_angles()
|
// 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);
|
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
|
class Transformation
|
||||||
{
|
{
|
||||||
|
|
|
@ -883,7 +883,7 @@ public:
|
||||||
|
|
||||||
// The triangular model.
|
// The triangular model.
|
||||||
const TriangleMesh& mesh() const { return *m_mesh.get(); }
|
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(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(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); }
|
void set_mesh(const indexed_triangle_set &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
|
||||||
|
|
|
@ -61,6 +61,8 @@
|
||||||
#define ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT (1 && ENABLE_2_4_0_BETA2)
|
#define ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT (1 && ENABLE_2_4_0_BETA2)
|
||||||
// Enable fit print volume command for circular printbeds
|
// Enable fit print volume command for circular printbeds
|
||||||
#define ENABLE_ENHANCED_PRINT_VOLUME_FIT (1 && ENABLE_2_4_0_BETA2)
|
#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_
|
#endif // _prusaslicer_technologies_h_
|
||||||
|
|
|
@ -99,6 +99,8 @@ set(SLIC3R_GUI_SOURCES
|
||||||
GUI/GLShader.hpp
|
GUI/GLShader.hpp
|
||||||
GUI/GLCanvas3D.hpp
|
GUI/GLCanvas3D.hpp
|
||||||
GUI/GLCanvas3D.cpp
|
GUI/GLCanvas3D.cpp
|
||||||
|
GUI/SceneRaycaster.hpp
|
||||||
|
GUI/SceneRaycaster.cpp
|
||||||
GUI/OpenGLManager.hpp
|
GUI/OpenGLManager.hpp
|
||||||
GUI/OpenGLManager.cpp
|
GUI/OpenGLManager.cpp
|
||||||
GUI/Selection.hpp
|
GUI/Selection.hpp
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
static const float GROUND_Z = -0.04f;
|
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 = { 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 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_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 };
|
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_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()));
|
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.
|
// Let the calee to update the UI.
|
||||||
return true;
|
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);
|
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,
|
void Bed3D::render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor,
|
||||||
bool show_axes)
|
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);
|
m_model.set_color(m_is_dark ? DEFAULT_MODEL_COLOR_DARK : DEFAULT_MODEL_COLOR);
|
||||||
|
|
||||||
update_model_offset();
|
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()) {
|
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) {
|
if (m_model.get_filename().empty() && !bottom) {
|
||||||
// draw background
|
// draw background
|
||||||
glsafe(::glDepthMask(GL_FALSE));
|
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();
|
m_triangles.render();
|
||||||
glsafe(::glDepthMask(GL_TRUE));
|
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
|
} // GUI
|
||||||
} // Slic3r
|
} // Slic3r
|
||||||
|
|
|
@ -147,7 +147,6 @@ public:
|
||||||
Point point_projection(const Point& point) const;
|
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(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);
|
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_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_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);
|
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
|
} // GUI
|
||||||
|
|
|
@ -623,7 +623,7 @@ int GLVolumeCollection::load_object_volume(
|
||||||
const ModelVolume *model_volume = model_object->volumes[volume_idx];
|
const ModelVolume *model_volume = model_object->volumes[volume_idx];
|
||||||
const int extruder_id = model_volume->extruder_id();
|
const int extruder_id = model_volume->extruder_id();
|
||||||
const ModelInstance *instance = model_object->instances[instance_idx];
|
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());
|
this->volumes.emplace_back(new GLVolume());
|
||||||
GLVolume& v = *this->volumes.back();
|
GLVolume& v = *this->volumes.back();
|
||||||
v.set_color(color_from_model_volume(*model_volume));
|
v.set_color(color_from_model_volume(*model_volume));
|
||||||
|
@ -632,7 +632,8 @@ int GLVolumeCollection::load_object_volume(
|
||||||
#if ENABLE_SMOOTH_NORMALS
|
#if ENABLE_SMOOTH_NORMALS
|
||||||
v.model.init_from(mesh, true);
|
v.model.init_from(mesh, true);
|
||||||
#else
|
#else
|
||||||
v.model.init_from(mesh);
|
v.model.init_from(*mesh);
|
||||||
|
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh);
|
||||||
#endif // ENABLE_SMOOTH_NORMALS
|
#endif // ENABLE_SMOOTH_NORMALS
|
||||||
v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx);
|
v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx);
|
||||||
if (model_volume->is_model_part()) {
|
if (model_volume->is_model_part()) {
|
||||||
|
@ -686,8 +687,9 @@ void GLVolumeCollection::load_object_auxiliary(
|
||||||
v.model.init_from(mesh, true);
|
v.model.init_from(mesh, true);
|
||||||
#else
|
#else
|
||||||
v.model.init_from(mesh);
|
v.model.init_from(mesh);
|
||||||
#endif // ENABLE_SMOOTH_NORMALS
|
|
||||||
v.model.set_color((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR);
|
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.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);
|
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.
|
// 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]);
|
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
|
// Orca: make it transparent
|
||||||
for(auto& color : colors)
|
for(auto& color : colors)
|
||||||
color.a(0.66f);
|
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_per_colors[i].init_from(color_part);
|
||||||
}
|
}
|
||||||
v.model.init_from(wipe_tower_shell);
|
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_convex_hull(wipe_tower_shell);
|
||||||
v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
|
v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
|
||||||
v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
|
v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include "GLModel.hpp"
|
#include "GLModel.hpp"
|
||||||
#include "GLShader.hpp"
|
#include "GLShader.hpp"
|
||||||
|
#include "MeshUtils.hpp"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
@ -202,6 +203,8 @@ public:
|
||||||
EHoverState hover;
|
EHoverState hover;
|
||||||
|
|
||||||
GUI::GLModel model;
|
GUI::GLModel model;
|
||||||
|
// raycaster used for picking
|
||||||
|
std::unique_ptr<GUI::MeshRaycaster> mesh_raycaster;
|
||||||
// BBS
|
// BBS
|
||||||
mutable std::vector<GUI::GLModel> mmuseg_models;
|
mutable std::vector<GUI::GLModel> mmuseg_models;
|
||||||
mutable ObjectBase::Timestamp mmuseg_ts;
|
mutable ObjectBase::Timestamp mmuseg_ts;
|
||||||
|
|
|
@ -1820,9 +1820,17 @@ void GLCanvas3D::render(bool only_init)
|
||||||
_rectangular_selection_picking_pass();
|
_rectangular_selection_picking_pass();
|
||||||
//BBS: enable picking when no volumes for partplate logic
|
//BBS: enable picking when no volumes for partplate logic
|
||||||
//else if (!m_volumes.empty())
|
//else if (!m_volumes.empty())
|
||||||
else
|
else {
|
||||||
// regular picking pass
|
// regular picking pass
|
||||||
_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
|
#if ENABLE_RENDER_PICKING_PASS
|
||||||
|
@ -1896,6 +1904,11 @@ void GLCanvas3D::render(bool only_init)
|
||||||
}
|
}
|
||||||
#endif // ENABLE_RENDER_PICKING_PASS
|
#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
|
#if ENABLE_SHOW_CAMERA_TARGET
|
||||||
_render_camera_target();
|
_render_camera_target();
|
||||||
#endif // ENABLE_SHOW_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);
|
volume.model.init_from(mesh, true);
|
||||||
#else
|
#else
|
||||||
volume.model.init_from(mesh);
|
volume.model.init_from(mesh);
|
||||||
|
volume.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<TriangleMesh>(mesh));
|
||||||
#endif // ENABLE_SMOOTH_NORMALS
|
#endif // ENABLE_SMOOTH_NORMALS
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -2523,7 +2537,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||||
#if ENABLE_SMOOTH_NORMALS
|
#if ENABLE_SMOOTH_NORMALS
|
||||||
volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true);
|
volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true);
|
||||||
#else
|
#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
|
#endif // ENABLE_SMOOTH_NORMALS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2688,6 +2704,13 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||||
#endif
|
#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.
|
// and force this canvas to be redrawn.
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
@ -4205,6 +4228,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) {
|
else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) {
|
||||||
|
m_mouse.position = pos.cast<double>();
|
||||||
|
|
||||||
if (evt.LeftUp()) {
|
if (evt.LeftUp()) {
|
||||||
m_selection.stop_dragging();
|
m_selection.stop_dragging();
|
||||||
m_rotation_center(0) = m_rotation_center(1) = m_rotation_center(2) = 0.f;
|
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();
|
deselect_all();
|
||||||
}
|
}
|
||||||
else if (evt.RightUp() && !is_layers_editing_enabled()) {
|
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
|
// 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
|
// the context menu is already shown
|
||||||
render();
|
render();
|
||||||
|
@ -6396,87 +6420,111 @@ void GLCanvas3D::_refresh_if_shown_on_screen()
|
||||||
|
|
||||||
void GLCanvas3D::_picking_pass()
|
void GLCanvas3D::_picking_pass()
|
||||||
{
|
{
|
||||||
std::vector<int>* hover_volume_idxs = const_cast<std::vector<int>*>(&m_hover_volume_idxs);
|
if (!m_picking_enabled || m_mouse.dragging || m_mouse.position == Vec2d(DBL_MAX, DBL_MAX)) {
|
||||||
std::vector<int>* hover_plate_idxs = const_cast<std::vector<int>*>(&m_hover_plate_idxs);
|
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||||
|
imgui.begin(std::string("Hit result"), ImGuiWindowFlags_AlwaysAutoResize);
|
||||||
|
imgui.text("Picking disabled");
|
||||||
|
imgui.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_picking_enabled && !m_mouse.dragging && m_mouse.position != Vec2d(DBL_MAX, DBL_MAX)) {
|
m_hover_volume_idxs.clear();
|
||||||
hover_volume_idxs->clear();
|
m_hover_plate_idxs.clear();
|
||||||
hover_plate_idxs->clear();
|
|
||||||
|
|
||||||
// Render the object for picking.
|
// TODO: Support plate 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)
|
const ClippingPlane clipping_plane = m_gizmos.get_clipping_plane().inverted_normal();
|
||||||
// This flag is often ignored by NVIDIA drivers if rendering into a screen buffer.
|
const SceneRaycaster::HitResult hit = m_scene_raycaster.hit(m_mouse.position, wxGetApp().plater()->get_camera(), &clipping_plane);
|
||||||
glsafe(::glDisable(GL_MULTISAMPLE));
|
if (hit.is_valid()) {
|
||||||
|
switch (hit.type)
|
||||||
glsafe(::glDisable(GL_BLEND));
|
{
|
||||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
case SceneRaycaster::EType::Volume:
|
||||||
|
{
|
||||||
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
if (0 <= hit.raycaster_id && hit.raycaster_id < (int)m_volumes.volumes.size()) {
|
||||||
|
const GLVolume* volume = m_volumes.volumes[hit.raycaster_id];
|
||||||
//BBS: only render plate in view 3D
|
if (volume->is_active && !volume->disabled && (volume->composite_id.volume_id >= 0 || m_render_sla_auxiliaries)) {
|
||||||
if (m_canvas_type == ECanvasType::CanvasView3D) {
|
// do not add the volume id if any gizmo is active and CTRL is pressed
|
||||||
const Camera &camera = wxGetApp().plater()->get_camera();
|
if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL)) {
|
||||||
_render_plates_for_picking(camera.get_view_matrix(), camera.get_projection_matrix());
|
m_hover_volume_idxs.emplace_back(hit.raycaster_id);
|
||||||
}
|
m_gizmos.set_hover_id(-1);
|
||||||
|
}
|
||||||
_render_volumes_for_picking();
|
}
|
||||||
|
|
||||||
//BBS: remove the bed picking logic
|
|
||||||
//_render_bed_for_picking(!wxGetApp().plater()->get_camera().is_looking_downward());
|
|
||||||
|
|
||||||
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()) {
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
else
|
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);
|
||||||
}
|
|
||||||
|
|
||||||
_update_volumes_hover_state();
|
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()
|
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);
|
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)
|
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);
|
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
|
void GLCanvas3D::_render_plane() const
|
||||||
{
|
{
|
||||||
;//TODO render assemble plane
|
;//TODO render assemble plane
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||||
#include "GCodeViewer.hpp"
|
#include "GCodeViewer.hpp"
|
||||||
#include "Camera.hpp"
|
#include "Camera.hpp"
|
||||||
|
#include "SceneRaycaster.hpp"
|
||||||
#include "IMToolbar.hpp"
|
#include "IMToolbar.hpp"
|
||||||
|
|
||||||
#include "libslic3r/Slicing.hpp"
|
#include "libslic3r/Slicing.hpp"
|
||||||
|
@ -501,6 +502,7 @@ private:
|
||||||
bool m_is_dark = false;
|
bool m_is_dark = false;
|
||||||
wxGLCanvas* m_canvas;
|
wxGLCanvas* m_canvas;
|
||||||
wxGLContext* m_context;
|
wxGLContext* m_context;
|
||||||
|
SceneRaycaster m_scene_raycaster;
|
||||||
Bed3D &m_bed;
|
Bed3D &m_bed;
|
||||||
#if ENABLE_RETINA_GL
|
#if ENABLE_RETINA_GL
|
||||||
std::unique_ptr<RetinaHelper> m_retina_helper;
|
std::unique_ptr<RetinaHelper> m_retina_helper;
|
||||||
|
@ -726,6 +728,16 @@ public:
|
||||||
|
|
||||||
bool init();
|
bool init();
|
||||||
void post_event(wxEvent &&event);
|
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 reset_explosion_ratio() { m_explosion_ratio = 1.0; }
|
||||||
void on_change_color_mode(bool is_dark, bool reinit = true);
|
void on_change_color_mode(bool is_dark, bool reinit = true);
|
||||||
|
@ -1116,10 +1128,8 @@ private:
|
||||||
void _rectangular_selection_picking_pass();
|
void _rectangular_selection_picking_pass();
|
||||||
void _render_background();
|
void _render_background();
|
||||||
void _render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes);
|
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
|
//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_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
|
//BBS: add outline drawing logic
|
||||||
void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true);
|
void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true);
|
||||||
//BBS: GUI refactor: add canvas size as parameters
|
//BBS: GUI refactor: add canvas size as parameters
|
||||||
|
|
|
@ -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
|
// 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) {
|
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,
|
if (mesh_raycaster.unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal,
|
||||||
m_c->object_clipper()->get_clipping_plane(), &facet)) {
|
m_c->object_clipper()->get_clipping_plane(), &facet)) {
|
||||||
// Is this hit the closest to the camera so far?
|
// Is this hit the closest to the camera so far?
|
||||||
|
|
|
@ -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
|
// 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) {
|
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,
|
if (mesh_raycaster.unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal,
|
||||||
m_c->object_clipper()->get_clipping_plane(), &facet)) {
|
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)
|
if (mesh_id == m_volume_idx)
|
||||||
continue;
|
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(),
|
if (mesh_raycaster.unproject_on_mesh(mouse_pos, trafo_matrices[mesh_id], camera, hit, normal, m_c->object_clipper()->get_clipping_plane(),
|
||||||
&facet)) {
|
&facet)) {
|
||||||
|
|
|
@ -331,7 +331,7 @@ void Raycaster::on_update()
|
||||||
if (meshes != m_old_meshes) {
|
if (meshes != m_old_meshes) {
|
||||||
m_raycasters.clear();
|
m_raycasters.clear();
|
||||||
for (const TriangleMesh* mesh : meshes)
|
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;
|
m_old_meshes = meshes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -357,6 +357,38 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
|
||||||
return out;
|
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
|
Vec3f MeshRaycaster::get_closest_point(const Vec3f& point, Vec3f* normal) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "slic3r/GUI/GLModel.hpp"
|
#include "slic3r/GUI/GLModel.hpp"
|
||||||
|
|
||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -52,6 +53,8 @@ public:
|
||||||
void set_offset(double offset) { m_data[3] = offset; }
|
void set_offset(double offset) { m_data[3] = offset; }
|
||||||
double get_offset() const { return m_data[3]; }
|
double get_offset() const { return m_data[3]; }
|
||||||
Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); }
|
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; }
|
bool is_active() const { return m_data[3] != DBL_MAX; }
|
||||||
static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); }
|
static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); }
|
||||||
const std::array<double, 4>& get_data() const { return m_data; }
|
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.
|
// whether certain points are visible or obscured by the mesh etc.
|
||||||
class MeshRaycaster {
|
class MeshRaycaster {
|
||||||
public:
|
public:
|
||||||
// The class references extern TriangleMesh, which must stay alive
|
explicit MeshRaycaster(std::shared_ptr<const TriangleMesh> mesh)
|
||||||
// during MeshRaycaster existence.
|
: m_mesh(mesh)
|
||||||
MeshRaycaster(const TriangleMesh& mesh)
|
, m_emesh(*mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length
|
||||||
: m_emesh(mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length
|
, m_normals(its_face_normals(mesh->its))
|
||||||
, m_normals(its_face_normals(mesh.its))
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,10 +163,22 @@ public:
|
||||||
const ClippingPlane* clipping_plane = nullptr // clipping plane (if active)
|
const ClippingPlane* clipping_plane = nullptr // clipping plane (if active)
|
||||||
) const;
|
) 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.
|
// Given a point in world coords, the method returns closest point on the mesh.
|
||||||
// The output is in mesh coords.
|
// The output is in mesh coords.
|
||||||
// normal* can be used to also get normal of the respective triangle.
|
// normal* can be used to also get normal of the respective triangle.
|
||||||
|
|
||||||
Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const;
|
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.
|
// Given a point in mesh coords, the method returns the closest facet from mesh.
|
||||||
|
@ -173,11 +187,22 @@ public:
|
||||||
Vec3f get_triangle_normal(size_t facet_idx) const;
|
Vec3f get_triangle_normal(size_t facet_idx) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::shared_ptr<const TriangleMesh> m_mesh;
|
||||||
sla::IndexedMesh m_emesh;
|
sla::IndexedMesh m_emesh;
|
||||||
std::vector<stl_normal> m_normals;
|
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 GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
|
220
src/slic3r/GUI/SceneRaycaster.cpp
Normal file
220
src/slic3r/GUI/SceneRaycaster.cpp
Normal 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
|
114
src/slic3r/GUI/SceneRaycaster.hpp
Normal file
114
src/slic3r/GUI/SceneRaycaster.hpp
Normal 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_
|
Loading…
Add table
Add a link
Reference in a new issue