mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Merge remote-tracking branch 'remotes/origin/dev' into vk-materials
This commit is contained in:
		
						commit
						80c5eaacdf
					
				
					 14 changed files with 334 additions and 181 deletions
				
			
		|  | @ -151,8 +151,8 @@ bool stl_write_binary(stl_file *stl, const char *file, const char *label) | |||
| 	memcpy(buffer, &stl->stats.number_of_facets, 4); | ||||
| 	stl_internal_reverse_quads(buffer, 4); | ||||
| 	fwrite(buffer, 4, 1, fp); | ||||
| 	for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { | ||||
| 		memcpy(buffer, stl->facet_start + i, 50); | ||||
| 	for (const stl_facet &facet : stl->facet_start) { | ||||
| 		memcpy(buffer, &facet, 50); | ||||
| 		// Convert to little endian.
 | ||||
| 		stl_internal_reverse_quads(buffer, 48); | ||||
| 		fwrite(buffer, SIZEOF_STL_FACET, 1, fp); | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionPaths &paths | |||
|     this->append(paths); | ||||
| } | ||||
| 
 | ||||
| ExtrusionEntityCollection& ExtrusionEntityCollection::operator= (const ExtrusionEntityCollection &other) | ||||
| ExtrusionEntityCollection& ExtrusionEntityCollection::operator=(const ExtrusionEntityCollection &other) | ||||
| { | ||||
|     this->entities      = other.entities; | ||||
|     for (size_t i = 0; i < this->entities.size(); ++i) | ||||
|  | @ -175,20 +175,20 @@ size_t ExtrusionEntityCollection::items_count() const | |||
| } | ||||
| 
 | ||||
| // Returns a single vector of pointers to all non-collection items contained in this one.
 | ||||
| void ExtrusionEntityCollection::flatten(ExtrusionEntityCollection* retval) const | ||||
| { | ||||
|     for (const ExtrusionEntity *entity : this->entities) | ||||
|         if (entity->is_collection()) | ||||
|             retval->append(static_cast<const ExtrusionEntityCollection*>(entity)->flatten().entities); | ||||
|         else | ||||
|             retval->append(*entity); | ||||
| } | ||||
| 
 | ||||
| ExtrusionEntityCollection ExtrusionEntityCollection::flatten() const | ||||
| { | ||||
|     ExtrusionEntityCollection coll; | ||||
|     this->flatten(&coll); | ||||
|     return coll; | ||||
| 	struct Flatten { | ||||
| 		ExtrusionEntityCollection out; | ||||
| 		void recursive_do(const ExtrusionEntityCollection &collection) { | ||||
| 			for (const ExtrusionEntity* entity : collection.entities) | ||||
| 				if (entity->is_collection()) | ||||
| 					this->recursive_do(*static_cast<const ExtrusionEntityCollection*>(entity)); | ||||
| 				else | ||||
| 					out.append(*entity); | ||||
| 		} | ||||
| 	} flatten; | ||||
| 	flatten.recursive_do(*this); | ||||
|     return flatten.out; | ||||
| } | ||||
| 
 | ||||
| double ExtrusionEntityCollection::min_mm3_per_mm() const | ||||
|  |  | |||
|  | @ -85,7 +85,6 @@ public: | |||
|     Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const | ||||
|         { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; } | ||||
|     size_t items_count() const; | ||||
|     void flatten(ExtrusionEntityCollection* retval) const; | ||||
|     ExtrusionEntityCollection flatten() const; | ||||
|     double min_mm3_per_mm() const; | ||||
|     double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; } | ||||
|  |  | |||
|  | @ -227,6 +227,42 @@ void ObjectLayers::msw_rescale() | |||
| { | ||||
|     m_bmp_delete.msw_rescale(); | ||||
|     m_bmp_add.msw_rescale(); | ||||
| 
 | ||||
|     m_grid_sizer->SetHGap(wxGetApp().em_unit()); | ||||
| 
 | ||||
|     // rescale edit-boxes
 | ||||
|     const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount(); | ||||
|     for (int i = 0; i < cells_cnt; i++) | ||||
|     { | ||||
|         const wxSizerItem* item = m_grid_sizer->GetItem(i); | ||||
|         if (item->IsWindow()) | ||||
|         { | ||||
|             LayerRangeEditor* editor = dynamic_cast<LayerRangeEditor*>(item->GetWindow()); | ||||
|             if (editor != nullptr) | ||||
|                 editor->msw_rescale(); | ||||
|         } | ||||
|         else if (item->IsSizer()) // case when we have editor with buttons
 | ||||
|         { | ||||
|             wxSizerItem* e_item = item->GetSizer()->GetItem(size_t(0)); // editor
 | ||||
|             if (e_item->IsWindow()) { | ||||
|                 LayerRangeEditor* editor = dynamic_cast<LayerRangeEditor*>(e_item->GetWindow()); | ||||
|                 if (editor != nullptr) | ||||
|                     editor->msw_rescale(); | ||||
|             } | ||||
| 
 | ||||
|             const std::vector<size_t> btns = {2, 3};  // del_btn, add_btn
 | ||||
|             for (auto btn : btns) | ||||
|             { | ||||
|                 wxSizerItem* b_item = item->GetSizer()->GetItem(btn); | ||||
|                 if (b_item->IsWindow()) { | ||||
|                     ScalableButton* button = dynamic_cast<ScalableButton*>(b_item->GetWindow()); | ||||
|                     if (button != nullptr) | ||||
|                         button->msw_rescale(); | ||||
|                 }                 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     m_grid_sizer->Layout(); | ||||
| } | ||||
| 
 | ||||
| void ObjectLayers::reset_selection() | ||||
|  | @ -342,5 +378,10 @@ coordf_t LayerRangeEditor::get_value() | |||
|     return layer_height; | ||||
| } | ||||
| 
 | ||||
| void LayerRangeEditor::msw_rescale() | ||||
| { | ||||
|     SetMinSize(wxSize(8 * wxGetApp().em_unit(), wxDefaultCoord)); | ||||
| } | ||||
| 
 | ||||
| } //namespace GUI
 | ||||
| } //namespace Slic3r 
 | ||||
