66
77#include < boost/unordered/concurrent_flat_map.hpp>
88#include < boost/unordered/concurrent_flat_set.hpp>
9+ #include < range/v3/numeric/accumulate.hpp>
910#include < range/v3/view/map.hpp>
1011#include < spdlog/spdlog.h>
1112#include < spdlog/stopwatch.h>
3334namespace cura ::MeshMaterialSplitter
3435{
3536
37+ /* !
38+ * Utility structure that contains pre-calculated data to generate the multi-material modifiers of a mesh
39+ */
40+ struct MeshGeneratorData
41+ {
42+ const Mesh& mesh; // !< The mesh for which a material modifier is to be generated
43+ AABB3D bounding_box; // !< The bounding box of the mesh
44+ size_t estimated_iterations; // !< The roughly estimated number of voxels-propagation iterations (for progress reporting)
45+ coord_t depth; // !< The propagation depth retrieved from the mesh settings
46+ coord_t resolution; // !< The points cloud resolution retrieved from the mesh settings
47+ };
48+
3649/* !
3750 * Utility structure to store values in a map that are grouped by unique z-height and extruder number combinations
3851 */
@@ -525,21 +538,25 @@ void findBoundaryVoxels(boost::concurrent_flat_set<VoxelGrid::LocalCoordinates>&
525538 * @param texture_data The lookup containing the rasterized texture data
526539 * @param depth_squared The maximum propagation depth, squared
527540 * @param mesh_extruder_nr The main mesh extruder number
541+ * @param delta_iterations The number of already processed iterations over the total, for progress reporting
542+ * @param total_estimated_iterations The total number of iterations to be processed, for progress reporting
528543 */
529544void propagateVoxels (
530545 VoxelGrid& voxel_grid,
531546 boost::concurrent_flat_set<VoxelGrid::LocalCoordinates>& evaluated_voxels,
532- const coord_t estimated_iterations,
547+ const size_t estimated_iterations,
533548 const std::vector<Shape>& sliced_mesh,
534549 const SpatialLookup& texture_data,
535550 const coord_t depth_squared,
536- const uint8_t mesh_extruder_nr)
551+ const uint8_t mesh_extruder_nr,
552+ const size_t delta_iterations,
553+ const size_t total_estimated_iterations)
537554{
538- uint32_t iteration = 0 ;
555+ size_t iteration = 0 ;
539556
540557 while (! evaluated_voxels.empty ())
541558 {
542- Progress::messageProgress (Progress::Stage::SPLIT_MULTIMATERIAL, iteration, estimated_iterations);
559+ Progress::messageProgress (Progress::Stage::SPLIT_MULTIMATERIAL, std::min ( iteration, estimated_iterations) + delta_iterations, total_estimated_iterations );
543560
544561 // Make the list of new voxels to be evaluated, based on which were evaluated before
545562 spdlog::debug (" Finding voxels around {} voxels for iteration {}" , evaluated_voxels.size (), iteration);
@@ -560,29 +577,30 @@ void propagateVoxels(
560577
561578/* !
562579 * Generate a modifier mesh for every extruder other than 0, that has some user-painted texture data
563- * @param mesh The mesh being sliced
580+ * @param mesh_data The generation data for the mesh to be processed
564581 * @param texture_data_provider The provider containing the texture painted data
582+ * @param delta_iterations The number of already processed iterations over the total, for progress reporting
583+ * @param total_estimated_iterations The total number of iterations to be processed, for progress reporting
565584 * @return A list of modifier meshes to be added to the slicing process
566585 */
567- std::vector<Mesh> makeModifierMeshes (const Mesh& mesh, const std::shared_ptr<TextureDataProvider>& texture_data_provider)
586+ std::vector<Mesh> makeModifierMeshes (
587+ const MeshGeneratorData& mesh_data,
588+ const std::shared_ptr<TextureDataProvider>& texture_data_provider,
589+ const size_t delta_iterations,
590+ const size_t total_estimated_iterations)
568591{
569- const Settings& settings = mesh.settings_ ;
592+ const Settings& settings = mesh_data. mesh .settings_ ;
570593 const uint8_t mesh_extruder_nr = static_cast <uint8_t >(settings.get <size_t >(" extruder_nr" ));
571594
572595 // 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
573596 // to assign any point in 3D space just by finding the closest outside point and see what extruder it is assigned to.
574597 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- }
581- bounding_box.expand (resolution * 8 );
598+ AABB3D bounding_box = mesh_data.bounding_box ;
599+ bounding_box.expand (mesh_data.resolution * 8 );
582600
583601 // Create the voxel grid and initially fill it with the rasterized mesh triangles, which will be used as spatial reference for the texture data
584- VoxelGrid voxel_grid (bounding_box, resolution);
585- if (! makeVoxelGridFromTexture (mesh, texture_data_provider, voxel_grid, mesh_extruder_nr))
602+ VoxelGrid voxel_grid (bounding_box, mesh_data. resolution );
603+ if (! makeVoxelGridFromTexture (mesh_data. mesh , texture_data_provider, voxel_grid, mesh_extruder_nr))
586604 {
587605 // Texture is filled with the main extruder, don't bother doing anything
588606 return {};
@@ -591,11 +609,10 @@ std::vector<Mesh> makeModifierMeshes(const Mesh& mesh, const std::shared_ptr<Tex
591609 spdlog::debug (" Prepare spatial lookup for texture data" );
592610 const SpatialLookup texture_data = SpatialLookup::makeSpatialLookupFromVoxelGrid (voxel_grid);
593611
594- const auto depth = settings.get <coord_t >(" multi_material_paint_depth" );
595- const coord_t depth_squared = depth * depth;
612+ const coord_t depth_squared = mesh_data.depth * mesh_data.depth ;
596613
597614 // Create a slice of the mesh so that we can quickly check for points insideness
598- const std::vector<Shape> sliced_mesh = sliceMesh (mesh, voxel_grid);
615+ const std::vector<Shape> sliced_mesh = sliceMesh (mesh_data. mesh , voxel_grid);
599616
600617 spdlog::debug (" Get initially filled voxels" );
601618 boost::concurrent_flat_set<VoxelGrid::LocalCoordinates> previously_evaluated_voxels;
@@ -608,35 +625,92 @@ std::vector<Mesh> makeModifierMeshes(const Mesh& mesh, const std::shared_ptr<Tex
608625 };
609626 });
610627
611- // Make a rough estimation of the max number of iterations, by calculating how deep we may propagate inside the mesh
612- const double bounding_box_max_depth = std::max ({ bounding_box.spanX () / 2.0 , bounding_box.spanY () / 2.0 , bounding_box.spanZ () / 2.0 });
613- const double estimated_min_depth = std::min (static_cast <double >(depth), bounding_box_max_depth);
614- const coord_t estimated_iterations = estimated_min_depth / resolution;
615- spdlog::debug (" Estimated {} iterations" , estimated_iterations);
616-
617- propagateVoxels (voxel_grid, previously_evaluated_voxels, estimated_iterations, sliced_mesh, texture_data, depth_squared, mesh_extruder_nr);
628+ propagateVoxels (
629+ voxel_grid,
630+ previously_evaluated_voxels,
631+ mesh_data.estimated_iterations ,
632+ sliced_mesh,
633+ texture_data,
634+ depth_squared,
635+ mesh_extruder_nr,
636+ delta_iterations,
637+ total_estimated_iterations);
618638
619639 return makeMeshesFromVoxelsGrid (voxel_grid, mesh_extruder_nr);
620640}
621641
622- void makeMaterialModifierMeshes (const Mesh& mesh, MeshGroup* meshgroup)
642+ /* !
643+ * Pre-calculate multi-material mesh generation data for the meshes in the given group
644+ * @param meshgroup The group containing the meshes to be processed
645+ * @return The list of mesh generation data for meshes that contain relevant information
646+ */
647+ std::vector<MeshGeneratorData> makeInitialMeshesGenerationData (const MeshGroup* meshgroup)
623648{
624- if (mesh.texture_ == nullptr || mesh.texture_data_mapping_ == nullptr || ! mesh.texture_data_mapping_ ->contains (" extruder" ))
649+ std::vector<MeshGeneratorData> result;
650+
651+ for (const Mesh& mesh : meshgroup->meshes )
625652 {
626- return ;
653+ if (mesh.texture_ == nullptr || mesh.texture_data_mapping_ == nullptr || ! mesh.texture_data_mapping_ ->contains (" extruder" ))
654+ {
655+ continue ;
656+ }
657+
658+ const Settings& settings = mesh.settings_ ;
659+ MeshGeneratorData mesh_data{ .mesh = mesh,
660+ .depth = settings.get <coord_t >(" multi_material_paint_depth" ),
661+ .resolution = settings.get <coord_t >(" multi_material_paint_resolution" ) };
662+
663+ for (const MeshVertex& vertex : mesh.vertices_ )
664+ {
665+ mesh_data.bounding_box .include (vertex.p_ );
666+ }
667+
668+ // Make a rough estimation of the max number of iterations, by calculating how deep we may propagate inside the mesh
669+ const double bounding_box_max_depth = std::max ({ mesh_data.bounding_box .spanX () / 2.0 , mesh_data.bounding_box .spanY () / 2.0 , mesh_data.bounding_box .spanZ () / 2.0 });
670+ const double estimated_min_depth = std::min (static_cast <double >(mesh_data.depth ), bounding_box_max_depth);
671+ mesh_data.estimated_iterations = estimated_min_depth / mesh_data.resolution ;
672+ spdlog::debug (" Estimated {} iterations for {}" , mesh_data.estimated_iterations , mesh.mesh_name_ );
673+
674+ result.push_back (mesh_data);
627675 }
628676
629- const spdlog::stopwatch timer ;
630- spdlog::info ( " Start multi-material mesh generation " );
677+ return result ;
678+ }
631679
632- const auto texture_data_provider = std::make_shared<TextureDataProvider>(nullptr , mesh.texture_ , mesh.texture_data_mapping_ );
680+ void makeMaterialModifierMeshes (MeshGroup* meshgroup)
681+ {
682+ const std::vector<MeshGeneratorData> mesh_generation_data = makeInitialMeshesGenerationData (meshgroup);
683+ size_t delta_iterations = 0 ;
684+ const size_t total_estimated_iterations = ranges::accumulate (
685+ mesh_generation_data,
686+ 0 ,
687+ [](const size_t total_iterations, const MeshGeneratorData& mesh_data)
688+ {
689+ return total_iterations + mesh_data.estimated_iterations ;
690+ });
633691
634- for (const Mesh& modifier_mesh : makeModifierMeshes (mesh, texture_data_provider))
692+ std::vector<Mesh> modifier_meshes;
693+ for (const MeshGeneratorData& mesh_data : mesh_generation_data)
635694 {
636- meshgroup->meshes .push_back (modifier_mesh);
695+ const Mesh& mesh = mesh_data.mesh ;
696+ const spdlog::stopwatch timer;
697+ spdlog::info (" Start multi-material mesh generation for {}" , mesh.mesh_name_ );
698+
699+ const auto texture_data_provider = std::make_shared<TextureDataProvider>(nullptr , mesh.texture_ , mesh.texture_data_mapping_ );
700+ for (const Mesh& modifier_mesh : makeModifierMeshes (mesh_data, texture_data_provider, delta_iterations, total_estimated_iterations))
701+ {
702+ modifier_meshes.push_back (std::move (modifier_mesh));
703+ }
704+
705+ delta_iterations += mesh_data.estimated_iterations ;
706+ spdlog::info (" Multi-material mesh generation for {} took {} seconds" , mesh.mesh_name_ , timer.elapsed ().count ());
637707 }
638708
639- spdlog::info (" Multi-material mesh generation took {} seconds" , timer.elapsed ().count ());
709+ // Add meshes to group afterwards to avoid re-allocating the meshes in the vector
710+ for (Mesh& modifier_mesh : modifier_meshes)
711+ {
712+ meshgroup->meshes .push_back (std::move (modifier_mesh));
713+ }
640714}
641715
642716} // namespace cura::MeshMaterialSplitter
0 commit comments