Also remap filament region painting to new mesh when Repair/Simplify/Cut tools are used
This commit is contained in:
@@ -48,7 +48,8 @@ static void apply_tolerance(ModelVolume* vol)
|
||||
|
||||
static void apply_cut_texture_data(ModelVolume *volume, const SimplifyTextureDataSnapshot *source_snapshot, const Transform3d &cut_matrix)
|
||||
{
|
||||
if (volume == nullptr || source_snapshot == nullptr || source_snapshot->source == SimplifyColorSource::None ||
|
||||
if (volume == nullptr || source_snapshot == nullptr ||
|
||||
(source_snapshot->source == SimplifyColorSource::None && !source_snapshot->region_painting_present) ||
|
||||
volume->mesh().empty())
|
||||
return;
|
||||
|
||||
@@ -66,6 +67,9 @@ static bool volume_has_remappable_color_data(const ModelVolume &volume)
|
||||
if (its.vertices.empty() || its.indices.empty())
|
||||
return false;
|
||||
|
||||
if (model_volume_region_painting_needs_remap(volume))
|
||||
return true;
|
||||
|
||||
if (!volume.texture_mapping_color_facets.empty())
|
||||
return true;
|
||||
|
||||
@@ -481,9 +485,12 @@ static void merge_solid_parts_inside_object(ModelObjectPtrs& objects)
|
||||
}
|
||||
|
||||
TriangleMesh mesh;
|
||||
const ModelVolume *merged_volume_source = nullptr;
|
||||
// Merge all SolidPart but not Connectors
|
||||
for (const ModelVolume* mv : mo->volumes) {
|
||||
if (mv->is_model_part() && !mv->is_cut_connector()) {
|
||||
if (merged_volume_source == nullptr)
|
||||
merged_volume_source = mv;
|
||||
TriangleMesh m = mv->mesh();
|
||||
m.transform(mv->get_matrix());
|
||||
mesh.merge(m);
|
||||
@@ -492,6 +499,14 @@ static void merge_solid_parts_inside_object(ModelObjectPtrs& objects)
|
||||
if (!mesh.empty()) {
|
||||
ModelVolume* new_volume = mo->add_volume(mesh);
|
||||
new_volume->name = mo->name;
|
||||
if (merged_volume_source != nullptr) {
|
||||
new_volume->config.assign_config(merged_volume_source->config);
|
||||
if (ModelMaterial *material = merged_volume_source->material())
|
||||
new_volume->set_material(merged_volume_source->material_id(), *material);
|
||||
else
|
||||
new_volume->set_material_id(merged_volume_source->material_id());
|
||||
new_volume->cut_info = merged_volume_source->cut_info;
|
||||
}
|
||||
// Delete all merged SolidPart but not Connectors
|
||||
for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) {
|
||||
const ModelVolume* mv = mo->volumes[i];
|
||||
|
||||
@@ -47,6 +47,11 @@ struct RgbaFacetLookup
|
||||
std::unordered_map<int, std::vector<size_t>> by_triangle;
|
||||
};
|
||||
|
||||
struct RegionFacetLookup
|
||||
{
|
||||
std::vector<std::unordered_map<int, std::vector<size_t>>> by_state_and_triangle;
|
||||
};
|
||||
|
||||
struct FastTriangleProjection
|
||||
{
|
||||
bool enabled = false;
|
||||
@@ -86,6 +91,18 @@ static ColorRGBA unpack_rgba(uint32_t packed)
|
||||
float(packed & 0xFFu) / 255.f);
|
||||
}
|
||||
|
||||
static unsigned int effective_region_state(unsigned int state, unsigned int base_filament_id)
|
||||
{
|
||||
if (state == 0 || state == base_filament_id || state > unsigned(EnforcerBlockerType::ExtruderMax))
|
||||
return 0;
|
||||
return state;
|
||||
}
|
||||
|
||||
static unsigned int volume_base_region_filament_id(const ModelVolume &volume)
|
||||
{
|
||||
return unsigned(std::max(volume.extruder_id(), 1));
|
||||
}
|
||||
|
||||
static bool valid_triangle_vertices(const indexed_triangle_set &its, size_t tri_idx, std::array<Vec3f, 3> &vertices)
|
||||
{
|
||||
if (tri_idx >= its.indices.size())
|
||||
@@ -407,6 +424,19 @@ static bool volume_has_valid_image_texture(const ModelVolume &volume)
|
||||
return valid_rgba || volume_has_valid_raw_atlas(volume);
|
||||
}
|
||||
|
||||
static bool region_painting_data_needs_remap(const ModelVolume &volume)
|
||||
{
|
||||
if (volume.mmu_segmentation_facets.empty())
|
||||
return false;
|
||||
|
||||
const unsigned int base_filament_id = volume_base_region_filament_id(volume);
|
||||
const auto &used_states = volume.mmu_segmentation_facets.get_data().used_states;
|
||||
for (size_t state_idx = 0; state_idx < used_states.size(); ++state_idx)
|
||||
if (used_states[state_idx] && effective_region_state(unsigned(state_idx), base_filament_id) != 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::array<Vec2f, 3> source_triangle_uvs(const SimplifyTextureDataSnapshot &snapshot, size_t tri_idx, bool *valid = nullptr)
|
||||
{
|
||||
std::array<Vec2f, 3> uvs = { Vec2f::Zero(), Vec2f::Zero(), Vec2f::Zero() };
|
||||
@@ -779,6 +809,20 @@ static RgbaFacetLookup make_rgba_facet_lookup(const SimplifyTextureDataSnapshot
|
||||
return lookup;
|
||||
}
|
||||
|
||||
static RegionFacetLookup make_region_facet_lookup(const SimplifyTextureDataSnapshot &snapshot)
|
||||
{
|
||||
RegionFacetLookup lookup;
|
||||
lookup.by_state_and_triangle.resize(snapshot.region_facets_per_type.size());
|
||||
for (size_t state_idx = 0; state_idx < snapshot.region_facets_per_type.size(); ++state_idx) {
|
||||
const auto &facets = snapshot.region_facets_per_type[state_idx];
|
||||
auto &by_triangle = lookup.by_state_and_triangle[state_idx];
|
||||
by_triangle.reserve(facets.size());
|
||||
for (size_t facet_idx = 0; facet_idx < facets.size(); ++facet_idx)
|
||||
by_triangle[facets[facet_idx].source_triangle].emplace_back(facet_idx);
|
||||
}
|
||||
return lookup;
|
||||
}
|
||||
|
||||
static ColorRGBA rgb_metadata_background_color(const std::string &metadata)
|
||||
{
|
||||
const std::string key = "\"background_color\":\"#";
|
||||
@@ -843,6 +887,45 @@ static ColorRGBA sample_rgba_facets_at_source(const SimplifyTextureDataSnapshot
|
||||
return unpack_rgba(best_rgba);
|
||||
}
|
||||
|
||||
static unsigned int sample_region_state_at_source(const SimplifyTextureDataSnapshot &snapshot,
|
||||
const RegionFacetLookup &lookup,
|
||||
size_t source_triangle,
|
||||
const Vec3f &point)
|
||||
{
|
||||
float best_distance = std::numeric_limits<float>::max();
|
||||
unsigned int best_state = 0;
|
||||
bool found_candidate = false;
|
||||
for (size_t state_idx = 0; state_idx < snapshot.region_facets_per_type.size(); ++state_idx) {
|
||||
if (state_idx >= lookup.by_state_and_triangle.size())
|
||||
continue;
|
||||
const auto range_it = lookup.by_state_and_triangle[state_idx].find(int(source_triangle));
|
||||
if (range_it == lookup.by_state_and_triangle[state_idx].end())
|
||||
continue;
|
||||
|
||||
const auto &facets = snapshot.region_facets_per_type[state_idx];
|
||||
for (const size_t facet_idx : range_it->second) {
|
||||
if (facet_idx >= facets.size())
|
||||
continue;
|
||||
const TriangleSelector::FacetStateTriangle &facet = facets[facet_idx];
|
||||
const Vec3f bary = barycentric_weights_3d(point, facet.vertices);
|
||||
const float min_weight = std::min({ bary.x(), bary.y(), bary.z() });
|
||||
const unsigned int state = effective_region_state(unsigned(state_idx), unsigned(snapshot.region_base_filament_id));
|
||||
if (min_weight >= -1e-4f)
|
||||
return state;
|
||||
|
||||
const Vec3f safe = normalized_nonnegative_barycentric(bary);
|
||||
const Vec3f closest = facet.vertices[0] * safe.x() + facet.vertices[1] * safe.y() + facet.vertices[2] * safe.z();
|
||||
const float distance = (closest - point).squaredNorm();
|
||||
if (distance < best_distance) {
|
||||
best_distance = distance;
|
||||
best_state = state;
|
||||
found_candidate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found_candidate ? best_state : 0;
|
||||
}
|
||||
|
||||
static ColorRGBA sample_vertex_color_at_source(const SimplifyTextureDataSnapshot &snapshot, size_t source_triangle, const Vec3f &barycentric)
|
||||
{
|
||||
if (source_triangle >= snapshot.source_mesh.indices.size())
|
||||
@@ -914,6 +997,83 @@ static int texture_mapping_depth_from_span(float span, float target_span, int ma
|
||||
return std::clamp(int(std::ceil(std::log2(span / target_span))), 0, max_depth);
|
||||
}
|
||||
|
||||
static void region_append_nibble(std::vector<bool> &bits, unsigned int code)
|
||||
{
|
||||
for (int bit = 0; bit < 4; ++bit)
|
||||
bits.emplace_back((code & (1u << bit)) != 0u);
|
||||
}
|
||||
|
||||
static void region_append_leaf(TriangleSelector::TriangleSplittingData &data, unsigned int state)
|
||||
{
|
||||
state = std::min<unsigned int>(state, unsigned(EnforcerBlockerType::ExtruderMax));
|
||||
if (state < data.used_states.size())
|
||||
data.used_states[state] = true;
|
||||
|
||||
if (state >= 3u) {
|
||||
region_append_nibble(data.bitstream, 0b1100u);
|
||||
state -= 3u;
|
||||
while (state >= 15u) {
|
||||
region_append_nibble(data.bitstream, 0b1111u);
|
||||
state -= 15u;
|
||||
}
|
||||
region_append_nibble(data.bitstream, state);
|
||||
} else {
|
||||
region_append_nibble(data.bitstream, state << 2);
|
||||
}
|
||||
}
|
||||
|
||||
static std::array<std::array<Vec3f, 3>, 4> region_split_triangle(const std::array<Vec3f, 3> &vertices)
|
||||
{
|
||||
const Vec3f ab = 0.5f * (vertices[0] + vertices[1]);
|
||||
const Vec3f bc = 0.5f * (vertices[1] + vertices[2]);
|
||||
const Vec3f ca = 0.5f * (vertices[2] + vertices[0]);
|
||||
return { std::array<Vec3f, 3>{ vertices[0], ab, ca },
|
||||
std::array<Vec3f, 3>{ ab, vertices[1], bc },
|
||||
std::array<Vec3f, 3>{ bc, vertices[2], ca },
|
||||
std::array<Vec3f, 3>{ ab, bc, ca } };
|
||||
}
|
||||
|
||||
using RegionPaintingSampler = std::function<unsigned int(size_t, const Vec3f &, const Vec3f &)>;
|
||||
|
||||
static bool region_append_sampled_triangle(TriangleSelector::TriangleSplittingData &data,
|
||||
const RegionPaintingSampler &sampler,
|
||||
size_t target_triangle,
|
||||
const std::array<Vec3f, 3> &vertices,
|
||||
const std::array<Vec3f, 3> &barycentrics,
|
||||
int depth,
|
||||
int min_depth,
|
||||
int max_depth)
|
||||
{
|
||||
const Vec3f centroid = (vertices[0] + vertices[1] + vertices[2]) / 3.f;
|
||||
const Vec3f centroid_bary = (barycentrics[0] + barycentrics[1] + barycentrics[2]) / 3.f;
|
||||
const unsigned int s0 = sampler(target_triangle, vertices[0], barycentrics[0]);
|
||||
const unsigned int s1 = sampler(target_triangle, vertices[1], barycentrics[1]);
|
||||
const unsigned int s2 = sampler(target_triangle, vertices[2], barycentrics[2]);
|
||||
const unsigned int sc = sampler(target_triangle, centroid, centroid_bary);
|
||||
const bool states_differ = s0 != s1 || s1 != s2 || s2 != sc;
|
||||
|
||||
if (depth < max_depth && (depth < min_depth || states_differ)) {
|
||||
region_append_nibble(data.bitstream, 3u);
|
||||
const std::array<std::array<Vec3f, 3>, 4> child_vertices = region_split_triangle(vertices);
|
||||
const std::array<std::array<Vec3f, 3>, 4> child_barycentrics = region_split_triangle(barycentrics);
|
||||
bool has_non_base = false;
|
||||
for (int child_idx = 3; child_idx >= 0; --child_idx) {
|
||||
has_non_base |= region_append_sampled_triangle(data,
|
||||
sampler,
|
||||
target_triangle,
|
||||
child_vertices[size_t(child_idx)],
|
||||
child_barycentrics[size_t(child_idx)],
|
||||
depth + 1,
|
||||
min_depth,
|
||||
max_depth);
|
||||
}
|
||||
return has_non_base;
|
||||
}
|
||||
|
||||
region_append_leaf(data, sc);
|
||||
return sc != 0;
|
||||
}
|
||||
|
||||
static SimplifyTextureDataResult remap_rgba_from_snapshot(const SimplifyTextureDataSnapshot &snapshot,
|
||||
const indexed_triangle_set &simplified_mesh,
|
||||
const SimplifyTextureCancelFn &throw_on_cancel,
|
||||
@@ -996,6 +1156,110 @@ static SimplifyTextureDataResult remap_rgba_from_snapshot(const SimplifyTextureD
|
||||
return result;
|
||||
}
|
||||
|
||||
static void remap_region_painting_from_snapshot(const SimplifyTextureDataSnapshot &snapshot,
|
||||
const indexed_triangle_set &simplified_mesh,
|
||||
const SimplifyTextureCancelFn &throw_on_cancel,
|
||||
const SimplifyTextureProgressFn &status_fn,
|
||||
SimplifyTextureDataResult &result)
|
||||
{
|
||||
result.region_painting_touched = snapshot.region_painting_present;
|
||||
if (!snapshot.region_painting_present) {
|
||||
set_progress(status_fn, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!snapshot.region_painting_transfer_needed) {
|
||||
set_progress(status_fn, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
if (simplified_mesh.indices.empty() || snapshot.source_mesh.indices.empty() || snapshot.region_facets_per_type.empty()) {
|
||||
result.region_painting_remap_failed = true;
|
||||
set_progress(status_fn, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
set_progress(status_fn, 0);
|
||||
AABBMesh source_aabb(snapshot.source_mesh);
|
||||
RegionFacetLookup region_lookup = make_region_facet_lookup(snapshot);
|
||||
std::vector<FastTriangleProjection> projection_cache(simplified_mesh.indices.size());
|
||||
std::vector<uint8_t> projection_cache_valid(simplified_mesh.indices.size(), 0);
|
||||
|
||||
const int max_depth = 4;
|
||||
const float target_span = std::max(mesh_max_axis_span(simplified_mesh) / 120.f, 0.3f);
|
||||
const std::array<Vec3f, 3> root_barycentrics = {
|
||||
Vec3f(1.f, 0.f, 0.f),
|
||||
Vec3f(0.f, 1.f, 0.f),
|
||||
Vec3f(0.f, 0.f, 1.f)
|
||||
};
|
||||
|
||||
RegionPaintingSampler sampler = [&](size_t target_triangle, const Vec3f &point, const Vec3f &target_barycentric) {
|
||||
size_t source_triangle = 0;
|
||||
Vec3f source_barycentric = Vec3f(1.f / 3.f, 1.f / 3.f, 1.f / 3.f);
|
||||
bool source_projected = false;
|
||||
if (target_triangle < projection_cache.size()) {
|
||||
if (projection_cache_valid[target_triangle] == 0) {
|
||||
std::array<Vec3f, 3> target_vertices;
|
||||
if (valid_triangle_vertices(simplified_mesh, target_triangle, target_vertices))
|
||||
projection_cache[target_triangle] = make_fast_triangle_projection(snapshot, source_aabb, target_vertices);
|
||||
projection_cache_valid[target_triangle] = 1;
|
||||
}
|
||||
source_projected = projected_source_from_fast_triangle(projection_cache[target_triangle],
|
||||
target_barycentric,
|
||||
source_triangle,
|
||||
source_barycentric);
|
||||
}
|
||||
if (!source_projected && !project_to_source(snapshot, source_aabb, point, source_triangle, source_barycentric))
|
||||
return 0u;
|
||||
|
||||
std::array<Vec3f, 3> source_vertices;
|
||||
if (!valid_triangle_vertices(snapshot.source_mesh, source_triangle, source_vertices))
|
||||
return 0u;
|
||||
const Vec3f source_point = source_vertices[0] * source_barycentric.x() +
|
||||
source_vertices[1] * source_barycentric.y() +
|
||||
source_vertices[2] * source_barycentric.z();
|
||||
return sample_region_state_at_source(snapshot, region_lookup, source_triangle, source_point);
|
||||
};
|
||||
|
||||
TriangleSelector::TriangleSplittingData data;
|
||||
data.triangles_to_split.reserve(simplified_mesh.indices.size());
|
||||
size_t sample_counter = 0;
|
||||
for (size_t tri_idx = 0; tri_idx < simplified_mesh.indices.size(); ++tri_idx) {
|
||||
if ((++sample_counter & 255u) == 0) {
|
||||
check_cancel(throw_on_cancel);
|
||||
set_progress(status_fn, int((uint64_t(tri_idx) * 100u) / std::max<size_t>(simplified_mesh.indices.size(), 1)));
|
||||
}
|
||||
|
||||
std::array<Vec3f, 3> vertices;
|
||||
if (!valid_triangle_vertices(simplified_mesh, tri_idx, vertices))
|
||||
continue;
|
||||
|
||||
const size_t bitstream_start = data.bitstream.size();
|
||||
const int min_depth = texture_mapping_depth_from_span(triangle_max_edge_length(vertices), target_span, max_depth);
|
||||
const bool has_non_base = region_append_sampled_triangle(data,
|
||||
sampler,
|
||||
tri_idx,
|
||||
vertices,
|
||||
root_barycentrics,
|
||||
0,
|
||||
min_depth,
|
||||
max_depth);
|
||||
if (has_non_base)
|
||||
data.triangles_to_split.emplace_back(int(tri_idx), int(bitstream_start));
|
||||
else
|
||||
data.bitstream.resize(bitstream_start);
|
||||
}
|
||||
|
||||
check_cancel(throw_on_cancel);
|
||||
set_progress(status_fn, 100);
|
||||
data.triangles_to_split.shrink_to_fit();
|
||||
data.bitstream.shrink_to_fit();
|
||||
if (!data.triangles_to_split.empty()) {
|
||||
result.region_painting_data = std::move(data);
|
||||
result.region_painting_valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool refresh_result_preview_from_raw(SimplifyTextureDataResult &result)
|
||||
{
|
||||
ImageMapRawFilamentOffsetAtlas atlas;
|
||||
@@ -1204,6 +1468,11 @@ static void touch_imported_texture(ModelVolume &volume)
|
||||
|
||||
} // namespace
|
||||
|
||||
bool model_volume_region_painting_needs_remap(const ModelVolume &volume)
|
||||
{
|
||||
return region_painting_data_needs_remap(volume);
|
||||
}
|
||||
|
||||
SimplifyTextureDataSnapshot snapshot_simplify_texture_data(const ModelVolume &volume)
|
||||
{
|
||||
SimplifyTextureDataSnapshot snapshot;
|
||||
@@ -1211,17 +1480,22 @@ SimplifyTextureDataSnapshot snapshot_simplify_texture_data(const ModelVolume &vo
|
||||
if (its.vertices.empty() || its.indices.empty())
|
||||
return snapshot;
|
||||
|
||||
bool found_color_source = false;
|
||||
if (!volume.texture_mapping_color_facets.empty()) {
|
||||
snapshot.source = SimplifyColorSource::RgbaData;
|
||||
snapshot.source_mesh = its;
|
||||
snapshot.rgba_metadata_json = volume.texture_mapping_color_facets.metadata_json();
|
||||
volume.texture_mapping_color_facets.get_facet_triangles(volume, snapshot.rgba_facets);
|
||||
if (!snapshot.rgba_facets.empty())
|
||||
return snapshot;
|
||||
snapshot = SimplifyTextureDataSnapshot();
|
||||
found_color_source = true;
|
||||
else {
|
||||
snapshot.source = SimplifyColorSource::None;
|
||||
snapshot.source_mesh = indexed_triangle_set();
|
||||
snapshot.rgba_metadata_json.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (volume_has_valid_image_texture(volume)) {
|
||||
if (!found_color_source && volume_has_valid_image_texture(volume)) {
|
||||
snapshot.source = SimplifyColorSource::ImageTexture;
|
||||
snapshot.source_mesh = its;
|
||||
snapshot.texture_rgba.assign(volume.imported_texture_rgba.begin(), volume.imported_texture_rgba.end());
|
||||
@@ -1235,21 +1509,45 @@ SimplifyTextureDataSnapshot snapshot_simplify_texture_data(const ModelVolume &vo
|
||||
snapshot.texture_raw_metadata_json = volume.imported_texture_raw_metadata_json;
|
||||
snapshot.uv_map_generator_version = volume.uv_map_generator_version;
|
||||
if (snapshot_has_valid_rgba_texture(snapshot) || snapshot_has_valid_raw_atlas(snapshot))
|
||||
return snapshot;
|
||||
snapshot = SimplifyTextureDataSnapshot();
|
||||
found_color_source = true;
|
||||
else {
|
||||
snapshot.source = SimplifyColorSource::None;
|
||||
snapshot.source_mesh = indexed_triangle_set();
|
||||
snapshot.texture_rgba.clear();
|
||||
snapshot.texture_uvs_per_face.clear();
|
||||
snapshot.texture_uv_valid.clear();
|
||||
snapshot.texture_raw_filament_offsets.clear();
|
||||
snapshot.texture_width = 0;
|
||||
snapshot.texture_height = 0;
|
||||
snapshot.texture_raw_channels = 0;
|
||||
snapshot.texture_raw_metadata_json.clear();
|
||||
snapshot.uv_map_generator_version = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!volume.imported_vertex_colors_rgba.empty() && volume.imported_vertex_colors_rgba.size() == its.vertices.size()) {
|
||||
if (!found_color_source &&
|
||||
!volume.imported_vertex_colors_rgba.empty() &&
|
||||
volume.imported_vertex_colors_rgba.size() == its.vertices.size()) {
|
||||
snapshot.source = SimplifyColorSource::VertexColors;
|
||||
snapshot.source_mesh = its;
|
||||
snapshot.vertex_colors_rgba.assign(volume.imported_vertex_colors_rgba.begin(), volume.imported_vertex_colors_rgba.end());
|
||||
found_color_source = true;
|
||||
}
|
||||
|
||||
snapshot.region_painting_present = !volume.mmu_segmentation_facets.empty();
|
||||
snapshot.region_base_filament_id = int(volume_base_region_filament_id(volume));
|
||||
snapshot.region_painting_transfer_needed = region_painting_data_needs_remap(volume);
|
||||
if (snapshot.region_painting_transfer_needed) {
|
||||
if (!found_color_source)
|
||||
snapshot.source_mesh = its;
|
||||
volume.mmu_segmentation_facets.get_facet_triangles(volume, snapshot.region_facets_per_type);
|
||||
}
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
void transform_simplify_texture_data_snapshot(SimplifyTextureDataSnapshot &snapshot, const Transform3d &transform)
|
||||
{
|
||||
if (snapshot.source == SimplifyColorSource::None)
|
||||
if (snapshot.source == SimplifyColorSource::None && !snapshot.region_painting_transfer_needed)
|
||||
return;
|
||||
|
||||
if (!snapshot.source_mesh.vertices.empty() && !snapshot.source_mesh.indices.empty()) {
|
||||
@@ -1263,50 +1561,93 @@ void transform_simplify_texture_data_snapshot(SimplifyTextureDataSnapshot &snaps
|
||||
for (Vec3f &vertex : facet.vertices)
|
||||
vertex = (transform * vertex.cast<double>()).cast<float>();
|
||||
}
|
||||
|
||||
for (auto &facets : snapshot.region_facets_per_type)
|
||||
for (TriangleSelector::FacetStateTriangle &facet : facets)
|
||||
for (Vec3f &vertex : facet.vertices)
|
||||
vertex = (transform * vertex.cast<double>()).cast<float>();
|
||||
}
|
||||
|
||||
SimplifyTextureDataResult remap_simplify_texture_data(const SimplifyTextureDataSnapshot &snapshot,
|
||||
const indexed_triangle_set &simplified_mesh,
|
||||
const SimplifyTextureCancelFn &throw_on_cancel,
|
||||
const SimplifyTextureProgressFn &status_fn)
|
||||
const SimplifyTextureProgressFn &status_fn,
|
||||
const SimplifyTextureDataRemapOptions &options)
|
||||
{
|
||||
if (snapshot.source == SimplifyColorSource::None || simplified_mesh.indices.empty()) {
|
||||
const bool has_color_data = snapshot.source != SimplifyColorSource::None;
|
||||
const bool remap_color = has_color_data && options.remap_color_data && !simplified_mesh.indices.empty();
|
||||
const bool remap_region = snapshot.region_painting_present &&
|
||||
options.remap_region_painting &&
|
||||
snapshot.region_painting_transfer_needed &&
|
||||
!simplified_mesh.indices.empty();
|
||||
|
||||
if (!remap_color && !remap_region) {
|
||||
set_progress(status_fn, 100);
|
||||
SimplifyTextureDataResult result;
|
||||
result.source = snapshot.source;
|
||||
result.source = options.remap_color_data ? snapshot.source : SimplifyColorSource::None;
|
||||
result.region_painting_touched = snapshot.region_painting_present;
|
||||
return result;
|
||||
}
|
||||
|
||||
switch (snapshot.source) {
|
||||
case SimplifyColorSource::RgbaData:
|
||||
return remap_rgba_from_snapshot(snapshot, simplified_mesh, throw_on_cancel, status_fn);
|
||||
case SimplifyColorSource::ImageTexture: {
|
||||
SimplifyTextureDataResult result = remap_image_texture_from_snapshot(snapshot, simplified_mesh, throw_on_cancel, status_fn);
|
||||
if (result.source == SimplifyColorSource::ImageTexture)
|
||||
return result;
|
||||
SimplifyTextureDataSnapshot fallback_snapshot = snapshot;
|
||||
limit_snapshot_texture_resolution(fallback_snapshot);
|
||||
if (!snapshot_has_valid_rgba_texture(fallback_snapshot) && snapshot_has_valid_raw_atlas(fallback_snapshot)) {
|
||||
ImageMapRawFilamentOffsetAtlas atlas;
|
||||
atlas.width = fallback_snapshot.texture_width;
|
||||
atlas.height = fallback_snapshot.texture_height;
|
||||
atlas.channels = fallback_snapshot.texture_raw_channels;
|
||||
atlas.offsets = fallback_snapshot.texture_raw_filament_offsets;
|
||||
atlas.metadata_json = fallback_snapshot.texture_raw_metadata_json;
|
||||
atlas.filaments = image_map_raw_filaments_from_metadata_json(atlas.metadata_json, atlas.channels);
|
||||
fallback_snapshot.texture_rgba = image_map_raw_filament_offset_preview_rgba(atlas);
|
||||
auto scaled_progress = [&status_fn](int offset, int span) {
|
||||
return [status_fn, offset, span](int percent) {
|
||||
set_progress(status_fn, offset + int(std::round(float(std::clamp(percent, 0, 100)) * float(span) / 100.f)));
|
||||
};
|
||||
};
|
||||
|
||||
const bool split_progress = remap_color && snapshot.region_painting_present;
|
||||
SimplifyTextureDataResult result;
|
||||
if (remap_color) {
|
||||
const SimplifyTextureProgressFn color_progress = split_progress ? scaled_progress(0, 80) : status_fn;
|
||||
switch (snapshot.source) {
|
||||
case SimplifyColorSource::RgbaData:
|
||||
result = remap_rgba_from_snapshot(snapshot, simplified_mesh, throw_on_cancel, color_progress);
|
||||
break;
|
||||
case SimplifyColorSource::ImageTexture: {
|
||||
result = remap_image_texture_from_snapshot(snapshot, simplified_mesh, throw_on_cancel, color_progress);
|
||||
if (result.source == SimplifyColorSource::ImageTexture)
|
||||
break;
|
||||
SimplifyTextureDataSnapshot fallback_snapshot = snapshot;
|
||||
limit_snapshot_texture_resolution(fallback_snapshot);
|
||||
if (!snapshot_has_valid_rgba_texture(fallback_snapshot) && snapshot_has_valid_raw_atlas(fallback_snapshot)) {
|
||||
ImageMapRawFilamentOffsetAtlas atlas;
|
||||
atlas.width = fallback_snapshot.texture_width;
|
||||
atlas.height = fallback_snapshot.texture_height;
|
||||
atlas.channels = fallback_snapshot.texture_raw_channels;
|
||||
atlas.offsets = fallback_snapshot.texture_raw_filament_offsets;
|
||||
atlas.metadata_json = fallback_snapshot.texture_raw_metadata_json;
|
||||
atlas.filaments = image_map_raw_filaments_from_metadata_json(atlas.metadata_json, atlas.channels);
|
||||
fallback_snapshot.texture_rgba = image_map_raw_filament_offset_preview_rgba(atlas);
|
||||
}
|
||||
result = remap_rgba_from_snapshot(fallback_snapshot, simplified_mesh, throw_on_cancel, color_progress);
|
||||
result.remap_failed = true;
|
||||
result.used_fallback_rgba = result.source == SimplifyColorSource::RgbaData && result.rgba_data != nullptr;
|
||||
break;
|
||||
}
|
||||
result = remap_rgba_from_snapshot(fallback_snapshot, simplified_mesh, throw_on_cancel, status_fn);
|
||||
result.remap_failed = true;
|
||||
result.used_fallback_rgba = result.source == SimplifyColorSource::RgbaData && result.rgba_data != nullptr;
|
||||
return result;
|
||||
case SimplifyColorSource::VertexColors:
|
||||
result = remap_vertex_colors_from_snapshot(snapshot, simplified_mesh, throw_on_cancel, color_progress);
|
||||
break;
|
||||
case SimplifyColorSource::None:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
result.source = SimplifyColorSource::None;
|
||||
}
|
||||
case SimplifyColorSource::VertexColors:
|
||||
return remap_vertex_colors_from_snapshot(snapshot, simplified_mesh, throw_on_cancel, status_fn);
|
||||
case SimplifyColorSource::None:
|
||||
break;
|
||||
|
||||
if (snapshot.region_painting_present) {
|
||||
SimplifyTextureProgressFn region_progress = status_fn;
|
||||
if (split_progress)
|
||||
region_progress = scaled_progress(80, 20);
|
||||
if (options.remap_region_painting)
|
||||
remap_region_painting_from_snapshot(snapshot, simplified_mesh, throw_on_cancel, region_progress, result);
|
||||
else {
|
||||
result.region_painting_touched = true;
|
||||
set_progress(region_progress, 100);
|
||||
}
|
||||
}
|
||||
return SimplifyTextureDataResult();
|
||||
|
||||
set_progress(status_fn, 100);
|
||||
return result;
|
||||
}
|
||||
|
||||
void apply_simplify_texture_data_result(ModelVolume &volume, SimplifyTextureDataResult &&result)
|
||||
@@ -1352,6 +1693,16 @@ void apply_simplify_texture_data_result(ModelVolume &volume, SimplifyTextureData
|
||||
touch_imported_texture(volume);
|
||||
break;
|
||||
}
|
||||
|
||||
if (result.region_painting_touched) {
|
||||
if (result.region_painting_valid && !result.region_painting_data.triangles_to_split.empty()) {
|
||||
TriangleSelector selector(volume.mesh());
|
||||
selector.deserialize(result.region_painting_data);
|
||||
volume.mmu_segmentation_facets.set(selector);
|
||||
} else {
|
||||
volume.mmu_segmentation_facets.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -38,6 +38,10 @@ struct SimplifyTextureDataSnapshot
|
||||
std::string texture_raw_metadata_json;
|
||||
int uv_map_generator_version { 0 };
|
||||
std::vector<uint32_t> vertex_colors_rgba;
|
||||
bool region_painting_present { false };
|
||||
bool region_painting_transfer_needed { false };
|
||||
int region_base_filament_id { 1 };
|
||||
std::vector<std::vector<TriangleSelector::FacetStateTriangle>> region_facets_per_type;
|
||||
};
|
||||
|
||||
struct SimplifyTextureDataResult
|
||||
@@ -56,11 +60,23 @@ struct SimplifyTextureDataResult
|
||||
std::string texture_raw_metadata_json;
|
||||
int uv_map_generator_version { 0 };
|
||||
std::vector<uint32_t> vertex_colors_rgba;
|
||||
bool region_painting_touched { false };
|
||||
bool region_painting_valid { false };
|
||||
bool region_painting_remap_failed { false };
|
||||
TriangleSelector::TriangleSplittingData region_painting_data;
|
||||
};
|
||||
|
||||
struct SimplifyTextureDataRemapOptions
|
||||
{
|
||||
bool remap_color_data { true };
|
||||
bool remap_region_painting { true };
|
||||
};
|
||||
|
||||
using SimplifyTextureCancelFn = std::function<void()>;
|
||||
using SimplifyTextureProgressFn = std::function<void(int)>;
|
||||
|
||||
bool model_volume_region_painting_needs_remap(const ModelVolume &volume);
|
||||
|
||||
SimplifyTextureDataSnapshot snapshot_simplify_texture_data(const ModelVolume &volume);
|
||||
|
||||
void transform_simplify_texture_data_snapshot(SimplifyTextureDataSnapshot &snapshot, const Transform3d &transform);
|
||||
@@ -68,7 +84,8 @@ void transform_simplify_texture_data_snapshot(SimplifyTextureDataSnapshot &snaps
|
||||
SimplifyTextureDataResult remap_simplify_texture_data(const SimplifyTextureDataSnapshot &snapshot,
|
||||
const indexed_triangle_set &simplified_mesh,
|
||||
const SimplifyTextureCancelFn &throw_on_cancel = {},
|
||||
const SimplifyTextureProgressFn &status_fn = {});
|
||||
const SimplifyTextureProgressFn &status_fn = {},
|
||||
const SimplifyTextureDataRemapOptions &options = SimplifyTextureDataRemapOptions());
|
||||
|
||||
void apply_simplify_texture_data_result(ModelVolume &volume, SimplifyTextureDataResult &&result);
|
||||
|
||||
|
||||
@@ -240,14 +240,12 @@ static std::vector<std::string> collect_texture_mapping_vertex_color_match_warni
|
||||
if (has_uv_texture_reference_but_no_image)
|
||||
return {
|
||||
L("Image Texture Mapping is used on this object and OBJ UVs were found, but the texture image could not be loaded. "
|
||||
"Texture color matching will be skipped for this object. "
|
||||
"(This importer path currently expects a PNG image texture.)")
|
||||
"Texture color matching will be skipped for this object.")
|
||||
};
|
||||
|
||||
return {
|
||||
L("Image Texture Mapping is used on this object, but no imported vertex colors or OBJ UV texture data were found. "
|
||||
"Texture color matching will be skipped for this object. "
|
||||
"(This importer path currently expects a PNG image texture.)")
|
||||
"Texture color matching will be skipped for this object.")
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6028,8 +6028,14 @@ void ObjectList::fix_through_cgal()
|
||||
color_remap_stats.remap_canceled |= stats.remap_canceled;
|
||||
color_remap_stats.remap_failed |= stats.remap_failed;
|
||||
color_remap_stats.used_fallback_rgba |= stats.used_fallback_rgba;
|
||||
color_remap_stats.had_region_painting |= stats.had_region_painting;
|
||||
color_remap_stats.region_remap_requested |= stats.region_remap_requested;
|
||||
color_remap_stats.region_remap_skipped |= stats.region_remap_skipped;
|
||||
color_remap_stats.region_remap_failed |= stats.region_remap_failed;
|
||||
color_remap_stats.volumes_remapped += stats.volumes_remapped;
|
||||
color_remap_stats.volumes_cleared += stats.volumes_cleared;
|
||||
color_remap_stats.region_volumes_remapped += stats.region_volumes_remapped;
|
||||
color_remap_stats.region_volumes_cleared += stats.region_volumes_cleared;
|
||||
};
|
||||
|
||||
std::vector<int> obj_idxs, vol_idxs;
|
||||
@@ -6165,6 +6171,12 @@ void ObjectList::fix_through_cgal()
|
||||
msg += "\n\n" + _L("Color remapping was skipped; stale color data was cleared from repaired parts.");
|
||||
if (color_remap_stats.remap_canceled && color_remap_stats.volumes_cleared > 0)
|
||||
msg += "\n\n" + _L("Color remapping was canceled; stale color data was cleared from repaired parts.");
|
||||
if (color_remap_stats.region_remap_failed && color_remap_stats.region_volumes_cleared > 0)
|
||||
msg += "\n\n" + _L("Some region painting could not be remapped after repair and was cleared.");
|
||||
if (color_remap_stats.region_remap_skipped && color_remap_stats.region_volumes_cleared > 0)
|
||||
msg += "\n\n" + _L("Region painting remapping was skipped; stale region painting was cleared from repaired parts.");
|
||||
if (color_remap_stats.region_remap_requested && color_remap_stats.remap_canceled && color_remap_stats.region_volumes_cleared > 0)
|
||||
msg += "\n\n" + _L("Region painting remapping was canceled; stale region painting was cleared from repaired parts.");
|
||||
if (msg.IsEmpty())
|
||||
msg = _L("Repairing was canceled");
|
||||
plater->get_notification_manager()->push_notification(NotificationType::CgalFinished, NotificationManager::NotificationLevel::PrintInfoShortNotificationLevel, into_u8(msg));
|
||||
|
||||
@@ -715,6 +715,21 @@ void GLGizmoAdvancedCut::perform_cut(const Selection& selection)
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||
_u8L("Color remapping was canceled; stale color data was cleared from repaired parts."));
|
||||
if (remap_stats.region_remap_failed && remap_stats.region_volumes_cleared > 0)
|
||||
plater->get_notification_manager()->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||
_u8L("Some region painting could not be remapped after repair and was cleared."));
|
||||
if (remap_stats.region_remap_skipped && remap_stats.region_volumes_cleared > 0)
|
||||
plater->get_notification_manager()->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||
_u8L("Region painting remapping was skipped; stale region painting was cleared from repaired parts."));
|
||||
if (remap_stats.region_remap_requested && remap_stats.remap_canceled && remap_stats.region_volumes_cleared > 0)
|
||||
plater->get_notification_manager()->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||
_u8L("Region painting remapping was canceled; stale region painting was cleared from repaired parts."));
|
||||
return !remap_stats.remap_canceled;
|
||||
};
|
||||
ProgressDialog progress_dlg(_L("Repairing model object"), "", 100, find_toplevel_parent(plater), wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT, true);
|
||||
|
||||
@@ -3395,6 +3395,21 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||
_u8L("Color remapping was canceled; stale color data was cleared from repaired parts."));
|
||||
if (remap_stats.region_remap_failed && remap_stats.region_volumes_cleared > 0)
|
||||
plater->get_notification_manager()->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||
_u8L("Some region painting could not be remapped after repair and was cleared."));
|
||||
if (remap_stats.region_remap_skipped && remap_stats.region_volumes_cleared > 0)
|
||||
plater->get_notification_manager()->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||
_u8L("Region painting remapping was skipped; stale region painting was cleared from repaired parts."));
|
||||
if (remap_stats.region_remap_requested && remap_stats.remap_canceled && remap_stats.region_volumes_cleared > 0)
|
||||
plater->get_notification_manager()->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||
_u8L("Region painting remapping was canceled; stale region painting was cleared from repaired parts."));
|
||||
return !remap_stats.remap_canceled;
|
||||
};
|
||||
ProgressDialog progress_dlg(_L("Repairing model object"), "", 100, find_toplevel_parent(plater), wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT, true);
|
||||
|
||||
@@ -195,6 +195,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
||||
|
||||
m_volume = act_volume;
|
||||
m_configuration.decimate_ratio = 50.; // default value
|
||||
m_configuration.remap_region_painting = true;
|
||||
m_configuration.fix_count_by_ratio(m_volume->mesh().its.indices.size());
|
||||
init_model(m_volume->mesh().its);
|
||||
|
||||
@@ -343,6 +344,10 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
||||
m_imgui->disabled_end(); // use_count
|
||||
|
||||
m_imgui->bbl_checkbox(_L("Show wireframe").c_str(), m_show_wireframe);
|
||||
if (model_volume_region_painting_needs_remap(*m_volume)) {
|
||||
if (m_imgui->bbl_checkbox(_L("Remap region painting to simplified mesh").c_str(), m_configuration.remap_region_painting))
|
||||
start_process = true;
|
||||
}
|
||||
const wxString skip_color_conversion_label = color_conversion_in_progress ?
|
||||
_L("Skip color conversion (in progress)") :
|
||||
_L("Skip color conversion");
|
||||
@@ -551,12 +556,14 @@ void GLGizmoSimplify::process()
|
||||
// Initialize.
|
||||
uint32_t triangle_count = 0;
|
||||
float max_error = std::numeric_limits<float>::max();
|
||||
bool remap_region_painting = true;
|
||||
{
|
||||
std::lock_guard lk(m_state_mutex);
|
||||
if (m_state.config.use_count)
|
||||
triangle_count = m_state.config.wanted_count;
|
||||
if (! m_state.config.use_count)
|
||||
max_error = m_state.config.max_error;
|
||||
remap_region_painting = m_state.config.remap_region_painting;
|
||||
m_state.progress = 0.f;
|
||||
m_state.result.reset();
|
||||
m_state.color_conversion_in_progress = false;
|
||||
@@ -574,12 +581,15 @@ void GLGizmoSimplify::process()
|
||||
set_color_conversion_in_progress(true);
|
||||
try {
|
||||
throw_on_color_conversion_stop();
|
||||
SimplifyTextureDataRemapOptions remap_options;
|
||||
remap_options.remap_region_painting = remap_region_painting;
|
||||
texture_result =
|
||||
remap_simplify_texture_data(texture_snapshot, *its, throw_on_color_conversion_stop, [&statusfn](int percent) {
|
||||
statusfn(90.f + float(std::clamp(percent, 0, 100)) * 0.1f);
|
||||
});
|
||||
}, remap_options);
|
||||
} catch (SimplifyColorConversionSkippedException &) {
|
||||
texture_result = SimplifyTextureDataResult();
|
||||
texture_result.region_painting_touched = texture_snapshot.region_painting_present;
|
||||
} catch (...) {
|
||||
set_color_conversion_in_progress(false);
|
||||
throw;
|
||||
|
||||
@@ -62,11 +62,13 @@ private:
|
||||
float decimate_ratio = 50.f; // in percent
|
||||
uint32_t wanted_count = 0; // initialize by percents
|
||||
float max_error = 1.; // maximal quadric error
|
||||
bool remap_region_painting = true;
|
||||
|
||||
void fix_count_by_ratio(size_t triangle_count);
|
||||
bool operator==(const Configuration& rhs) {
|
||||
return (use_count == rhs.use_count && decimate_ratio == rhs.decimate_ratio
|
||||
&& wanted_count == rhs.wanted_count && max_error == rhs.max_error);
|
||||
&& wanted_count == rhs.wanted_count && max_error == rhs.max_error
|
||||
&& remap_region_painting == rhs.remap_region_painting);
|
||||
}
|
||||
bool operator!=(const Configuration& rhs) {
|
||||
return ! (*this == rhs);
|
||||
|
||||
@@ -19331,14 +19331,13 @@ void Plater::clear_before_change_mesh(int obj_idx)
|
||||
{
|
||||
ModelObject* mo = model().objects[obj_idx];
|
||||
|
||||
// If there are custom supports/seams/mmu/fuzzy skin segmentation, remove them. Fixed mesh
|
||||
// If there are custom supports/seams/fuzzy skin segmentation, remove them. Fixed mesh
|
||||
// may be different and they would make no sense.
|
||||
bool paint_removed = false;
|
||||
for (ModelVolume* mv : mo->volumes) {
|
||||
paint_removed |= ! mv->supported_facets.empty() || ! mv->seam_facets.empty() || ! mv->mmu_segmentation_facets.empty() || !mv->fuzzy_skin_facets.empty();
|
||||
paint_removed |= ! mv->supported_facets.empty() || ! mv->seam_facets.empty() || !mv->fuzzy_skin_facets.empty();
|
||||
mv->supported_facets.reset();
|
||||
mv->seam_facets.reset();
|
||||
mv->mmu_segmentation_facets.reset();
|
||||
mv->fuzzy_skin_facets.reset();
|
||||
}
|
||||
if (paint_removed) {
|
||||
@@ -19346,7 +19345,7 @@ void Plater::clear_before_change_mesh(int obj_idx)
|
||||
get_notification_manager()->push_notification(
|
||||
NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
|
||||
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||
_u8L("Custom supports and color region painting were removed before repairing."));
|
||||
_u8L("Custom supports and seam painting were removed before repairing."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,11 @@ bool color_snapshot_is_valid(const SimplifyTextureDataSnapshot &snapshot)
|
||||
return snapshot.source != SimplifyColorSource::None;
|
||||
}
|
||||
|
||||
bool snapshot_has_mesh_bound_data(const SimplifyTextureDataSnapshot &snapshot)
|
||||
{
|
||||
return snapshot.source != SimplifyColorSource::None || snapshot.region_painting_present;
|
||||
}
|
||||
|
||||
bool volume_may_change_during_repair(const ModelVolume &volume, const ModelRepairOptions &options)
|
||||
{
|
||||
return options.weld_same_position_vertices ||
|
||||
@@ -130,6 +135,12 @@ bool volume_has_remappable_color_data(const ModelVolume &volume)
|
||||
return volume.imported_vertex_colors_rgba.size() == its.vertices.size();
|
||||
}
|
||||
|
||||
bool volume_has_transferable_region_painting(const ModelVolume &volume)
|
||||
{
|
||||
const indexed_triangle_set &its = volume.mesh().its;
|
||||
return !its.vertices.empty() && !its.indices.empty() && model_volume_region_painting_needs_remap(volume);
|
||||
}
|
||||
|
||||
bool target_has_remappable_color_data(const ModelObject &model_object, int volume_idx)
|
||||
{
|
||||
const size_t start_volume = volume_idx == -1 ? 0 : size_t(volume_idx);
|
||||
@@ -143,6 +154,19 @@ bool target_has_remappable_color_data(const ModelObject &model_object, int volum
|
||||
return false;
|
||||
}
|
||||
|
||||
bool target_has_transferable_region_painting(const ModelObject &model_object, int volume_idx)
|
||||
{
|
||||
const size_t start_volume = volume_idx == -1 ? 0 : size_t(volume_idx);
|
||||
const size_t end_volume =
|
||||
volume_idx == -1 ? model_object.volumes.size() : std::min(model_object.volumes.size(), size_t(volume_idx) + 1);
|
||||
for (size_t idx = start_volume; idx < end_volume; ++idx) {
|
||||
const ModelVolume *volume = model_object.volumes[idx];
|
||||
if (volume != nullptr && volume_has_transferable_region_painting(*volume))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ColorSnapshotByVolume collect_color_snapshots(ModelObject &model_object, int volume_idx, const ModelRepairOptions &options)
|
||||
{
|
||||
ColorSnapshotByVolume snapshots;
|
||||
@@ -154,7 +178,7 @@ ColorSnapshotByVolume collect_color_snapshots(ModelObject &model_object, int vol
|
||||
if (volume == nullptr || !volume_may_change_during_repair(*volume, options))
|
||||
continue;
|
||||
SimplifyTextureDataSnapshot snapshot = snapshot_simplify_texture_data(*volume);
|
||||
if (color_snapshot_is_valid(snapshot))
|
||||
if (snapshot_has_mesh_bound_data(snapshot))
|
||||
snapshots.emplace(volume, std::move(snapshot));
|
||||
}
|
||||
return snapshots;
|
||||
@@ -163,7 +187,9 @@ ColorSnapshotByVolume collect_color_snapshots(ModelObject &model_object, int vol
|
||||
bool ask_repair_options(GUI::ProgressDialog &progress_dialog,
|
||||
ModelRepairOptions &options,
|
||||
ModelRepairColorRemapChoice &color_remap_choice,
|
||||
bool show_color_remap_option)
|
||||
bool ®ion_painting_remap,
|
||||
bool show_color_remap_option,
|
||||
bool show_region_painting_remap_option)
|
||||
{
|
||||
wxDialog dialog(&progress_dialog, wxID_ANY, _L("Repair options"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
||||
auto *top_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
@@ -186,6 +212,13 @@ bool ask_repair_options(GUI::ProgressDialog &progress_dialog,
|
||||
top_sizer->Add(remap_colors_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 12);
|
||||
}
|
||||
|
||||
wxCheckBox *remap_region_painting_checkbox = nullptr;
|
||||
if (show_region_painting_remap_option) {
|
||||
remap_region_painting_checkbox = new wxCheckBox(&dialog, wxID_ANY, _L("Remap region painting to repaired mesh"));
|
||||
remap_region_painting_checkbox->SetValue(region_painting_remap);
|
||||
top_sizer->Add(remap_region_painting_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 12);
|
||||
}
|
||||
|
||||
wxSizer *buttons = dialog.CreateSeparatedButtonSizer(wxOK | wxCANCEL);
|
||||
if (buttons != nullptr)
|
||||
top_sizer->Add(buttons, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 12);
|
||||
@@ -200,6 +233,8 @@ bool ask_repair_options(GUI::ProgressDialog &progress_dialog,
|
||||
options.split_before_repair = split_checkbox->GetValue();
|
||||
if (remap_colors_checkbox != nullptr)
|
||||
color_remap_choice = remap_colors_checkbox->GetValue() ? ModelRepairColorRemapChoice::Remap : ModelRepairColorRemapChoice::Skip;
|
||||
if (remap_region_painting_checkbox != nullptr)
|
||||
region_painting_remap = remap_region_painting_checkbox->GetValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -248,14 +283,25 @@ void merge_color_remap_stats(ModelRepairColorRemapStats &dst, const ModelRepairC
|
||||
dst.remap_canceled |= src.remap_canceled;
|
||||
dst.remap_failed |= src.remap_failed;
|
||||
dst.used_fallback_rgba |= src.used_fallback_rgba;
|
||||
dst.had_region_painting |= src.had_region_painting;
|
||||
dst.region_remap_requested |= src.region_remap_requested;
|
||||
dst.region_remap_skipped |= src.region_remap_skipped;
|
||||
dst.region_remap_failed |= src.region_remap_failed;
|
||||
dst.volumes_remapped += src.volumes_remapped;
|
||||
dst.volumes_cleared += src.volumes_cleared;
|
||||
dst.region_volumes_remapped += src.region_volumes_remapped;
|
||||
dst.region_volumes_cleared += src.region_volumes_cleared;
|
||||
}
|
||||
|
||||
void clear_remappable_color_data(ModelVolume &volume, ModelRepairColorRemapStats &stats)
|
||||
void clear_remappable_data(ModelVolume &volume, const SimplifyTextureDataSnapshot &snapshot, ModelRepairColorRemapStats &stats)
|
||||
{
|
||||
apply_simplify_texture_data_result(volume, SimplifyTextureDataResult());
|
||||
++stats.volumes_cleared;
|
||||
SimplifyTextureDataResult result;
|
||||
result.region_painting_touched = snapshot.region_painting_present;
|
||||
apply_simplify_texture_data_result(volume, std::move(result));
|
||||
if (color_snapshot_is_valid(snapshot))
|
||||
++stats.volumes_cleared;
|
||||
if (snapshot.region_painting_present)
|
||||
++stats.region_volumes_cleared;
|
||||
}
|
||||
|
||||
bool result_preserved_color(const SimplifyTextureDataSnapshot &snapshot, const SimplifyTextureDataResult &result)
|
||||
@@ -276,21 +322,24 @@ bool result_preserved_color(const SimplifyTextureDataSnapshot &snapshot, const S
|
||||
|
||||
bool remap_or_clear_color_data(ModelVolume &volume,
|
||||
const SimplifyTextureDataSnapshot &snapshot,
|
||||
bool remap_requested,
|
||||
bool remap_color_requested,
|
||||
bool remap_region_painting_requested,
|
||||
std::atomic<bool> &cancel_requested,
|
||||
const std::function<void(int)> &status_fn,
|
||||
ModelRepairColorRemapStats &stats)
|
||||
{
|
||||
if (!color_snapshot_is_valid(snapshot))
|
||||
if (!snapshot_has_mesh_bound_data(snapshot))
|
||||
return false;
|
||||
|
||||
if (!remap_requested) {
|
||||
if (color_snapshot_is_valid(snapshot) && !remap_color_requested)
|
||||
stats.remap_skipped = true;
|
||||
clear_remappable_color_data(volume, stats);
|
||||
return false;
|
||||
}
|
||||
if (snapshot.region_painting_transfer_needed && !remap_region_painting_requested)
|
||||
stats.region_remap_skipped = true;
|
||||
|
||||
try {
|
||||
SimplifyTextureDataRemapOptions options;
|
||||
options.remap_color_data = remap_color_requested;
|
||||
options.remap_region_painting = remap_region_painting_requested;
|
||||
SimplifyTextureDataResult result = remap_simplify_texture_data(
|
||||
snapshot,
|
||||
volume.mesh().its,
|
||||
@@ -298,25 +347,42 @@ bool remap_or_clear_color_data(ModelVolume &volume,
|
||||
if (cancel_requested)
|
||||
throw ColorRemapCanceledException();
|
||||
},
|
||||
status_fn);
|
||||
status_fn,
|
||||
options);
|
||||
|
||||
stats.used_fallback_rgba |= result.used_fallback_rgba;
|
||||
stats.remap_failed |= result.remap_failed && !result.used_fallback_rgba;
|
||||
stats.region_remap_failed |= result.region_painting_remap_failed;
|
||||
const bool preserved = result_preserved_color(snapshot, result);
|
||||
const bool region_preserved = result.region_painting_touched && result.region_painting_valid;
|
||||
apply_simplify_texture_data_result(volume, std::move(result));
|
||||
if (preserved) {
|
||||
++stats.volumes_remapped;
|
||||
if (color_snapshot_is_valid(snapshot)) {
|
||||
if (preserved)
|
||||
++stats.volumes_remapped;
|
||||
else if (remap_color_requested) {
|
||||
stats.remap_failed = true;
|
||||
++stats.volumes_cleared;
|
||||
} else {
|
||||
++stats.volumes_cleared;
|
||||
}
|
||||
}
|
||||
if (snapshot.region_painting_present) {
|
||||
if (region_preserved)
|
||||
++stats.region_volumes_remapped;
|
||||
else
|
||||
++stats.region_volumes_cleared;
|
||||
}
|
||||
if (preserved || region_preserved) {
|
||||
return true;
|
||||
} else {
|
||||
stats.remap_failed = true;
|
||||
++stats.volumes_cleared;
|
||||
}
|
||||
} catch (ColorRemapCanceledException &) {
|
||||
stats.remap_canceled = true;
|
||||
throw;
|
||||
} catch (std::exception &) {
|
||||
stats.remap_failed = true;
|
||||
clear_remappable_color_data(volume, stats);
|
||||
if (snapshot.region_painting_transfer_needed)
|
||||
stats.region_remap_failed = true;
|
||||
clear_remappable_data(volume, snapshot, stats);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -365,10 +431,13 @@ bool fix_model_with_cgal_gui(ModelObject &model_object,
|
||||
|
||||
if (!active_prompt_state.repair_options_selected) {
|
||||
const bool show_color_remap_option = target_has_remappable_color_data(model_object, volume_idx);
|
||||
const bool show_region_painting_remap_option = target_has_transferable_region_painting(model_object, volume_idx);
|
||||
if (!ask_repair_options(progress_dialog,
|
||||
active_prompt_state.repair_options,
|
||||
active_prompt_state.color_remap_choice,
|
||||
show_color_remap_option)) {
|
||||
active_prompt_state.region_painting_remap,
|
||||
show_color_remap_option,
|
||||
show_region_painting_remap_option)) {
|
||||
active_prompt_state.repair_options_canceled = true;
|
||||
return false;
|
||||
}
|
||||
@@ -379,15 +448,23 @@ bool fix_model_with_cgal_gui(ModelObject &model_object,
|
||||
|
||||
ColorSnapshotByVolume color_snapshots = collect_color_snapshots(model_object, volume_idx, repair_options);
|
||||
ModelRepairColorRemapChoice color_remap_choice = active_prompt_state.color_remap_choice;
|
||||
const bool region_painting_remap_requested = active_prompt_state.region_painting_remap;
|
||||
if (!color_snapshots.empty()) {
|
||||
local_color_stats.had_color_data = true;
|
||||
bool had_transferable_region_painting = false;
|
||||
for (const auto &snapshot_pair : color_snapshots) {
|
||||
local_color_stats.had_color_data |= color_snapshot_is_valid(snapshot_pair.second);
|
||||
local_color_stats.had_region_painting |= snapshot_pair.second.region_painting_present;
|
||||
had_transferable_region_painting |= snapshot_pair.second.region_painting_transfer_needed;
|
||||
}
|
||||
if (color_remap_choice == ModelRepairColorRemapChoice::Cancel) {
|
||||
if (color_remap_stats)
|
||||
merge_color_remap_stats(*color_remap_stats, local_color_stats);
|
||||
return false;
|
||||
}
|
||||
local_color_stats.remap_requested = color_remap_choice == ModelRepairColorRemapChoice::Remap;
|
||||
local_color_stats.remap_skipped = color_remap_choice == ModelRepairColorRemapChoice::Skip;
|
||||
local_color_stats.remap_requested = local_color_stats.had_color_data && color_remap_choice == ModelRepairColorRemapChoice::Remap;
|
||||
local_color_stats.remap_skipped = local_color_stats.had_color_data && color_remap_choice == ModelRepairColorRemapChoice::Skip;
|
||||
local_color_stats.region_remap_requested = had_transferable_region_painting && region_painting_remap_requested;
|
||||
local_color_stats.region_remap_skipped = had_transferable_region_painting && !region_painting_remap_requested;
|
||||
}
|
||||
|
||||
// Orca: Synchronization primitives for progress updates between worker thread and GUI.
|
||||
@@ -407,6 +484,7 @@ bool fix_model_with_cgal_gui(ModelObject &model_object,
|
||||
bool success = false;
|
||||
size_t ivolume = 0;
|
||||
const bool remap_requested = color_remap_choice == ModelRepairColorRemapChoice::Remap;
|
||||
const bool remap_region_painting_requested = region_painting_remap_requested;
|
||||
|
||||
// Orca: Lambda for updating progress from worker thread.
|
||||
auto on_progress = [&mtx, &condition, &ivolume, &model_object, &progress](RepairProgressStage stage, const char *msg, unsigned prcnt) {
|
||||
@@ -445,6 +523,7 @@ bool fix_model_with_cgal_gui(ModelObject &model_object,
|
||||
&fix_result,
|
||||
&local_color_stats,
|
||||
remap_requested,
|
||||
remap_region_painting_requested,
|
||||
repair_options,
|
||||
color_snapshots = std::move(color_snapshots)]() {
|
||||
try {
|
||||
@@ -518,12 +597,18 @@ bool fix_model_with_cgal_gui(ModelObject &model_object,
|
||||
else if (pre_split_weld_changed && color_snapshot != nullptr && !color_part_states.empty())
|
||||
color_part_states.front() = ColorRemapPartState::Pending;
|
||||
|
||||
auto clear_pending_color_parts = [&model_object, &local_color_stats, ivolume, part_end, &color_part_states]() {
|
||||
auto clear_pending_color_parts = [&model_object,
|
||||
&local_color_stats,
|
||||
ivolume,
|
||||
part_end,
|
||||
&color_part_states,
|
||||
color_snapshot]() {
|
||||
for (size_t part_idx = ivolume; part_idx <= part_end && part_idx < model_object.volumes.size(); ++part_idx) {
|
||||
ColorRemapPartState &state = color_part_states[part_idx - ivolume];
|
||||
if (state != ColorRemapPartState::Pending)
|
||||
continue;
|
||||
clear_remappable_color_data(*model_object.volumes[part_idx], local_color_stats);
|
||||
if (color_snapshot != nullptr)
|
||||
clear_remappable_data(*model_object.volumes[part_idx], *color_snapshot, local_color_stats);
|
||||
state = ColorRemapPartState::Cleared;
|
||||
}
|
||||
};
|
||||
@@ -557,6 +642,7 @@ bool fix_model_with_cgal_gui(ModelObject &model_object,
|
||||
*part_volume,
|
||||
*color_snapshot,
|
||||
remap_requested,
|
||||
remap_region_painting_requested,
|
||||
cancel_requested,
|
||||
[on_progress](int percent) {
|
||||
const int clamped_percent = std::clamp(percent, 0, 100);
|
||||
|
||||
@@ -19,8 +19,14 @@ struct ModelRepairColorRemapStats
|
||||
bool remap_canceled = false;
|
||||
bool remap_failed = false;
|
||||
bool used_fallback_rgba = false;
|
||||
bool had_region_painting = false;
|
||||
bool region_remap_requested = false;
|
||||
bool region_remap_skipped = false;
|
||||
bool region_remap_failed = false;
|
||||
size_t volumes_remapped = 0;
|
||||
size_t volumes_cleared = 0;
|
||||
size_t region_volumes_remapped = 0;
|
||||
size_t region_volumes_cleared = 0;
|
||||
};
|
||||
|
||||
struct ModelRepairOptions
|
||||
@@ -42,6 +48,7 @@ struct ModelRepairPromptState
|
||||
bool repair_options_canceled = false;
|
||||
ModelRepairOptions repair_options;
|
||||
ModelRepairColorRemapChoice color_remap_choice = ModelRepairColorRemapChoice::Remap;
|
||||
bool region_painting_remap = true;
|
||||
};
|
||||
|
||||
// Return false if fixing was canceled. fix_result is empty on success.
|
||||
|
||||
Reference in New Issue
Block a user