|  | @ -49,6 +49,7 @@ public: | |||
| 
 | ||||
|     EditorType          type() const {return m_type;} | ||||
|     void                set_focus_data() const { m_set_focus_data(m_type);} | ||||
|     void                msw_rescale(); | ||||
| 
 | ||||
| private: | ||||
|     coordf_t            get_value(); | ||||
|  |  | |||
|  | @ -1667,6 +1667,10 @@ void ObjectList::load_subobject(ModelVolumeType type) | |||
|          | ||||
|     if (sel_item) | ||||
|         select_item(sel_item); | ||||
| 
 | ||||
| #ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
 | ||||
|     selection_changed(); | ||||
| #endif //no __WXOSX__ //__WXMSW__
 | ||||
| } | ||||
| 
 | ||||
| void ObjectList::load_part( ModelObject* model_object, | ||||
|  |  | |||
|  | @ -104,8 +104,6 @@ void msw_rescale_word_local_combo(wxBitmapComboBox* combo) | |||
|      | ||||
|     combo->Append(_(L("World coordinates"))); | ||||
|     combo->Append(_(L("Local coordinates"))); | ||||
| //     combo->SetSelection(0);
 | ||||
| //     combo->SetValue(combo->GetString(0));
 | ||||
| 
 | ||||
|     wxBitmap empty_bmp(1, combo->GetFont().GetPixelSize().y + 2); | ||||
|     empty_bmp.SetWidth(0); | ||||
|  | @ -141,20 +139,10 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : | |||
| 
 | ||||
|     ConfigOptionDef def; | ||||
| 
 | ||||
|     // Objects(sub-objects) name
 | ||||
| //     def.label = L("Name");
 | ||||
| //     def.gui_type = "legend";
 | ||||
| //     def.tooltip = L("Object name");
 | ||||
| //     def.width = 21 * wxGetApp().em_unit();
 | ||||
| //     def.default_value = new ConfigOptionString{ " " };
 | ||||
| //     m_og->append_single_option_line(Option(def, "object_name"));
 | ||||
| 
 | ||||
|     Line line = Line{ "Name", "Object name" }; | ||||
| 
 | ||||
|     auto manifold_warning_icon = [this](wxWindow* parent) { | ||||
|         m_fix_throught_netfab_bitmap = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap); | ||||
| //         auto sizer = new wxBoxSizer(wxHORIZONTAL);
 | ||||
| //         sizer->Add(m_fix_throught_netfab_bitmap);
 | ||||
| 
 | ||||
|         if (is_windows10()) | ||||
|             m_fix_throught_netfab_bitmap->Bind(wxEVT_CONTEXT_MENU, [this](wxCommandEvent &e) | ||||
|  | @ -167,11 +155,9 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : | |||
|                 update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors_list()); | ||||
|             }); | ||||
| 
 | ||||
| //         return sizer;
 | ||||
|         return m_fix_throught_netfab_bitmap; | ||||
|     }; | ||||
| 
 | ||||
|  //   line.append_widget(manifold_warning_icon);
 | ||||
|     line.near_label_widget = manifold_warning_icon; | ||||
|     def.label = ""; | ||||
|     def.gui_type = "legend"; | ||||
|  |  | |||
|  | @ -296,8 +296,9 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) | |||
|         // Matrices set, we can render the point mark now.
 | ||||
|         // If in editing mode, we'll also render a cone pointing to the sphere.
 | ||||
|         if (m_editing_mode) { | ||||
|             // in case the normal is not yet cached, find and cache it
 | ||||
|             if (m_editing_cache[i].normal == Vec3f::Zero()) | ||||
|                 update_cache_entry_normal(i); // in case the normal is not yet cached, find and cache it
 | ||||
|                 m_mesh_raycaster->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); | ||||
| 
 | ||||
|             Eigen::Quaterniond q; | ||||
|             q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast<double>()); | ||||
|  | @ -366,13 +367,8 @@ void GLGizmoSlaSupports::update_mesh() | |||
|     m_its = &m_mesh->its; | ||||
| 
 | ||||
