@@ -34,15 +34,15 @@ namespace cura::MeshMaterialSplitter
3434{
3535
3636/* !
37- * Utility structure to store values in a map that are grouped by unique z-height and extruder number combinations
37+ * Utility structure to store values in a map that are grouped by unique z-height and value combinations
3838 */
3939union ContourKey
4040{
4141 uint32_t key;
4242 struct
4343 {
4444 uint16_t z;
45- uint8_t extruder ;
45+ uint8_t value ;
4646 uint8_t black_hole{ 0 }; // Don't place anything in there, or it would be lost forever (it exists only to properly set the 4th byte of the key)
4747 } definition;
4848};
@@ -62,18 +62,20 @@ bool operator==(const ContourKey& key1, const ContourKey& key2)
6262 * @param mesh The mesh to fill the voxels grid with
6363 * @param texture_data_provider The provider containing the painted texture data
6464 * @param voxel_grid The voxels grid to be filled with mesh data
65- * @param mesh_extruder_nr The main mesh extruder number
65+ * @param default_value The main mesh extruder number
6666 * @return True if this generated relevant data for multi-extruder, otherwise this means the mesh is completely filled with only extruder 0 and there is no need to go further on
6767 * trying to calculate the modified meshes.
6868 */
69- bool makeVoxelGridFromTexture (const Mesh& mesh, const std::shared_ptr<TextureDataProvider>& texture_data_provider, VoxelGrid& voxel_grid, const uint8_t mesh_extruder_nr)
69+ boost::concurrent_flat_set<uint8_t > makeVoxelGridFromTexture (
70+ const Mesh& mesh,
71+ const std::shared_ptr<TextureDataProvider>& texture_data_provider,
72+ const std::string& texture_feature,
73+ VoxelGrid& voxel_grid,
74+ const uint8_t default_value,
75+ const std::unordered_set<size_t >& authorized_values,
76+ const bool ignore_default_value = false )
7077{
71- boost::concurrent_flat_set<uint8_t > found_extruders;
72- std::unordered_set<size_t > active_extruders;
73- for (const ExtruderTrain& extruder : Application::getInstance ().current_slice_ ->scene .extruders )
74- {
75- active_extruders.insert (extruder.extruder_nr_ );
76- }
78+ boost::concurrent_flat_set<uint8_t > found_values;
7779
7880 cura::parallel_for (
7981 mesh.faces_ ,
@@ -109,52 +111,45 @@ bool makeVoxelGridFromTexture(const Mesh& mesh, const std::shared_ptr<TextureDat
109111
110112 const Point2F point_uv_coords = MeshUtils::getUVCoordinates (barycentric_coordinates.value (), face_uvs);
111113 const std::pair<size_t , size_t > pixel = texture_data_provider->getTexture ()->getPixelCoordinates (Point2F (point_uv_coords.x_ , point_uv_coords.y_ ));
112- std::optional<uint32_t > extruder_nr = texture_data_provider->getValue (std::get<0 >(pixel), std::get<1 >(pixel), " extruder" );
113- if (! extruder_nr.has_value () || ! active_extruders.contains (extruder_nr.value ()))
114+ std::optional<uint32_t > extruder_nr = texture_data_provider->getValue (std::get<0 >(pixel), std::get<1 >(pixel), texture_feature);
115+ if (! extruder_nr.has_value () || ! authorized_values.contains (extruder_nr.value ()))
116+ {
117+ extruder_nr = default_value;
118+ }
119+
120+ if (ignore_default_value && extruder_nr.value () == default_value)
114121 {
115- extruder_nr = mesh_extruder_nr ;
122+ continue ;
116123 }
117124
118125 voxel_grid.setOrUpdateOccupation (traversed_voxel, extruder_nr.value ());
119- found_extruders .insert (extruder_nr.value ());
126+ found_values .insert (extruder_nr.value ());
120127 }
121128 });
122129
123- if (found_extruders.size () == 1 )
124- {
125- // We have found only one extruder in the texture, so return true only if this extruder is not the mesh extruder, otherwise the rest is useless
126- bool is_specific_extruder = true ;
127- found_extruders.visit_all (
128- [&is_specific_extruder, &mesh_extruder_nr](const uint8_t extruder_nr)
129- {
130- is_specific_extruder = extruder_nr != mesh_extruder_nr;
131- });
132- return is_specific_extruder;
133- }
134-
135- return true ;
130+ return found_values;
136131}
137132
138133/* !
139134 * Create modifier meshes from the given voxels grid, filled with the contours of the areas that should be processed by the different extruders.
140135 * @param voxel_grid The voxels grid containing the extruder occupations
141- * @param mesh_extruder_nr The main mesh extruder number
136+ * @param ignore_value The main mesh extruder number
142137 * @return A list of modifier meshes to be registered
143138 *
144139 * This function works by treating each horizontal plane separately of the voxels grid. For each plane, we apply a marching squares algorithm in order to generate 2D polygons.
145140 * Then we just have to extrude those polygons vertically. The final mesh has no horizontal face, thus it is not watertight at all. However, since it will subsequently
146141 * be re-sliced on XY planes, this is good enough.
147142 */
148- std::vector< Mesh> makeMeshesFromVoxelsGrid (const VoxelGrid& voxel_grid, const uint8_t mesh_extruder_nr )
143+ std::map< uint8_t , Mesh> makeMeshesFromVoxelsGrid (const VoxelGrid& voxel_grid, const uint8_t ignore_value, const Mesh& input_mesh, const bool use_value_as_extruder_nr )
149144{
150145 spdlog::debug (" Make modifier meshes from voxels grid" );
151146
152147 // First, gather all positions that should be considered for the marching square, e.g. all that have a specific extruder and around them
153148 boost::concurrent_flat_set<VoxelGrid::LocalCoordinates> marching_squares;
154149 voxel_grid.visitOccupiedVoxels (
155- [&marching_squares, &mesh_extruder_nr ](const auto & occupied_voxel)
150+ [&marching_squares, &ignore_value ](const auto & occupied_voxel)
156151 {
157- if (occupied_voxel.second != mesh_extruder_nr )
152+ if (occupied_voxel.second != ignore_value )
158153 {
159154 marching_squares.insert (occupied_voxel.first );
160155 if (occupied_voxel.first .position .x > 0 )
@@ -207,49 +202,50 @@ std::vector<Mesh> makeMeshesFromVoxelsGrid(const VoxelGrid& voxel_grid, const ui
207202#ifdef __cpp_lib_execution
208203 std::execution::par,
209204#endif
210- [&voxel_grid, &raw_contours, &position_delta_center, &marching_segments, &mesh_extruder_nr ](const VoxelGrid::LocalCoordinates square_start)
205+ [&voxel_grid, &raw_contours, &position_delta_center, &marching_segments, &ignore_value ](const VoxelGrid::LocalCoordinates square_start)
211206 {
212207 const int32_t x_plus1 = static_cast <int32_t >(square_start.position .x ) + 1 ;
213208 const bool x_plus1_valid = x_plus1 <= std::numeric_limits<uint16_t >::max ();
214209 const int32_t y_plus1 = static_cast <int32_t >(square_start.position .y ) + 1 ;
215210 const bool y_plus1_valid = y_plus1 <= std::numeric_limits<uint16_t >::max ();
216211
217- std::unordered_set<uint8_t > filled_extruders ;
212+ std::unordered_set<uint8_t > filled_values ;
218213 std::array<uint8_t , 4 > occupation_bits;
219- auto add_occupied_extruder = [&voxel_grid,
220- &mesh_extruder_nr ,
221- &filled_extruders ,
222- &occupation_bits,
223- &square_start](const int32_t x, const int32_t y, const bool position_valid, const size_t occupation_bit_index) -> void
214+ auto add_occupied_value = [&voxel_grid,
215+ &ignore_value ,
216+ &filled_values ,
217+ &occupation_bits,
218+ &square_start](const int32_t x, const int32_t y, const bool position_valid, const size_t occupation_bit_index) -> void
224219 {
225220 if (position_valid)
226221 {
227222 const std::optional<uint8_t > occupation = voxel_grid.getOccupation (VoxelGrid::LocalCoordinates (x, y, square_start.position .z ));
228223 if (occupation.has_value ())
229224 {
230- filled_extruders .insert (occupation.value ());
225+ filled_values .insert (occupation.value ());
231226 occupation_bits[occupation_bit_index] = occupation.value ();
232227 return ;
233228 }
234229 }
235230
236- occupation_bits[occupation_bit_index] = mesh_extruder_nr;
231+ filled_values.insert (ignore_value);
232+ occupation_bits[occupation_bit_index] = ignore_value;
237233 };
238234
239- add_occupied_extruder (x_plus1, y_plus1, x_plus1_valid && y_plus1_valid, 0 );
240- add_occupied_extruder (square_start.position .x , y_plus1, y_plus1_valid, 1 );
241- add_occupied_extruder (x_plus1, square_start.position .y , x_plus1_valid, 2 );
242- add_occupied_extruder (square_start.position .x , square_start.position .y , true , 3 );
235+ add_occupied_value (x_plus1, y_plus1, x_plus1_valid && y_plus1_valid, 0 );
236+ add_occupied_value (square_start.position .x , y_plus1, y_plus1_valid, 1 );
237+ add_occupied_value (x_plus1, square_start.position .y , x_plus1_valid, 2 );
238+ add_occupied_value (square_start.position .x , square_start.position .y , true , 3 );
243239
244- if (filled_extruders .size () < 2 )
240+ if (filled_values .size () < 2 )
245241 {
246242 // Early-out, since this is not going to generate any segment
247243 return ;
248244 }
249245
250- for (const uint8_t extruder : filled_extruders )
246+ for (const uint8_t extruder : filled_values )
251247 {
252- if (extruder == mesh_extruder_nr )
248+ if (extruder == ignore_value )
253249 {
254250 continue ;
255251 }
@@ -307,10 +303,10 @@ std::vector<Mesh> makeMeshesFromVoxelsGrid(const VoxelGrid& voxel_grid, const ui
307303#ifdef __cpp_lib_execution
308304 std::execution::par,
309305#endif
310- [&simplifier, &voxel_grid, &meshes, &mutex](const auto & contour)
306+ [&simplifier, &voxel_grid, &meshes, &mutex, &input_mesh, &use_value_as_extruder_nr ](const auto & contour)
311307 {
312308 const uint16_t z = contour.first .definition .z ;
313- const uint8_t extruder = contour.first .definition .extruder ;
309+ const uint8_t value = contour.first .definition .value ;
314310 const coord_t z_low = voxel_grid.toGlobalZ (z, false );
315311 const coord_t z_high = voxel_grid.toGlobalZ (z + 1 , false );
316312
@@ -320,16 +316,15 @@ std::vector<Mesh> makeMeshesFromVoxelsGrid(const VoxelGrid& voxel_grid, const ui
320316 for (const Polygon& simplified_polygon : simplified_polygons)
321317 {
322318 const std::lock_guard lock (mutex);
323- const auto mesh_iterator = meshes.find (extruder );
319+ const auto mesh_iterator = meshes.find (value );
324320 if (mesh_iterator == meshes.end ())
325321 {
326- Mesh mesh (Application::getInstance ().current_slice_ ->scene .extruders .at (extruder).settings_ );
327- mesh.settings_ .add (" cutting_mesh" , " true" );
328- mesh.settings_ .add (" extruder_nr" , std::to_string (extruder));
329- meshes.insert ({ extruder, mesh });
322+ const Settings& settings = use_value_as_extruder_nr ? Application::getInstance ().current_slice_ ->scene .extruders [value].settings_
323+ : Application::getInstance ().current_slice_ ->scene .settings ;
324+ meshes.insert ({ value, Mesh (settings) });
330325 }
331326
332- Mesh& mesh = meshes[extruder ];
327+ Mesh& mesh = meshes[value ];
333328 for (auto iterator = simplified_polygon.beginSegments (); iterator != simplified_polygon.endSegments (); ++iterator)
334329 {
335330 const Point2LL& start = (*iterator).start ;
@@ -340,12 +335,35 @@ std::vector<Mesh> makeMeshesFromVoxelsGrid(const VoxelGrid& voxel_grid, const ui
340335 }
341336 });
342337
343- std::vector<Mesh> meshes_vec;
344- for (Mesh& mesh : meshes | ranges::views::values)
338+ return meshes;
339+ }
340+
341+ std::vector<Mesh> applyMeshExtruders (std::map<uint8_t , Mesh>& meshes)
342+ {
343+ std::vector<Mesh> output_meshes;
344+ for (auto & [extruder_nr, mesh] : meshes)
345345 {
346- meshes_vec.push_back (std::move (mesh));
346+ mesh.settings_ .add (" cutting_mesh" , " true" );
347+ mesh.settings_ .add (" extruder_nr" , std::to_string (extruder_nr));
348+ output_meshes.push_back (std::move (mesh));
347349 }
348- return meshes_vec;
350+
351+ return output_meshes;
352+ }
353+
354+ std::vector<Mesh> applyMeshSupport (std::map<uint8_t , Mesh>& meshes)
355+ {
356+ std::vector<Mesh> output_meshes;
357+ for (auto & [support_value, mesh] : meshes)
358+ {
359+ if (support_value == 2 )
360+ {
361+ mesh.settings_ .add (" anti_overhang_mesh" , " true" );
362+ output_meshes.push_back (std::move (mesh));
363+ }
364+ }
365+
366+ return output_meshes;
349367}
350368
351369/* !
@@ -561,28 +579,43 @@ void propagateVoxels(
561579/* !
562580 * Generate a modifier mesh for every extruder other than 0, that has some user-painted texture data
563581 * @param mesh The mesh being sliced
582+ * @param mesh_bounding_box The bounding box of the mesh, right ?
564583 * @param texture_data_provider The provider containing the texture painted data
565584 * @return A list of modifier meshes to be added to the slicing process
566585 */
567- std::vector<Mesh> makeMaterialModifierMeshes (const Mesh& mesh, const std::shared_ptr<TextureDataProvider>& texture_data_provider)
586+ std::vector<Mesh> makeMaterialModifierMeshes (const Mesh& mesh, const AABB3D& mesh_bounding_box, const std::shared_ptr<TextureDataProvider>& texture_data_provider)
568587{
569- const Settings& settings = Application::getInstance (). current_slice_ -> scene . settings ;
570- const uint8_t mesh_extruder_nr = static_cast <uint8_t >(mesh. settings_ .get <size_t >(" extruder_nr" ));
588+ const Settings& settings = mesh. settings_ ;
589+ const uint8_t mesh_extruder_nr = static_cast <uint8_t >(settings .get <size_t >(" extruder_nr" ));
571590
572591 // Fill a first voxel grid by rasterizing the triangles of the mesh in 3D, and assign the extruders according to the texture. This way we can later evaluate which extruder
573592 // to assign any point in 3D space just by finding the closest outside point and see what extruder it is assigned to.
574593 spdlog::debug (" Fill original voxels based on texture data" );
575- auto resolution = settings.get <coord_t >(" multi_material_paint_resolution" );
576- AABB3D bounding_box;
577- for (const MeshVertex& vertex : mesh.vertices_ )
578- {
579- bounding_box.include (vertex.p_ );
580- }
594+ const auto resolution = settings.get <coord_t >(" multi_material_paint_resolution" );
595+ AABB3D bounding_box = mesh_bounding_box;
581596 bounding_box.expand (resolution * 8 );
582597
583598 // Create the voxel grid and initially fill it with the rasterized mesh triangles, which will be used as spatial reference for the texture data
584599 VoxelGrid voxel_grid (bounding_box, resolution);
585- if (! makeVoxelGridFromTexture (mesh, texture_data_provider, voxel_grid, mesh_extruder_nr))
600+
601+ std::unordered_set<size_t > active_extruders;
602+ for (const ExtruderTrain& extruder : Application::getInstance ().current_slice_ ->scene .extruders )
603+ {
604+ active_extruders.insert (extruder.extruder_nr_ );
605+ }
606+
607+ const boost::concurrent_flat_set<uint8_t > found_extruders = makeVoxelGridFromTexture (mesh, texture_data_provider, " extruder" , voxel_grid, mesh_extruder_nr, active_extruders);
608+ bool texture_contains_multimaterial_data = true ;
609+ if (found_extruders.size () == 1 )
610+ {
611+ // We have found only one extruder in the texture, so return true only if this extruder is not the mesh extruder, otherwise the rest is useless
612+ found_extruders.visit_all (
613+ [&texture_contains_multimaterial_data, &mesh_extruder_nr](const uint8_t extruder_nr)
614+ {
615+ texture_contains_multimaterial_data = extruder_nr != mesh_extruder_nr;
616+ });
617+ }
618+ if (! texture_contains_multimaterial_data)
586619 {
587620 // Texture is filled with the main extruder, don't bother doing anything
588621 return {};
@@ -616,16 +649,37 @@ std::vector<Mesh> makeMaterialModifierMeshes(const Mesh& mesh, const std::shared
616649
617650 propagateVoxels (voxel_grid, previously_evaluated_voxels, estimated_iterations, sliced_mesh, texture_data, deepness_squared, mesh_extruder_nr);
618651
619- return makeMeshesFromVoxelsGrid (voxel_grid, mesh_extruder_nr);
652+ constexpr bool use_value_as_extruder_nr = true ;
653+ std::map<uint8_t , Mesh> meshes = makeMeshesFromVoxelsGrid (voxel_grid, mesh_extruder_nr, mesh, use_value_as_extruder_nr);
654+ return applyMeshExtruders (meshes);
620655}
621656
622- std::vector<Mesh> makeSupportModifierMeshes (const Mesh& mesh, const std::shared_ptr<TextureDataProvider>& texture_data_provider)
657+ std::vector<Mesh> makeSupportModifierMeshes (const Mesh& mesh, const AABB3D& mesh_bounding_box, const std::shared_ptr<TextureDataProvider>& texture_data_provider)
623658{
659+ const Settings& settings = mesh.settings_ ;
660+ const auto resolution = settings.get <coord_t >(" multi_material_paint_resolution" );
661+
662+ // Create the voxel grid and initially fill it with the rasterized mesh triangles
663+ VoxelGrid voxel_grid (mesh_bounding_box, resolution);
664+ constexpr bool ignore_default_value = true ;
665+ const boost::concurrent_flat_set<uint8_t > found_support_values
666+ = makeVoxelGridFromTexture (mesh, texture_data_provider, " support" , voxel_grid, 0 , { 1 , 2 }, ignore_default_value);
667+ if (found_support_values.empty ())
668+ {
669+ // Texture is filled with only automatic support, don't bother doing anything
670+ return {};
671+ }
672+
673+ constexpr uint8_t ignore_value = 0 ;
674+ constexpr bool use_value_as_extruder_nr = false ;
675+ std::map<uint8_t , Mesh> meshes = makeMeshesFromVoxelsGrid (voxel_grid, ignore_value, mesh, use_value_as_extruder_nr);
676+ return applyMeshSupport (meshes);
624677}
625678
626679void makePaintingModifierMeshes (const Mesh& mesh, MeshGroup* meshgroup)
627680{
628- if (mesh.texture_ == nullptr || mesh.texture_data_mapping_ == nullptr || ! mesh.texture_data_mapping_ ->contains (" extruder" ))
681+ if (mesh.texture_ == nullptr || mesh.texture_data_mapping_ == nullptr
682+ || (! mesh.texture_data_mapping_ ->contains (" extruder" ) && ! mesh.texture_data_mapping_ ->contains (" support" )))
629683 {
630684 return ;
631685 }
@@ -635,7 +689,18 @@ void makePaintingModifierMeshes(const Mesh& mesh, MeshGroup* meshgroup)
635689
636690 const auto texture_data_provider = std::make_shared<TextureDataProvider>(nullptr , mesh.texture_ , mesh.texture_data_mapping_ );
637691
638- for (const Mesh& modifier_mesh : makeMaterialModifierMeshes (mesh, texture_data_provider))
692+ AABB3D mesh_bounding_box;
693+ for (const MeshVertex& vertex : mesh.vertices_ )
694+ {
695+ mesh_bounding_box.include (vertex.p_ );
696+ }
697+
698+ for (const Mesh& modifier_mesh : makeMaterialModifierMeshes (mesh, mesh_bounding_box, texture_data_provider))
699+ {
700+ meshgroup->meshes .push_back (modifier_mesh);
701+ }
702+
703+ for (const Mesh& modifier_mesh : makeSupportModifierMeshes (mesh, mesh_bounding_box, texture_data_provider))
639704 {
640705 meshgroup->meshes .push_back (modifier_mesh);
641706 }
0 commit comments