Skip to content

Commit 945b11b

Browse files
committed
Basically working blocker mesh generation based on painting
CURA-12580
1 parent 0269c86 commit 945b11b

File tree

5 files changed

+143
-78
lines changed

5 files changed

+143
-78
lines changed

include/mesh.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ class Mesh
139139
std::shared_ptr<Image> texture_;
140140
std::shared_ptr<TextureDataMapping> texture_data_mapping_;
141141

142-
Mesh(Settings& parent);
142+
Mesh(const Settings& parent);
143143
Mesh();
144144

145145
/*!

include/settings/Settings.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ class Settings
100100
*
101101
* If this set of settings has no value for a setting, the parent is asked.
102102
*/
103-
void setParent(Settings* new_parent);
103+
void setParent(const Settings* new_parent);
104104

105105
std::unordered_map<std::string, std::string> getFlattendSettings() const;
106106

@@ -111,7 +111,7 @@ class Settings
111111
* Optionally, a parent setting container to ask for the value of a setting
112112
* if this container has no value for it.
113113
*/
114-
Settings* parent;
114+
const Settings* parent;
115115

116116
/*!
117117
* \brief A dictionary to map the setting keys to the actual setting values.

src/MeshMaterialSplitter.cpp

Lines changed: 138 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
3939
union 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

626679
void 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

Comments
 (0)