|     // If this is different mesh than last time or if the AABB tree is uninitialized, recalculate it.
 | ||||
|     if (m_model_object_id != m_model_object->id() || (m_AABB.m_left == NULL && m_AABB.m_right == NULL)) | ||||
|     { | ||||
|         m_AABB.deinit(); | ||||
|         m_AABB.init( | ||||
|             MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3), | ||||
|             MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3)); | ||||
|     } | ||||
|     if (m_model_object_id != m_model_object->id() || ! m_mesh_raycaster) | ||||
|         m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh)); | ||||
| 
 | ||||
|     m_model_object_id = m_model_object->id(); | ||||
|     disable_editing_mode(); | ||||
|  | @ -385,55 +381,26 @@ void GLGizmoSlaSupports::update_mesh() | |||
| bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal) | ||||
| { | ||||
|     // if the gizmo doesn't have the V, F structures for igl, calculate them first:
 | ||||
|     if (m_its == nullptr) | ||||
|     if (! m_mesh_raycaster) | ||||
|         update_mesh(); | ||||
| 
 | ||||
|     const Camera& camera = m_parent.get_camera(); | ||||
|     const std::array<int, 4>& viewport = camera.get_viewport(); | ||||
|     const Transform3d& modelview_matrix = camera.get_view_matrix(); | ||||
|     const Transform3d& projection_matrix = camera.get_projection_matrix(); | ||||
| 
 | ||||
|     Vec3d point1; | ||||
|     Vec3d point2; | ||||
|     ::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 0.f, modelview_matrix.data(), projection_matrix.data(), viewport.data(), &point1(0), &point1(1), &point1(2)); | ||||
|     ::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 1.f, modelview_matrix.data(), projection_matrix.data(), viewport.data(), &point2(0), &point2(1), &point2(2)); | ||||
| 
 | ||||
|     std::vector<igl::Hit> hits; | ||||
| 
 | ||||
|     const Selection& selection = m_parent.get_selection(); | ||||
|     const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); | ||||
|     Geometry::Transformation trafo = volume->get_instance_transformation(); | ||||
|     trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); | ||||
| 
 | ||||
|     point1(2) -= m_z_shift; | ||||
| 	point2(2) -= m_z_shift; | ||||
|     // The raycaster query
 | ||||
|     std::vector<Vec3f> hits; | ||||
|     std::vector<Vec3f> normals; | ||||
|     m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, &hits, &normals); | ||||
| 
 | ||||
|     Transform3d inv = volume->get_instance_transformation().get_matrix().inverse(); | ||||
| 
 | ||||
|     point1 = inv * point1; | ||||
|     point2 = inv * point2; | ||||
| 
 | ||||
|     if (!m_AABB.intersect_ray( | ||||
|         MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3), | ||||
|         MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3), | ||||
|         point1.cast<float>(), (point2-point1).cast<float>(), hits)) | ||||
|         return false; // no intersection found
 | ||||
| 
 | ||||
|     std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; }); | ||||
| 
 | ||||
|     // Now let's iterate through the points and find the first that is not clipped:
 | ||||
|     unsigned int i=0; | ||||
|     Vec3f bc; | ||||
|     Vec3f a; | ||||
|     Vec3f b; | ||||
|     Vec3f result; | ||||
|     for (i=0; i<hits.size(); ++i) { | ||||
|         igl::Hit& hit = hits[i]; | ||||
|         int fid = hit.id;   // facet id
 | ||||
|         bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
 | ||||
|         a = (m_its->vertices[m_its->indices[fid](1)] - m_its->vertices[m_its->indices[fid](0)]); | ||||
|         b = (m_its->vertices[m_its->indices[fid](2)] - m_its->vertices[m_its->indices[fid](0)]); | ||||
|         result = bc(0) * m_its->vertices[m_its->indices[fid](0)] + bc(1) * m_its->vertices[m_its->indices[fid](1)] + bc(2)*m_its->vertices[m_its->indices[fid](2)]; | ||||
|         if (m_clipping_plane_distance == 0.f || !is_point_clipped(result.cast<double>())) | ||||
|             break; | ||||
|     // We must also take care of the clipping plane (if active)
 | ||||
|     unsigned i = 0; | ||||
|     if (m_clipping_plane_distance != 0.f) { | ||||
|         for (i=0; i<hits.size(); ++i) | ||||
|             if (! is_point_clipped(hits[i].cast<double>())) | ||||
|                 break; | ||||
|     } | ||||
| 
 | ||||
|     if (i==hits.size() || (hits.size()-i) % 2 != 0) { | ||||
|  | @ -443,7 +410,7 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec | |||
|     } | ||||
| 
 | ||||
|     // Calculate and return both the point and the facet normal.
 | ||||
|     pos_and_normal = std::make_pair(result, a.cross(b)); | ||||
|     pos_and_normal = std::make_pair(hits[i], normals[i]); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
|  | @ -504,76 +471,28 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous | |||
|             GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); | ||||
| 
 | ||||
