mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-31 16:21:58 +08:00
SPE-2063: Determine correctly which extruders are used when the object is painted by the multi-material painting gizmo.
During the serialization of TriangleSelector and also during reading serialized painting data from 3MF, we cache all used states in the painted triangle mesh. Based on this information, we can quickly determine which extruders are used and which don't.
This commit is contained in:
parent
b425fe459d
commit
e14ba9d431
@ -2095,6 +2095,7 @@ void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::stri
|
||||
assert(m_data.triangles_to_split.empty() || m_data.triangles_to_split.back().triangle_idx < triangle_id);
|
||||
m_data.triangles_to_split.emplace_back(triangle_id, int(m_data.bitstream.size()));
|
||||
|
||||
const size_t bitstream_start_idx = m_data.bitstream.size();
|
||||
for (auto it = str.crbegin(); it != str.crend(); ++it) {
|
||||
const char ch = *it;
|
||||
int dec = 0;
|
||||
@ -2106,9 +2107,11 @@ void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::stri
|
||||
assert(false);
|
||||
|
||||
// Convert to binary and append into code.
|
||||
for (int i=0; i<4; ++i)
|
||||
for (int i = 0; i < 4; ++i)
|
||||
m_data.bitstream.insert(m_data.bitstream.end(), bool(dec & (1 << i)));
|
||||
}
|
||||
|
||||
m_data.update_used_states(bitstream_start_idx);
|
||||
}
|
||||
|
||||
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
|
||||
|
@ -664,9 +664,9 @@ enum class ConversionType : int {
|
||||
class FacetsAnnotation final : public ObjectWithTimestamp {
|
||||
public:
|
||||
// Assign the content if the timestamp differs, don't assign an ObjectID.
|
||||
void assign(const FacetsAnnotation& rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } }
|
||||
void assign(FacetsAnnotation&& rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
|
||||
const TriangleSelector::TriangleSplittingData &get_data() const throw() { return m_data; }
|
||||
void assign(const FacetsAnnotation &rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } }
|
||||
void assign(FacetsAnnotation &&rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
|
||||
const TriangleSelector::TriangleSplittingData &get_data() const noexcept { return m_data; }
|
||||
bool set(const TriangleSelector& selector);
|
||||
indexed_triangle_set get_facets(const ModelVolume& mv, TriangleStateType type) const;
|
||||
indexed_triangle_set get_facets_strict(const ModelVolume& mv, TriangleStateType type) const;
|
||||
|
@ -1375,9 +1375,20 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
if (const auto &volumes = print_object.model_object()->volumes;
|
||||
num_extruders > 1 &&
|
||||
std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume *v) { return ! v->mm_segmentation_facets.empty(); }) != volumes.end()) {
|
||||
//FIXME be more specific! Don't enumerate extruders that are not used for painting!
|
||||
painting_extruders.assign(num_extruders, 0);
|
||||
std::iota(painting_extruders.begin(), painting_extruders.end(), 1);
|
||||
|
||||
std::array<bool, static_cast<size_t>(TriangleStateType::Count)> used_facet_states{};
|
||||
for (const ModelVolume *volume : volumes) {
|
||||
const std::vector<bool> &volume_used_facet_states = volume->mm_segmentation_facets.get_data().used_states;
|
||||
|
||||
assert(volume_used_facet_states.size() == used_facet_states.size());
|
||||
for (size_t state_idx = 0; state_idx < std::min(volume_used_facet_states.size(), used_facet_states.size()); ++state_idx)
|
||||
used_facet_states[state_idx] |= volume_used_facet_states[state_idx];
|
||||
}
|
||||
|
||||
for (size_t state_idx = static_cast<size_t>(TriangleStateType::Extruder1); state_idx < used_facet_states.size(); ++state_idx) {
|
||||
if (used_facet_states[state_idx])
|
||||
painting_extruders.emplace_back(state_idx);
|
||||
}
|
||||
}
|
||||
if (model_object_status.print_object_regions_status == ModelObjectStatus::PrintObjectRegionsStatus::Valid) {
|
||||
// Verify that the trafo for regions & volume bounding boxes thus for regions is still applicable.
|
||||
|
@ -1560,6 +1560,9 @@ TriangleSelector::TriangleSplittingData TriangleSelector::serialize() const {
|
||||
} else {
|
||||
// In case this is leaf, we better save information about its state.
|
||||
int n = int(tr.get_state());
|
||||
if (n < static_cast<size_t>(TriangleStateType::Count))
|
||||
data.used_states[n] = true;
|
||||
|
||||
if (n >= 3) {
|
||||
assert(n <= 16);
|
||||
if (n <= 16) {
|
||||
@ -1694,6 +1697,39 @@ void TriangleSelector::deserialize(const TriangleSplittingData &data, bool needs
|
||||
}
|
||||
}
|
||||
|
||||
void TriangleSelector::TriangleSplittingData::update_used_states(const size_t bitstream_start_idx) {
|
||||
assert(bitstream_start_idx < this->bitstream.size());
|
||||
assert(!this->bitstream.empty() && this->bitstream.size() != bitstream_start_idx);
|
||||
assert((this->bitstream.size() - bitstream_start_idx) % 4 == 0);
|
||||
|
||||
if (this->bitstream.empty() || this->bitstream.size() == bitstream_start_idx)
|
||||
return;
|
||||
|
||||
size_t nibble_idx = bitstream_start_idx;
|
||||
|
||||
auto read_next_nibble = [&data_bitstream = std::as_const(this->bitstream), &nibble_idx]() -> uint8_t {
|
||||
assert(nibble_idx + 3 < data_bitstream.size());
|
||||
uint8_t code = 0;
|
||||
for (size_t bit_idx = 0; bit_idx < 4; ++bit_idx)
|
||||
code |= data_bitstream[nibble_idx++] << bit_idx;
|
||||
return code;
|
||||
};
|
||||
|
||||
while (nibble_idx < this->bitstream.size()) {
|
||||
const uint8_t code = read_next_nibble();
|
||||
|
||||
if (const bool is_split = (code & 0b11) != 0; is_split)
|
||||
continue;
|
||||
|
||||
const uint8_t facet_state = (code & 0b1100) == 0b1100 ? read_next_nibble() + 3 : code >> 2;
|
||||
assert(facet_state < this->used_states.size());
|
||||
if (facet_state >= this->used_states.size())
|
||||
continue;
|
||||
|
||||
this->used_states[facet_state] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Lightweight variant of deserialization, which only tests whether a face of test_state exists.
|
||||
bool TriangleSelector::has_facets(const TriangleSplittingData &data, const TriangleStateType test_state) {
|
||||
// Depth-first queue of a number of unvisited children.
|
||||
|
@ -228,15 +228,31 @@ public:
|
||||
std::vector<TriangleBitStreamMapping> triangles_to_split;
|
||||
// Bit stream containing splitting information.
|
||||
std::vector<bool> bitstream;
|
||||
// Array indicating which triangle state types are used (encoded inside bitstream).
|
||||
std::vector<bool> used_states { std::vector<bool>(static_cast<size_t>(TriangleStateType::Count), false) };
|
||||
|
||||
TriangleSplittingData() = default;
|
||||
|
||||
friend bool operator==(const TriangleSplittingData &lhs, const TriangleSplittingData &rhs) { return lhs.triangles_to_split == rhs.triangles_to_split && lhs.bitstream == rhs.bitstream; }
|
||||
friend bool operator==(const TriangleSplittingData &lhs, const TriangleSplittingData &rhs) {
|
||||
return lhs.triangles_to_split == rhs.triangles_to_split
|
||||
&& lhs.bitstream == rhs.bitstream
|
||||
&& lhs.used_states == rhs.used_states;
|
||||
}
|
||||
|
||||
friend bool operator!=(const TriangleSplittingData &lhs, const TriangleSplittingData &rhs) { return !(lhs == rhs); }
|
||||
|
||||
// Reset all used states before they are recomputed based on the bitstream.
|
||||
void reset_used_states() {
|
||||
used_states.resize(static_cast<size_t>(TriangleStateType::Count), false);
|
||||
std::fill(used_states.begin(), used_states.end(), false);
|
||||
}
|
||||
|
||||
// Update used states based on the bitstream. It just iterated over the bitstream from the bitstream_start_idx till the end.
|
||||
void update_used_states(size_t bitstream_start_idx);
|
||||
|
||||
private:
|
||||
friend class cereal::access;
|
||||
template<class Archive> void serialize(Archive &ar) { ar(triangles_to_split, bitstream); }
|
||||
template<class Archive> void serialize(Archive &ar) { ar(triangles_to_split, bitstream, used_states); }
|
||||
};
|
||||
|
||||
std::pair<std::vector<Vec3i>, std::vector<Vec3i>> precompute_all_neighbors() const;
|
||||
@ -302,6 +318,9 @@ public:
|
||||
// Load serialized data. Assumes that correct mesh is loaded.
|
||||
void deserialize(const TriangleSplittingData &data, bool needs_reset = true);
|
||||
|
||||
// Extract all used facet states from the given TriangleSplittingData.
|
||||
static std::vector<TriangleStateType> extract_used_facet_states(const TriangleSplittingData &data);
|
||||
|
||||
// For all triangles, remove the flag indicating that the triangle was selected by seed fill.
|
||||
void seed_fill_unselect_all_triangles();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user