Merge remote-tracking branch 'origin/et_show_sla_supports' into master

This commit is contained in:
YuSanka 2023-03-29 13:17:29 +02:00
commit eefae26961
16 changed files with 425 additions and 156 deletions

View File

@ -579,6 +579,71 @@ int GLVolumeCollection::load_wipe_tower_preview(
return int(volumes.size() - 1);
}
// Load SLA auxiliary GLVolumes (for support trees or pad).
// This function produces volumes for multiple instances in a single shot,
// as some object specific mesh conversions may be expensive.
void GLVolumeCollection::load_object_auxiliary(
const SLAPrintObject* print_object,
int obj_idx,
// pairs of <instance_idx, print_instance_idx>
const std::vector<std::pair<size_t, size_t>>& instances,
SLAPrintObjectStep milestone,
// Timestamp of the last change of the milestone
size_t timestamp)
{
if (print_object->get_mesh_to_print() == nullptr)
return;
const Transform3d mesh_trafo_inv = print_object->trafo().inverse();
auto add_volume = [this, &instances, timestamp](int obj_idx, int inst_idx, const ModelInstance& model_instance, SLAPrintObjectStep step,
const TriangleMesh& mesh, const ColorRGBA& color, std::optional<const TriangleMesh> convex_hull = std::nullopt) {
if (mesh.empty())
return;
GLVolume& v = *this->volumes.emplace_back(new GLVolume(color));
#if ENABLE_SMOOTH_NORMALS
v.model.init_from(mesh, true);
#else
v.model.init_from(mesh);
v.model.set_color(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(step), inst_idx);
v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
if (convex_hull.has_value())
v.set_convex_hull(*convex_hull);
v.is_modifier = false;
v.shader_outside_printer_detection_enabled = (step == slaposSupportTree);
v.set_instance_transformation(model_instance.get_transformation());
};
// Get the support mesh.
if (milestone == SLAPrintObjectStep::slaposSupportTree) {
TriangleMesh supports_mesh = print_object->support_mesh();
if (!supports_mesh.empty()) {
supports_mesh.transform(mesh_trafo_inv);
TriangleMesh convex_hull = supports_mesh.convex_hull_3d();
for (const std::pair<size_t, size_t>& instance_idx : instances) {
const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first];
add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposSupportTree, supports_mesh, GLVolume::SLA_SUPPORT_COLOR, convex_hull);
}
}
}
// Get the pad mesh.
if (milestone == SLAPrintObjectStep::slaposPad) {
TriangleMesh pad_mesh = print_object->pad_mesh();
if (!pad_mesh.empty()) {
pad_mesh.transform(mesh_trafo_inv);
TriangleMesh convex_hull = pad_mesh.convex_hull_3d();
for (const std::pair<size_t, size_t>& instance_idx : instances) {
const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first];
add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposPad, pad_mesh, GLVolume::SLA_PAD_COLOR, convex_hull);
}
}
}
}
GLVolume* GLVolumeCollection::new_toolpath_volume(const ColorRGBA& rgba)
{
GLVolume* out = new_nontoolpath_volume(rgba);

View File

@ -430,6 +430,16 @@ public:
float pos_x, float pos_y, float width, float depth, float height, float cone_angle, float rotation_angle, bool size_unknown, float brim_width);
#endif // ENABLE_OPENGL_ES
// Load SLA auxiliary GLVolumes (for support trees or pad).
void load_object_auxiliary(
const SLAPrintObject* print_object,
int obj_idx,
// pairs of <instance_idx, print_instance_idx>
const std::vector<std::pair<size_t, size_t>>& instances,
SLAPrintObjectStep milestone,
// Timestamp of the last change of the milestone
size_t timestamp);
GLVolume* new_toolpath_volume(const ColorRGBA& rgba);
GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba);
// Render the volumes by OpenGL.

View File