|             // First collect positions of all the points in world coordinates.
 | ||||
|             const Transform3d& instance_matrix = m_model_object->instances[m_active_instance]->get_transformation().get_matrix(); | ||||
|             Geometry::Transformation trafo = m_model_object->instances[m_active_instance]->get_transformation(); | ||||
|             trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); | ||||
|             std::vector<Vec3d> points; | ||||
|             for (unsigned int i=0; i<m_editing_cache.size(); ++i) { | ||||
|                 const sla::SupportPoint &support_point = m_editing_cache[i].support_point; | ||||
|                 points.push_back(instance_matrix * support_point.pos.cast<double>()); | ||||
|                 points.back()(2) += m_z_shift; | ||||
|             } | ||||
|             for (unsigned int i=0; i<m_editing_cache.size(); ++i) | ||||
|                 points.push_back(trafo.get_matrix() * m_editing_cache[i].support_point.pos.cast<double>()); | ||||
| 
 | ||||
|             // Now ask the rectangle which of the points are inside.
 | ||||
|             const Camera& camera = m_parent.get_camera(); | ||||
|             std::vector<unsigned int> selected_idxs = m_selection_rectangle.stop_dragging(m_parent, points); | ||||
|             std::vector<Vec3f> points_inside; | ||||
|             std::vector<unsigned int> points_idxs = m_selection_rectangle.stop_dragging(m_parent, points); | ||||
|             for (size_t idx : points_idxs) | ||||
|                 points_inside.push_back((trafo.get_matrix() * points[idx]).cast<float>()); | ||||
| 
 | ||||
|             // we'll recover current look direction (in world coords) and transform it to model coords.
 | ||||
|             const Selection& selection = m_parent.get_selection(); | ||||
|             const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); | ||||
|             const Transform3d& instance_matrix_no_translation_no_scaling = volume->get_instance_transformation().get_matrix(true,false,true); | ||||
|             Vec3f direction_to_camera = -camera.get_dir_forward().cast<float>(); | ||||
|             Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast<float>() * direction_to_camera).normalized().eval(); | ||||
|             Vec3f scaling = volume->get_instance_scaling_factor().cast<float>(); | ||||
|             direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2)); | ||||
| 
 | ||||
|             // Iterate over all points in the rectangle and check that they are neither clipped by the
 | ||||
|             // clipping plane nor obscured by the mesh.
 | ||||
|             for (const unsigned int i : selected_idxs) { | ||||
|                 const sla::SupportPoint &support_point = m_editing_cache[i].support_point; | ||||
|                 if (!is_point_clipped(support_point.pos.cast<double>())) { | ||||
|                     bool is_obscured = false; | ||||
|                     // Cast a ray in the direction of the camera and look for intersection with the mesh:
 | ||||
|                     std::vector<igl::Hit> hits; | ||||
|                     // Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies.
 | ||||
|                     if (m_AABB.intersect_ray( | ||||
|                             MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3), | ||||
|                             MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3), | ||||
|                             support_point.pos + direction_to_camera_mesh * (support_point.head_front_radius + EPSILON), direction_to_camera_mesh, hits)) { | ||||
|                         std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; }); | ||||
| 
 | ||||
|                         if (m_clipping_plane_distance != 0.f) { | ||||
|                             // If the closest hit facet normal points in the same direction as the ray,
 | ||||
|                             // we are looking through the mesh and should therefore discard the point:
 | ||||
|                             int fid = hits.front().id;   // facet id
 | ||||
|                             Vec3f a = (m_its->vertices[m_its->indices[fid](1)] - m_its->vertices[m_its->indices[fid](0)]); | ||||
|                             Vec3f b = (m_its->vertices[m_its->indices[fid](2)] - m_its->vertices[m_its->indices[fid](0)]); | ||||
|                             if ((a.cross(b)).dot(direction_to_camera_mesh) > 0.f) | ||||
|                                 is_obscured = true; | ||||
| 
 | ||||
|                             // Eradicate all hits that are on clipped surfaces:
 | ||||
|                             for (unsigned int j=0; j<hits.size(); ++j) { | ||||
|                                 const igl::Hit& hit = hits[j]; | ||||
|                                 int fid = hit.id;   // facet id
 | ||||
| 
 | ||||
|                                 Vec3f bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
 | ||||
|                                 Vec3f hit_pos = bc(0) * m_its->vertices[m_its->indices[fid](0)] + bc(1) * m_its->vertices[m_its->indices[fid](1)] + bc(2)*m_its->vertices[m_its->indices[fid](2)]; | ||||
|                                 if (is_point_clipped(hit_pos.cast<double>())) { | ||||
|                                     hits.erase(hits.begin()+j); | ||||
|                                     --j; | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
| 
 | ||||
|                         // FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction.
 | ||||
|                         // Also, the threshold is in mesh coordinates, not in actual dimensions.
 | ||||
|                         if (!hits.empty()) | ||||
|                             is_obscured = true; | ||||
|                     } | ||||
| 
 | ||||
|                     if (!is_obscured) { | ||||
|                         if (rectangle_status == GLSelectionRectangle::Deselect) | ||||
|                             unselect_point(i); | ||||
|                         else | ||||
|                             select_point(i); | ||||
|                     } | ||||
|             // Only select/deselect points that are actually visible
 | ||||
|             for (size_t idx :  m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, | ||||
|                           [this](const Vec3f& pt) { return is_point_clipped(pt.cast<double>()); })) | ||||
|             { | ||||
|                 const sla::SupportPoint &support_point = m_editing_cache[points_idxs[idx]].support_point; | ||||
|                 if (! is_point_clipped(support_point.pos.cast<double>())) { | ||||
|                     if (rectangle_status == GLSelectionRectangle::Deselect) | ||||
|                         unselect_point(points_idxs[idx]); | ||||
|                     else | ||||
|                         select_point(points_idxs[idx]); | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|  | @ -731,23 +650,6 @@ std::vector<const ConfigOption*> GLGizmoSlaSupports::get_config_options(const st | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| void GLGizmoSlaSupports::update_cache_entry_normal(size_t i) const | ||||
| { | ||||
|     int idx = 0; | ||||
|     Eigen::Matrix<float, 1, 3> pp = m_editing_cache[i].support_point.pos; | ||||
|     Eigen::Matrix<float, 1, 3> cc; | ||||
|     m_AABB.squared_distance( | ||||
|         MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3), | ||||
|         MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3), | ||||
|         pp, idx, cc); | ||||
|     Vec3f a = (m_its->vertices[m_its->indices[idx](1)] - m_its->vertices[m_its->indices[idx](0)]); | ||||
|     Vec3f b = (m_its->vertices[m_its->indices[idx](2)] - m_its->vertices[m_its->indices[idx](0)]); | ||||
|     m_editing_cache[i].normal = a.cross(b); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const | ||||
| { | ||||
|     if (!m_model_object || m_state == Off || m_clipping_plane_distance == 0.f) | ||||
|  | @ -1100,11 +1002,11 @@ void GLGizmoSlaSupports::on_set_state() | |||
|             m_parent.toggle_model_objects_visibility(true); | ||||
|             m_normal_cache.clear(); | ||||
|             m_clipping_plane_distance = 0.f; | ||||
|             // Release triangle mesh slicer and the AABB spatial search structure.
 | ||||
|             m_AABB.deinit(); | ||||
|             // Release clippers and the AABB raycaster.
 | ||||
|             m_its = nullptr; | ||||
|             m_object_clipper.reset(); | ||||
|             m_supports_clipper.reset(); | ||||
|             m_mesh_raycaster.reset(); | ||||
|         } | ||||
|     } | ||||
|     m_old_state = m_state; | ||||
|  |  | |||
|  | @ -4,11 +4,6 @@ | |||
| #include "GLGizmoBase.hpp" | ||||
| #include "slic3r/GUI/GLSelectionRectangle.hpp" | ||||
| 
 | ||||
| // There is an L function in igl that would be overridden by our localization macro - let's undefine it...
 | ||||
| #undef L | ||||
| #include <igl/AABB.h> | ||||
| #include "slic3r/GUI/I18N.hpp"  // ...and redefine again when we are done with the igl code
 | ||||
| 
 | ||||
| #include "libslic3r/SLA/SLACommon.hpp" | ||||
| #include <wx/dialog.h> | ||||
| 
 | ||||
|  | @ -20,6 +15,7 @@ namespace GUI { | |||
| 
 | ||||
| class ClippingPlane; | ||||
| class MeshClipper; | ||||
| class MeshRaycaster; | ||||
| enum class SLAGizmoEventType : unsigned char; | ||||
| 
 | ||||
| class GLGizmoSlaSupports : public GLGizmoBase | ||||
|  | @ -37,7 +33,8 @@ private: | |||
|     GLUquadricObj* m_quadric; | ||||
|     typedef Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXfUnaligned; | ||||
|     typedef Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXiUnaligned; | ||||
|     igl::AABB<MapMatrixXfUnaligned, 3> m_AABB; | ||||
| 
 | ||||
|     std::unique_ptr<MeshRaycaster> m_mesh_raycaster; | ||||
|     const TriangleMesh* m_mesh; | ||||
|     const indexed_triangle_set* m_its; | ||||
|     mutable const TriangleMesh* m_supports_mesh; | ||||
|  | @ -98,7 +95,6 @@ private: | |||
|     void render_clipping_plane(const Selection& selection) const; | ||||
|     bool is_mesh_update_necessary() const; | ||||
|     void update_mesh(); | ||||
|     void update_cache_entry_normal(size_t i) const; | ||||
|     bool unsaved_changes() const; | ||||
| 
 | ||||
|     bool m_lock_unique_islands = false; | ||||
|  |  | |||
|  | @ -3,6 +3,15 @@ | |||
| #include "libslic3r/Tesselate.hpp" | ||||
| #include "libslic3r/TriangleMesh.hpp" | ||||
| 
 | ||||
| #include "slic3r/GUI/Camera.hpp" | ||||
| 
 | ||||
| // There is an L function in igl that would be overridden by our localization macro.
 | ||||
| #undef L | ||||
| #include <igl/AABB.h> | ||||
| 
 | ||||
| #include <GL/glew.h> | ||||
| 
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| namespace GUI { | ||||
| 
 | ||||
|  | @ -90,6 +99,168 @@ void MeshClipper::recalculate_triangles() | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| class MeshRaycaster::AABBWrapper { | ||||
| public: | ||||
|     AABBWrapper(const TriangleMesh* mesh); | ||||
|     ~AABBWrapper() { m_AABB.deinit(); } | ||||
| 
 | ||||
|     typedef Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXfUnaligned; | ||||
|     typedef Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXiUnaligned; | ||||
|     igl::AABB<MapMatrixXfUnaligned, 3> m_AABB; | ||||
| 
 | ||||
|     Vec3f get_hit_pos(const igl::Hit& hit) const; | ||||
|     Vec3f get_hit_normal(const igl::Hit& hit) const; | ||||
| 
 | ||||
| private: | ||||
|     const TriangleMesh* m_mesh; | ||||
| }; | ||||
| 
 | ||||
| MeshRaycaster::AABBWrapper::AABBWrapper(const TriangleMesh* mesh) | ||||
|     : m_mesh(mesh) | ||||
| { | ||||
|     m_AABB.init( | ||||
|         MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3), | ||||
|         MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| MeshRaycaster::MeshRaycaster(const TriangleMesh& mesh) | ||||
|     : m_AABB_wrapper(new AABBWrapper(&mesh)), m_mesh(&mesh) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| MeshRaycaster::~MeshRaycaster() | ||||
| { | ||||
|     delete m_AABB_wrapper; | ||||
| } | ||||
| 
 | ||||
| Vec3f MeshRaycaster::AABBWrapper::get_hit_pos(const igl::Hit& hit) const | ||||
| { | ||||
|     const stl_triangle_vertex_indices& indices = m_mesh->its.indices[hit.id]; | ||||
|     return Vec3f((1-hit.u-hit.v) * m_mesh->its.vertices[indices(0)] | ||||
|                + hit.u           * m_mesh->its.vertices[indices(1)] | ||||
|                + hit.v           * m_mesh->its.vertices[indices(2)]); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Vec3f MeshRaycaster::AABBWrapper::get_hit_normal(const igl::Hit& hit) const | ||||
| { | ||||
|     const stl_triangle_vertex_indices& indices = m_mesh->its.indices[hit.id]; | ||||
|     Vec3f a(m_mesh->its.vertices[indices(1)] - m_mesh->its.vertices[indices(0)]); | ||||
|     Vec3f b(m_mesh->its.vertices[indices(2)] - m_mesh->its.vertices[indices(0)]); | ||||
|     return Vec3f(a.cross(b)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, | ||||
|                                       const Camera& camera, std::vector<Vec3f>* positions, std::vector<Vec3f>* normals) const | ||||
| { | ||||
|     const std::array<int, 4>& viewport = camera.get_viewport(); | ||||
|     const Transform3d& model_mat = camera.get_view_matrix(); | ||||
|     const Transform3d& proj_mat = camera.get_projection_matrix(); | ||||
| 
 | ||||
|     Vec3d pt1; | ||||
|     Vec3d pt2; | ||||
|     ::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 0., model_mat.data(), proj_mat.data(), viewport.data(), &pt1(0), &pt1(1), &pt1(2)); | ||||
|     ::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 1., model_mat.data(), proj_mat.data(), viewport.data(), &pt2(0), &pt2(1), &pt2(2)); | ||||
| 
 | ||||
|     std::vector<igl::Hit> hits; | ||||
| 
 | ||||
|     Transform3d inv = trafo.inverse(); | ||||
| 
 | ||||
|     pt1 = inv * pt1; | ||||
|     pt2 = inv * pt2; | ||||
| 
 | ||||
|     if (! m_AABB_wrapper->m_AABB.intersect_ray( | ||||
|         AABBWrapper::MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3), | ||||
|         AABBWrapper::MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3), | ||||
|         pt1.cast<float>(), (pt2-pt1).cast<float>(), hits)) | ||||
|         return false; // no intersection found
 | ||||
| 
 | ||||
|     std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; }); | ||||
| 
 | ||||
|     // Now stuff the points in the provided vector and calculate normals if asked about them:
 | ||||
|     if (positions != nullptr) { | ||||
|         positions->clear(); | ||||
|         if (normals != nullptr) | ||||
|             normals->clear(); | ||||
|         for (const igl::Hit& hit : hits) { | ||||
|             positions->push_back(m_AABB_wrapper->get_hit_pos(hit)); | ||||
| 
 | ||||
|             if (normals != nullptr) | ||||
|                 normals->push_back(m_AABB_wrapper->get_hit_normal(hit)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, const std::vector<Vec3f>& points, | ||||
|                                                        std::function<bool(const Vec3f&)> fn_ignore_hit) const | ||||
| { | ||||
|     std::vector<unsigned> out; | ||||
| 
 | ||||
|     const Transform3d& instance_matrix_no_translation_no_scaling = trafo.get_matrix(true,false,true); | ||||
|     Vec3f direction_to_camera = -camera.get_dir_forward().cast<float>(); | ||||
|     Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast<float>() * direction_to_camera).normalized().eval(); | ||||
|     Vec3f scaling = trafo.get_scaling_factor().cast<float>(); | ||||
|     direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2)); | ||||
| 
 | ||||
|     for (size_t i=0; i<points.size(); ++i) { | ||||
|         const Vec3f& pt = points[i]; | ||||
|         bool is_obscured = false; | ||||
|         // Cast a ray in the direction of the camera and look for intersection with the mesh:
 | ||||
|         std::vector<igl::Hit> hits; | ||||
|         // Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies.
 | ||||
|         if (m_AABB_wrapper->m_AABB.intersect_ray( | ||||
|                 AABBWrapper::MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3), | ||||
|                 AABBWrapper::MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3), | ||||
|                 pt + direction_to_camera_mesh * EPSILON, direction_to_camera_mesh, hits)) { | ||||
| 
 | ||||
|             std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; }); | ||||
|             // If the closest hit facet normal points in the same direction as the ray,
 | ||||