@ -1879,6 +1879,15 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
size_t volume_idx;
};
// SLA steps to pull the preview meshes for.
typedef std::array<SLAPrintObjectStep, 3> SLASteps;
SLASteps sla_steps = { slaposDrillHoles, slaposSupportTree, slaposPad };
struct SLASupportState {
std::array<PrintStateBase::StateWithTimeStamp, std::tuple_size<SLASteps>::value> step;
};
// State of the sla_steps for all SLAPrintObjects.
std::vector<SLASupportState> sla_support_state;
std::vector<size_t> instance_ids_selected;
std::vector<size_t> map_glvolume_old_to_new(m_volumes.volumes.size(), size_t(-1));
std::vector<GLVolumeState> deleted_volumes;
@ -1904,6 +1913,37 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
}
}
if (printer_technology == ptSLA) {
const SLAPrint* sla_print = this->sla_print();
#ifndef NDEBUG
// Verify that the SLAPrint object is synchronized with m_model.
check_model_ids_equal(*m_model, sla_print->model());
#endif // NDEBUG
sla_support_state.reserve(sla_print->objects().size());
for (const SLAPrintObject* print_object : sla_print->objects()) {
SLASupportState state;
for (size_t istep = 0; istep < sla_steps.size(); ++istep) {
state.step[istep] = print_object->step_state_with_timestamp(sla_steps[istep]);
if (state.step[istep].state == PrintStateBase::State::Done) {
std::shared_ptr<const indexed_triangle_set> m = print_object->get_mesh_to_print();
if (m == nullptr || m->empty())
// Consider the DONE step without a valid mesh as invalid for the purpose
// of mesh visualization.
state.step[istep].state = PrintStateBase::State::Invalidated;
else {
for (const ModelInstance* model_instance : print_object->model_object()->instances) {
// Only the instances, which are currently printable, will have the SLA support structures kept.
// The instances outside the print bed will have the GLVolumes of their support structures released.
if (model_instance->is_printable())
aux_volume_state.emplace_back(state.step[istep].timestamp, model_instance->id());
}
}
}
}
sla_support_state.emplace_back(state);
}
}
std::sort(model_volume_state.begin(), model_volume_state.end(), model_volume_state_lower);
std::sort(aux_volume_state.begin(), aux_volume_state.end(), model_volume_state_lower);
// Release all ModelVolume based GLVolumes not found in the current Model. Find the GLVolume of a hollowed mesh.
@ -2018,6 +2058,75 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
}
}
if (printer_technology == ptSLA) {
size_t idx = 0;
const SLAPrint *sla_print = this->sla_print();
std::vector<double> shift_zs(m_model->objects.size(), 0);
double relative_correction_z = sla_print->relative_correction().z();
if (relative_correction_z <= EPSILON)
relative_correction_z = 1.;
for (const SLAPrintObject *print_object : sla_print->objects()) {
SLASupportState &state = sla_support_state[idx ++];
const ModelObject *model_object = print_object->model_object();
// Find an index of the ModelObject
int object_idx;
// There may be new SLA volumes added to the scene for this print_object.
// Find the object index of this print_object in the Model::objects list.
auto it = std::find(sla_print->model().objects.begin(), sla_print->model().objects.end(), model_object);
assert(it != sla_print->model().objects.end());
object_idx = it - sla_print->model().objects.begin();
// Cache the Z offset to be applied to all volumes with this object_idx.
shift_zs[object_idx] = print_object->get_current_elevation() / relative_correction_z;
// Collect indices of this print_object's instances, for which the SLA support meshes are to be added to the scene.
// pairs of <instance_idx, print_instance_idx>
std::vector<std::pair<size_t, size_t>> instances[std::tuple_size<SLASteps>::value];
for (size_t print_instance_idx = 0; print_instance_idx < print_object->instances().size(); ++ print_instance_idx) {
const SLAPrintObject::Instance &instance = print_object->instances()[print_instance_idx];
// Find index of ModelInstance corresponding to this SLAPrintObject::Instance.
auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(),
[&instance](const ModelInstance *mi) { return mi->id() == instance.instance_id; });
assert(it != model_object->instances.end());
int instance_idx = it - model_object->instances.begin();
for (size_t istep = 0; istep < sla_steps.size(); ++istep) {
if (state.step[istep].state == PrintStateBase::State::Done) {
// Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created.
ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id);
auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower);
assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id);
if (it->new_geometry()) {
// This can be an SLA support structure that should not be rendered (in case someone used undo
// to revert to before it was generated). If that's the case, we should not generate anything.
if (model_object->sla_points_status != sla::PointsStatus::NoPoints)
instances[istep].emplace_back(std::pair<size_t, size_t>(instance_idx, print_instance_idx));
else
shift_zs[object_idx] = 0.;
}
else {
// Recycling an old GLVolume. Update the Object/Instance indices into the current Model.
m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx);
m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation());
}
}
}
}
for (size_t istep = 0; istep < sla_steps.size(); ++istep) {
if (!instances[istep].empty())
m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp);
}
}
// Shift-up all volumes of the object so that it has the right elevation with respect to the print bed
for (GLVolume* volume : m_volumes.volumes) {
const ModelObject* model_object = (volume->object_idx() < (int)m_model->objects.size()) ? m_model->objects[volume->object_idx()] : nullptr;
if (model_object != nullptr && model_object->instances[volume->instance_idx()]->is_printable()) {
const SLAPrintObject* po = sla_print->get_print_object_by_model_object_id(model_object->id());
if (po != nullptr)
volume->set_sla_shift_z(po->get_current_elevation() / sla_print->relative_correction().z());
}
}
}
if (printer_technology == ptFFF && m_config->has("nozzle_diameter")) {
// Should the wipe tower be visualized ?
unsigned int extruders_count = (unsigned int)dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter"))->values.size();
@ -2054,7 +2163,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
}
update_volumes_colors_by_extruder();
// Update selection indices based on the old/new GLVolumeCollection.
// Update selection indices based on the old/new GLVolumeCollection.
if (m_selection.get_mode() == Selection::Instance)
m_selection.instances_changed(instance_ids_selected);
else
@ -3486,6 +3595,9 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
int instance_idx = v->instance_idx();
int volume_idx = v->volume_idx();
if (volume_idx < 0)
continue;
std::pair<int, int> done_id(object_idx, instance_idx);
if (0 <= object_idx && object_idx < (int)m_model->objects.size()) {
@ -3500,7 +3612,7 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
#else
model_object->instances[instance_idx]->set_offset(v->get_instance_offset());
#endif // ENABLE_WORLD_COORDINATE
else if (volume_idx >= 0 && selection_mode == Selection::Volume)
else if (selection_mode == Selection::Volume)
#if ENABLE_WORLD_COORDINATE
model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation());
#else
@ -3591,6 +3703,9 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
const int instance_idx = v->instance_idx();
const int volume_idx = v->volume_idx();
if (volume_idx < 0)
continue;
done.insert(std::pair<int, int>(object_idx, instance_idx));
// Rotate instances/volumes.
@ -3667,6 +3782,9 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
const int instance_idx = v->instance_idx();
const int volume_idx = v->volume_idx();
if (volume_idx < 0)
continue;
done.insert(std::pair<int, int>(object_idx, instance_idx));
// Rotate instances/volumes
@ -3680,7 +3798,7 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
model_object->instances[instance_idx]->set_offset(v->get_instance_offset());
#endif // ENABLE_WORLD_COORDINATE
}
else if (selection_mode == Selection::Volume && volume_idx >= 0) {
else if (selection_mode == Selection::Volume) {
#if ENABLE_WORLD_COORDINATE
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation());
@ -6795,9 +6913,9 @@ void GLCanvas3D::_load_sla_shells()
// adds objects' volumes
for (const SLAPrintObject* obj : print->objects()) {
unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size();
for (const SLAPrintObject::Instance& instance : obj->instances()) {
std::shared_ptr<const indexed_triangle_set> m = obj->get_mesh_to_print();
if (m && !m->empty()) {
std::shared_ptr<const indexed_triangle_set> m = obj->get_mesh_to_print();
if (m && !m->empty()) {
for (const SLAPrintObject::Instance& instance : obj->instances()) {
add_volume(*obj, 0, instance, *m, GLVolume::MODEL_COLOR[0], true);
// Set the extruder_id and volume_id to achieve the same color as in the 3D scene when
// through the update_volumes_colors_by_extruder() call.
@ -6808,7 +6926,7 @@ void GLCanvas3D::_load_sla_shells()
add_volume(*obj, -int(slaposPad), instance, pad_mesh, GLVolume::SLA_PAD_COLOR, false);
}
}
double shift_z = obj->get_current_elevation();
const double shift_z = obj->get_current_elevation();
for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) {
// apply shift z
m_volumes.volumes[i]->set_sla_shift_z(shift_z);

View File

@ -875,6 +875,7 @@ void GLGizmoCut3D::on_set_state()
}
m_selected.clear();
m_parent.set_use_color_clip_plane(false);
m_c->selection_info()->set_use_shift(false);
}
}
@ -1238,7 +1239,10 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center,
{
const Selection& selection = m_parent.get_selection();
const Vec3d& instance_offset = selection.get_first_volume()->get_instance_offset();
const auto first_volume = selection.get_first_volume();
Vec3d instance_offset = first_volume->get_instance_offset();
instance_offset[Z] += first_volume->get_sla_shift_z();
const auto cut_matrix = Transform3d::Identity() * rotation_m.inverse() * translation_transform(instance_offset - plane_center);
const Selection::IndicesList& idxs = selection.get_volume_idxs();
@ -1356,6 +1360,12 @@ void GLGizmoCut3D::render_clipper_cut()
void GLGizmoCut3D::on_render()
{
if (m_state == On) {
// This gizmo is showing the object elevated. Tell the common
// SelectionInfo object to lie about the actual shift.
m_c->selection_info()->set_use_shift(true);
}
update_clipper();
init_picking_models();
@ -2288,7 +2298,10 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse
const Vec3d new_plane_center = m_bb_center + cross_dir * cross_dir.dot(pt - m_bb_center);
// update transformed bb
const auto new_tbb = transformed_bounding_box(new_plane_center, m);
const Vec3d& instance_offset = m_parent.get_selection().get_first_volume()->get_instance_offset();
const GLVolume* first_volume = m_parent.get_selection().get_first_volume();
Vec3d instance_offset = first_volume->get_instance_offset();
instance_offset[Z] += first_volume->get_sla_shift_z();
const Vec3d trans_center_pos = m.inverse() * (new_plane_center - instance_offset) + new_tbb.center();
if (new_tbb.contains(trans_center_pos)) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by line"), UndoRedo::SnapshotType::GizmoAction);

View File

@ -3410,6 +3410,7 @@ bool priv::start_create_volume_on_surface_job(
{
assert(gl_volume != nullptr);
if (gl_volume == nullptr) return false;
if (gl_volume->volume_idx() < 0) return false;
Plater *plater = wxGetApp().plater();
const ModelObjectPtrs &objects = plater->model().objects;
@ -3460,7 +3461,7 @@ void priv::find_closest_volume(const Selection &selection,
for (unsigned int id : indices) {
const GLVolume *gl_volume = selection.get_volume(id);
const ModelVolume *volume = get_model_volume(*gl_volume, objects);
if (!volume->is_model_part()) continue;
if (volume == nullptr || !volume->is_model_part()) continue;
Slic3r::Polygon hull = CameraUtils::create_hull2d(camera, *gl_volume);
Vec2d c = hull.centroid().cast<double>();
Vec2d d = c - screen_center;

View File

@ -101,6 +101,8 @@ void GLGizmoHollow::on_render()
m_selection_rectangle.render(m_parent);
m_c->object_clipper()->render_cut();
if (are_sla_supports_shown())
m_c->supports_clipper()->render_cut();
glsafe(::glDisable(GL_BLEND));
}
@ -772,6 +774,14 @@ RENDER_AGAIN:
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->object_clipper()->set_position_by_ratio(clp_dist, true);
// make sure supports are shown/hidden as appropriate
ImGui::Separator();
bool show_sups = are_sla_supports_shown();
if (m_imgui->checkbox(m_desc["show_supports"], show_sups)) {
show_sla_supports(show_sups);
force_refresh = true;
}
m_imgui->disabled_end();
m_imgui->end();

View File

@ -30,7 +30,8 @@ CommonGizmosDataID GLGizmoSlaBase::on_get_requirements() const
int(CommonGizmosDataID::SelectionInfo)
| int(CommonGizmosDataID::InstancesHider)
| int(CommonGizmosDataID::Raycaster)
| int(CommonGizmosDataID::ObjectClipper));
| int(CommonGizmosDataID::ObjectClipper)
| int(CommonGizmosDataID::SupportsClipper));
}
void GLGizmoSlaBase::update_volumes()
@ -50,27 +51,54 @@ void GLGizmoSlaBase::update_volumes()
TriangleMesh backend_mesh;
std::shared_ptr<const indexed_triangle_set> preview_mesh_ptr = po->get_mesh_to_print();
if (preview_mesh_ptr)
backend_mesh = TriangleMesh{*preview_mesh_ptr};
if (preview_mesh_ptr != nullptr)
backend_mesh = TriangleMesh(*preview_mesh_ptr);
if (!backend_mesh.empty()) {
// The backend has generated a valid mesh. Use it
backend_mesh.transform(po->trafo().inverse());
m_volumes.volumes.emplace_back(new GLVolume());
GLVolume* new_volume = m_volumes.volumes.back();
new_volume->model.init_from(backend_mesh);
new_volume->set_instance_transformation(po->model_object()->instances[m_parent.get_selection().get_instance_idx()]->get_transformation());
new_volume->set_sla_shift_z(po->get_current_elevation());
new_volume->mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(backend_mesh);
auto last_comp_step = static_cast<int>(po->last_completed_step());
if (last_comp_step == slaposCount)
last_comp_step = -1;
m_input_enabled = last_comp_step >= m_min_sla_print_object_step;
if (m_input_enabled)
new_volume->selected = true; // to set the proper color
else
new_volume->set_color(DISABLED_COLOR);
const int object_idx = m_parent.get_selection().get_object_idx();
const int instance_idx = m_parent.get_selection().get_instance_idx();
const Geometry::Transformation& inst_trafo = po->model_object()->instances[instance_idx]->get_transformation();
const double current_elevation = po->get_current_elevation();
auto add_volume = [this, object_idx, instance_idx, &inst_trafo, current_elevation](const TriangleMesh& mesh, int volume_id, bool add_mesh_raycaster = false) {
GLVolume* volume = m_volumes.volumes.emplace_back(new GLVolume());
volume->model.init_from(mesh);
volume->set_instance_transformation(inst_trafo);
volume->set_sla_shift_z(current_elevation);
if (add_mesh_raycaster)
volume->mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh);
if (m_input_enabled)
volume->selected = true; // to set the proper color
else
volume->set_color(DISABLED_COLOR);
volume->composite_id = GLVolume::CompositeID(object_idx, volume_id, instance_idx);
};
const Transform3d po_trafo_inverse = po->trafo().inverse();
// main mesh
backend_mesh.transform(po_trafo_inverse);
add_volume(backend_mesh, 0, true);
// supports mesh
TriangleMesh supports_mesh = po->support_mesh();
if (!supports_mesh.empty()) {
supports_mesh.transform(po_trafo_inverse);
add_volume(supports_mesh, -int(slaposSupportTree));
}
// pad mesh
TriangleMesh pad_mesh = po->pad_mesh();
if (!pad_mesh.empty()) {
pad_mesh.transform(po_trafo_inverse);
add_volume(pad_mesh, -int(slaposPad));
}
}
if (m_volumes.volumes.empty()) {
@ -110,16 +138,20 @@ void GLGizmoSlaBase::render_volumes()
clipping_plane.set_normal(-clipping_plane.get_normal());
m_volumes.set_clipping_plane(clipping_plane.get_data());
m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix());
shader->stop_using();
for (GLVolume* v : m_volumes.volumes) {
v->is_active = m_show_sla_supports || (!v->is_sla_pad() && !v->is_sla_support());
}
m_volumes.render(GLVolumeCollection::ERenderType::Opaque, true, camera.get_view_matrix(), camera.get_projection_matrix());
shader->stop_using();
}
void GLGizmoSlaBase::register_volume_raycasters_for_picking()
{
for (size_t i = 0; i < m_volumes.volumes.size(); ++i) {
const GLVolume* v = m_volumes.volumes[i];
m_volume_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, VOLUME_RAYCASTERS_BASE_ID + (int)i, *v->mesh_raycaster, v->world_matrix()));
if (!v->is_sla_pad() && !v->is_sla_support())
m_volume_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, VOLUME_RAYCASTERS_BASE_ID + (int)i, *v->mesh_raycaster, v->world_matrix()));
}
}