|             // we are looking through the mesh and should therefore discard the point:
 | ||||
|             if (m_AABB_wrapper->get_hit_normal(hits.front()).dot(direction_to_camera_mesh) > 0.f) | ||||
|                 is_obscured = true; | ||||
| 
 | ||||
|             // Eradicate all hits that the caller wants to ignore
 | ||||
|             for (unsigned j=0; j<hits.size(); ++j) { | ||||
|                 const igl::Hit& hit = hits[j]; | ||||
|                 if (fn_ignore_hit(m_AABB_wrapper->get_hit_pos(hit))) { | ||||
|                     hits.erase(hits.begin()+j); | ||||
|                     --j; | ||||
|                 } | ||||
|             } | ||||
|             // FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction.
 | ||||
|             // Also, the threshold is in mesh coordinates, not in actual dimensions.
 | ||||
|             if (! hits.empty()) | ||||
|                 is_obscured = true; | ||||
|         } | ||||
|         if (! is_obscured) | ||||
|             out.push_back(i); | ||||
|     } | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Vec3f MeshRaycaster::get_closest_point(const Vec3f& point, Vec3f* normal) const | ||||
| { | ||||
|     int idx = 0; | ||||
|     Eigen::Matrix<float, 1, 3> closest_point; | ||||
|     m_AABB_wrapper->m_AABB.squared_distance( | ||||
|         AABBWrapper::MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3), | ||||
|         AABBWrapper::MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3), | ||||
|         point, idx, closest_point); | ||||
|     if (normal) { | ||||
|         igl::Hit imag_hit; | ||||
|         imag_hit.id = idx; | ||||
|         *normal = m_AABB_wrapper->get_hit_normal(imag_hit); | ||||
|     } | ||||
|     return closest_point; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } // namespace GUI
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -14,6 +14,8 @@ class TriangleMeshSlicer; | |||
| 
 | ||||