View File

@ -40,11 +40,15 @@ protected:
bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
bool are_sla_supports_shown() const { return m_show_sla_supports; }
void show_sla_supports(bool show) { m_show_sla_supports = show; }
const GLVolumeCollection &volumes() const { return m_volumes; }
private:
GLVolumeCollection m_volumes;
bool m_input_enabled{ false };
bool m_show_sla_supports{ false };
int m_min_sla_print_object_step{ -1 };
std::vector<std::shared_ptr<SceneRaycasterItem>> m_volume_raycasters;
};

View File

@ -24,8 +24,10 @@ namespace Slic3r {
namespace GUI {
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoSlaBase(parent, icon_filename, sprite_id, slaposDrillHoles)
{}
: GLGizmoSlaBase(parent, icon_filename, sprite_id, slaposDrillHoles)
{
show_sla_supports(true);
}
bool GLGizmoSlaSupports::on_init()
{
@ -126,6 +128,7 @@ void GLGizmoSlaSupports::on_render()
m_selection_rectangle.render(m_parent);
m_c->object_clipper()->render_cut();
m_c->supports_clipper()->render_cut();
glsafe(::glDisable(GL_BLEND));
}

View File

@ -453,83 +453,74 @@ void SupportsClipper::on_update()
if (! mo || ! is_sla)
return;
const GLCanvas3D* canvas = get_pool()->get_canvas();
const PrintObjects& print_objects = canvas->sla_print()->objects();
const SLAPrintObject* print_object = (m_print_object_idx >= 0 && m_print_object_idx < int(print_objects.size()))
? print_objects[m_print_object_idx]
: nullptr;
const SLAPrintObject* po = get_pool()->selection_info()->print_object();
if (po == nullptr)
return;
// Find the respective SLAPrintObject.
if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) {
m_print_objects_count = print_objects.size();
m_print_object_idx = -1;
for (const SLAPrintObject* po : print_objects) {
++m_print_object_idx;
if (po->model_object()->id() == mo->id()) {
print_object = po;
break;
}
}
if (po->get_mesh_to_print() == nullptr) {
// The object has been not sliced yet. We better dump the cached data.
m_supports_clipper.reset();
m_pad_clipper.reset();
return;
}
if (print_object
&& print_object->is_step_done(slaposSupportTree)
&& ! print_object->support_mesh().empty())
{
// If the supports are already calculated, save the timestamp of the respective step
// so we can later tell they were recalculated.
size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp;
if (! m_clipper || timestamp != m_old_timestamp) {
// The timestamp has changed.
m_clipper.reset(new MeshClipper);
// The mesh should already have the shared vertices calculated.
m_clipper->set_mesh(print_object->support_mesh().its);
m_old_timestamp = timestamp;
}
const TriangleMesh& support_mesh = po->support_mesh();
if (support_mesh.empty()) {
// The supports are not available yet. We better dump the cached data.
m_supports_clipper.reset();
}
else {
m_supports_clipper.reset(new MeshClipper);
m_supports_clipper->set_mesh(support_mesh.its);
}
const TriangleMesh& pad_mesh = po->pad_mesh();
if (pad_mesh.empty()) {
// The supports are not available yet. We better dump the cached data.
m_pad_clipper.reset();
}
else {
m_pad_clipper.reset(new MeshClipper);
m_pad_clipper->set_mesh(pad_mesh.its);
}
else
// The supports are not valid. We better dump the cached data.
m_clipper.reset();
}
void SupportsClipper::on_release()
{
m_clipper.reset();
m_old_timestamp = 0;
m_supports_clipper.reset();
m_pad_clipper.reset();
m_print_object_idx = -1;
}
void SupportsClipper::render_cut() const
{
const CommonGizmosDataObjects::ObjectClipper* ocl = get_pool()->object_clipper();
if (ocl->get_position() == 0.
|| ! m_clipper)
if (ocl->get_position() == 0.)
return;
const SLAPrintObject* po = get_pool()->selection_info()->print_object();
if (po == nullptr)
return;
Geometry::Transformation po_trafo(po->trafo());
const SelectionInfo* sel_info = get_pool()->selection_info();
const ModelObject* mo = sel_info->model_object();
const Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation();
//Geometry::Transformation vol_trafo = mo->volumes.front()->get_transformation();
Geometry::Transformation trafo = inst_trafo;// * vol_trafo;
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift()));
Geometry::Transformation inst_trafo = sel_info->model_object()->instances[sel_info->get_active_instance()]->get_transformation();
inst_trafo = Geometry::Transformation(inst_trafo.get_matrix() * po_trafo.get_matrix().inverse());
inst_trafo.set_offset(inst_trafo.get_offset() + Vec3d(0.0, 0.0, sel_info->get_sla_shift()));
if (m_supports_clipper != nullptr) {
m_supports_clipper->set_plane(*ocl->get_clipping_plane());
m_supports_clipper->set_transformation(inst_trafo);
m_supports_clipper->render_cut({ 1.0f, 0.f, 0.37f, 1.0f });
}
// Get transformation of supports
Geometry::Transformation supports_trafo = trafo;
supports_trafo.set_scaling_factor(Vec3d::Ones());
supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), sel_info->get_sla_shift()));
supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2)));
// I don't know why, but following seems to be correct.
supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2),
1,
1.));
m_clipper->set_plane(*ocl->get_clipping_plane());
m_clipper->set_transformation(supports_trafo);
m_clipper->render_cut({ 1.0f, 0.f, 0.37f, 1.0f });
m_clipper->render_contour({ 1.f, 1.f, 1.f, 1.f });
if (m_pad_clipper != nullptr) {
m_pad_clipper->set_plane(*ocl->get_clipping_plane());
m_pad_clipper->set_transformation(inst_trafo);
m_pad_clipper->render_cut({ 0.6f, 0.f, 0.222f, 1.0f });
}
}