| namespace GUI { | ||||
| 
 | ||||
| struct Camera; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class ClippingPlane | ||||
|  | @ -86,6 +88,30 @@ private: | |||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class MeshRaycaster { | ||||
| public: | ||||
|     MeshRaycaster(const TriangleMesh& mesh); | ||||
|     ~MeshRaycaster(); | ||||
|     void set_transformation(const Geometry::Transformation& trafo); | ||||
|     void set_camera(const Camera& camera); | ||||
| 
 | ||||
|     bool unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, | ||||
|                            std::vector<Vec3f>* positions = nullptr, std::vector<Vec3f>* normals = nullptr) const; | ||||
| 
 | ||||
|     std::vector<unsigned> get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, | ||||
|                                               const std::vector<Vec3f>& points, std::function<bool(const Vec3f&)> fn_ignore_hit) const; | ||||
| 
 | ||||
|     Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const; | ||||
| 
 | ||||
| private: | ||||
|     // PIMPL wrapper around igl::AABB so I don't have to include the header-only IGL here
 | ||||
|     class AABBWrapper; | ||||
|     AABBWrapper* m_AABB_wrapper; | ||||
|     const TriangleMesh* m_mesh = nullptr; | ||||
| }; | ||||
| 
 | ||||
|      | ||||
| } // namespace GUI
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -529,7 +529,11 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) : | |||
|             const std::vector<double> &init_extruders = (project_config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values; | ||||
| 
 | ||||
|             const DynamicPrintConfig* config = &wxGetApp().preset_bundle->printers.get_edited_preset().config; | ||||
|             const std::vector<std::string> &extruder_colours = (config->option<ConfigOptionStrings>("extruder_colour"))->values; | ||||
|             std::vector<std::string> extruder_colours = (config->option<ConfigOptionStrings>("extruder_colour"))->values; | ||||
|             const std::vector<std::string>& filament_colours = (wxGetApp().plater()->get_plater_config()->option<ConfigOptionStrings>("filament_colour"))->values; | ||||
|             for (size_t i=0; i<extruder_colours.size(); ++i) | ||||
|                 if (extruder_colours[i] == "" && i < filament_colours.size()) | ||||
|                     extruder_colours[i] = filament_colours[i]; | ||||
| 
 | ||||
|             WipingDialog dlg(parent, cast<float>(init_matrix), cast<float>(init_extruders), extruder_colours); | ||||
| 
 | ||||
|  | @ -4845,6 +4849,11 @@ void Plater::on_activate() | |||
| 	this->p->show_delayed_error_message(); | ||||
| } | ||||
| 
 | ||||
| const DynamicPrintConfig* Plater::get_plater_config() const | ||||
| { | ||||
|     return p->config; | ||||
| } | ||||
| 
 | ||||
| wxString Plater::get_project_filename(const wxString& extension) const | ||||
| { | ||||
|     return p->get_project_filename(extension); | ||||
|  |  | |||
|  | @ -217,6 +217,7 @@ public: | |||
|     void on_config_change(const DynamicPrintConfig &config); | ||||
|     // On activating the parent window.
 | ||||
|     void on_activate(); | ||||
|     const DynamicPrintConfig* get_plater_config() const; | ||||
| 
 | ||||
|     void update_object_menu(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -460,6 +460,9 @@ void ObjectDataViewModelNode::init_container() | |||
| #endif  //__WXGTK__
 | ||||
| } | ||||
| 
 | ||||
| #define LAYER_ROOT_ICON "edit_layers_all" | ||||
| #define LAYER_ICON      "edit_layers_some" | ||||
| 
 | ||||
| ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type) : | ||||
|     m_parent(parent), | ||||
|     m_type(type), | ||||
|  | @ -478,7 +481,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent | |||
|     } | ||||
|     else if (type == itLayerRoot) | ||||
|     { | ||||
|         m_bmp = create_scaled_bitmap(nullptr, "edit_layers_all");    // FIXME: pass window ptr
 | ||||
|         m_bmp = create_scaled_bitmap(nullptr, LAYER_ROOT_ICON);    // FIXME: pass window ptr
 | ||||
|         m_name = _(L("Layers")); | ||||
|     } | ||||
| 
 | ||||
|  | @ -507,7 +510,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent | |||
|     } | ||||
|     const std::string label_range = (boost::format(" %.2f-%.2f ") % layer_range.first % layer_range.second).str(); | ||||
|     m_name = _(L("Range")) + label_range + "(" + _(L("mm")) + ")"; | ||||
|     m_bmp = create_scaled_bitmap(nullptr, "edit_layers_some");    // FIXME: pass window ptr
 | ||||
|     m_bmp = create_scaled_bitmap(nullptr, LAYER_ICON);    // FIXME: pass window ptr
 | ||||
| 
 | ||||
|     set_action_icon(); | ||||
|     init_container(); | ||||
|  | @ -581,6 +584,9 @@ void ObjectDataViewModelNode::msw_rescale() | |||
|     if (!m_action_icon_name.empty()) | ||||
|         m_action_icon = create_scaled_bitmap(nullptr, m_action_icon_name); | ||||
| 
 | ||||
|     if (m_printable != piUndef) | ||||
|         m_printable_icon = create_scaled_bitmap(nullptr, m_printable == piPrintable ? "eye_open.png" : "eye_closed.png"); | ||||
| 
 | ||||
|     if (!m_opt_categories.empty()) | ||||
|         update_settings_digest_bitmaps(); | ||||
| } | ||||
|  | @ -1766,11 +1772,22 @@ void ObjectDataViewModel::Rescale() | |||
|         ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); | ||||
|         node->msw_rescale(); | ||||
| 
 | ||||
|         if (node->m_type & itVolume) | ||||
|         switch (node->m_type) | ||||
|         { | ||||
|         case itObject: | ||||
|             if (node->m_bmp.IsOk()) node->m_bmp = *m_warning_bmp; | ||||
|             break; | ||||
|         case itVolume: | ||||
|             node->m_bmp = GetVolumeIcon(node->m_volume_type, node->m_bmp.GetWidth() != node->m_bmp.GetHeight()); | ||||
| 
 | ||||
|         if (node->m_type & itObject && node->m_bmp.IsOk()) | ||||
|             node->m_bmp = *m_warning_bmp; | ||||
|             break; | ||||
|         case itLayerRoot: | ||||
|             node->m_bmp = create_scaled_bitmap(nullptr, LAYER_ROOT_ICON);    // FIXME: pass window ptr
 | ||||
|             break; | ||||
|         case itLayer: | ||||
|             node->m_bmp = create_scaled_bitmap(nullptr, LAYER_ICON);    // FIXME: pass window ptr
 | ||||
|             break; | ||||
|         default: break; | ||||
|         } | ||||
| 
 | ||||
|         ItemChanged(item); | ||||
|     } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 bubnikv
						bubnikv