View File

@ -293,10 +293,10 @@ protected:
void on_release() override;
private:
size_t m_old_timestamp = 0;
int m_print_object_idx = -1;
int m_print_objects_count = 0;
std::unique_ptr<MeshClipper> m_clipper;
std::unique_ptr<MeshClipper> m_supports_clipper;
std::unique_ptr<MeshClipper> m_pad_clipper;
};
} // namespace CommonGizmosDataObjects

View File

@ -483,10 +483,46 @@ TriangleMesh priv::create_default_mesh()
return triangle_mesh;
}
namespace{
void update_volume_name(const ModelVolume &volume, const ObjectList *obj_list)
{
if (obj_list == nullptr)
return;
const std::vector<ModelObject *>* objects = obj_list->objects();
if (objects == nullptr)
return;
int object_idx = -1;
int volume_idx = -1;
for (size_t oi = 0; oi < objects->size(); ++oi) {
const ModelObject *mo = objects->at(oi);
if (mo == nullptr)
continue;
if (volume.get_object()->id() != mo->id())
continue;
const ModelVolumePtrs& volumes = mo->volumes;
for (size_t vi = 0; vi < volumes.size(); ++vi) {
const ModelVolume *mv = volumes[vi];
if (mv == nullptr)
continue;
if (mv->id() == volume.id()){
object_idx = static_cast<int>(oi);
volume_idx = static_cast<int>(vi);
break;
}
}
if (volume_idx > 0)
break;
}
obj_list->update_name_in_list(object_idx, volume_idx);
}
}
void UpdateJob::update_volume(ModelVolume *volume,
TriangleMesh &&mesh,
const TextConfiguration &text_configuration,
const std::string &volume_name)
std::string_view volume_name)
{
// check inputs
bool is_valid_input =
@ -506,19 +542,12 @@ void UpdateJob::update_volume(ModelVolume *volume,
// discard information about rotation, should not be stored in volume
volume->text_configuration->style.prop.angle.reset();
GUI_App &app = wxGetApp(); // may be move to input
GLCanvas3D *canvas = app.plater()->canvas3D();
const Selection &selection = canvas->get_selection();
const GLVolume *gl_volume = selection.get_volume(*selection.get_volume_idxs().begin());
int object_idx = gl_volume->object_idx();
GUI_App &app = wxGetApp(); // may be move ObjectList and Plater to input?
// update volume name in right panel( volume / object name)
if (volume->name != volume_name) {
volume->name = volume_name;
// update volume name in right panel( volume / object name)
int volume_idx = gl_volume->volume_idx();
ObjectList *obj_list = app.obj_list();
obj_list->update_name_in_list(object_idx, volume_idx);
update_volume_name(*volume, app.obj_list());
}
// When text is object.
@ -528,11 +557,12 @@ void UpdateJob::update_volume(ModelVolume *volume,
volume->get_object()->ensure_on_bed();
// redraw scene
bool refresh_immediately = false;
canvas->reload_scene(refresh_immediately);
Plater *plater = app.plater();
if (plater == nullptr)
return;
// Change buttons "Export G-code" into "Slice now"
canvas->post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
// Update Model and redraw scene
plater->update();
}
void priv::update_volume(TriangleMesh &&mesh, const DataUpdate &data, Transform3d* tr)
@ -646,8 +676,9 @@ void priv::create_volume(
if (manager.get_current_type() != GLGizmosManager::Emboss)
manager.open_gizmo(GLGizmosManager::Emboss);
// redraw scene
canvas->reload_scene(true);
// update model and redraw scene
//canvas->reload_scene(true);
plater->update();
}
ModelVolume *priv::get_volume(ModelObjectPtrs &objects,

View File

@ -145,7 +145,7 @@ public:
static void update_volume(ModelVolume *volume,
TriangleMesh &&mesh,
const TextConfiguration &text_configuration,
const std::string &volume_name);
std::string_view volume_name);
};
struct SurfaceVolumeData

View File

@ -1396,6 +1396,18 @@ void Selection::scale_to_fit_print_volume(const BuildVolume& volume)
// used to keep track whether the undo/redo snapshot has already been taken
bool undoredo_snapshot = false;
if (wxGetApp().plater()->printer_technology() == ptSLA) {
// remove SLA auxiliary volumes from the selection to ensure that the proper bounding box is calculated
std::vector<unsigned int> to_remove;
for (unsigned int i : m_list) {
if ((*m_volumes)[i]->volume_idx() < 0)
to_remove.push_back(i);
}
if (!to_remove.empty())
remove_volumes(m_mode, to_remove);
}
switch (volume.type())
{
case BuildVolume::Type::Rectangle: { undoredo_snapshot = fit_rectangle(volume, !undoredo_snapshot); break; }
@ -3006,7 +3018,7 @@ static void verify_instances_rotation_synchronized(const Model &model, const GLV
continue;
const Transform3d::ConstLinearPart& rotation0 = volumes[idx_volume_first]->get_instance_transformation().get_matrix().linear();
for (int i = idx_volume_first + 1; i < (int)volumes.size(); ++i)
if (volumes[i]->object_idx() == idx_object) {
if (volumes[i]->object_idx() == idx_object && volumes[i]->volume_idx() >= 0) {
const Transform3d::ConstLinearPart& rotation = volumes[i]->get_instance_transformation().get_matrix().linear();
assert(is_rotation_xy_synchronized(rotation, rotation0));
}

View File

@ -9,50 +9,6 @@
#include "libslic3r/Emboss.hpp"
namespace Slic3r::GUI {
/// <summary>
/// Calculate offset from mouse position to center of text
/// </summary>
/// <param name="screen_coor">Position on screen[in Px] e.g. mouse position</param>
/// <param name="volume">Selected volume(text)</param>
/// <param name="camera">Actual position and view direction of camera</param>
/// <returns>Offset in screen coordinate</returns>
static Vec2d calc_screen_offset_to_volume_center(const Vec2d &screen_coor, const ModelVolume &volume, const Camera &camera)
{
const Transform3d &volume_tr = volume.get_matrix();
assert(volume.text_configuration.has_value());
auto calc_offset = [&screen_coor, &volume_tr, &camera, &volume](const Transform3d &instrance_tr) -> Vec2d {
Transform3d to_world = instrance_tr * volume_tr;
// Use fix of .3mf loaded tranformation when exist
if (volume.text_configuration->fix_3mf_tr.has_value())
to_world = to_world * (*volume.text_configuration->fix_3mf_tr);
// zero point of volume in world coordinate system
Vec3d volume_center = to_world.translation();
// screen coordinate of volume center
Vec2i coor = CameraUtils::project(camera, volume_center);
return coor.cast<double>() - screen_coor;
};
auto object = volume.get_object();
assert(!object->instances.empty());
// Speed up for one instance
if (object->instances.size() == 1)
return calc_offset(object->instances.front()->get_matrix());
Vec2d nearest_offset;
double nearest_offset_size = std::numeric_limits<double>::max();
for (const ModelInstance *instance : object->instances) {
Vec2d offset = calc_offset(instance->get_matrix());
double offset_size = offset.norm();
if (nearest_offset_size < offset_size)
continue;
nearest_offset_size = offset_size;
nearest_offset = offset;
}
return nearest_offset;
}
// Calculate scale in world for check in debug
[[maybe_unused]] static std::optional<double> calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir)
@ -109,7 +65,8 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
gl_volumes[hovered_idx_] != gl_volume)
return false;
const ModelObject *object = get_model_object(*gl_volume, canvas.get_model()->objects);
const ModelObjectPtrs &objects = canvas.get_model()->objects;
const ModelObject *object = get_model_object(*gl_volume, objects);
assert(object != nullptr);
if (object == nullptr)
return false;
@ -148,7 +105,26 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
// wxCoord == int --> wx/types.h
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
Vec2d mouse_pos = mouse_coord.cast<double>();
Vec2d mouse_offset = calc_screen_offset_to_volume_center(mouse_pos, *volume, camera);
// world_matrix_fixed() without sla shift
Transform3d to_world = world_matrix_fixed(*gl_volume, objects);
// zero point of volume in world coordinate system
Vec3d volume_center = to_world.translation();
// screen coordinate of volume center
Vec2i coor = CameraUtils::project(camera, volume_center);
Vec2d mouse_offset = coor.cast<double>() - mouse_pos;
Vec2d mouse_offset_without_sla_shift = mouse_offset;
if (double sla_shift = gl_volume->get_sla_shift_z(); !is_approx(sla_shift, 0.)) {
Transform3d to_world_without_sla_move = instance->get_matrix() * volume->get_matrix();
if (volume->text_configuration.has_value() && volume->text_configuration->fix_3mf_tr.has_value())
to_world_without_sla_move = to_world_without_sla_move * (*volume->text_configuration->fix_3mf_tr);
// zero point of volume in world coordinate system
volume_center = to_world_without_sla_move.translation();
// screen coordinate of volume center
coor = CameraUtils::project(camera, volume_center);
mouse_offset_without_sla_shift = coor.cast<double>() - mouse_pos;
}
Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix();
@ -165,7 +141,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
std::optional<float> start_angle;
if (up_limit.has_value())
start_angle = Emboss::calc_up(world_tr, *up_limit);
surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition, start_angle};
surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition, start_angle, true, mouse_offset_without_sla_shift};
// disable moving with object by mouse
canvas.enable_moving(false);
@ -181,7 +157,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
// wxCoord == int --> wx/types.h
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
Vec2d mouse_pos = mouse_coord.cast<double>();
Vec2d offseted_mouse = mouse_pos + surface_drag->mouse_offset;
Vec2d offseted_mouse = mouse_pos + surface_drag->mouse_offset_without_sla_shift;
std::optional<RaycastManager::Hit> hit = ray_from_camera(
raycast_manager, offseted_mouse, camera, &surface_drag->condition);

View File

@ -39,6 +39,9 @@ struct SurfaceDrag
// Flag whether coordinate hit some volume
bool exist_hit = true;
// hold screen coor offset of cursor from object center without SLA shift
Vec2d mouse_offset_without_sla_shift;
};
/// <summary>