5050041d8 Paint texture mapping regions automatically when using image projection mode. Work on UV map auto-generation

This commit is contained in:
sentientstardust
2026-05-02 14:58:47 +01:00
parent 6df4c7f16a
commit a9dc1858b1
8 changed files with 1409 additions and 163 deletions

View File

@@ -6789,6 +6789,60 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode(
samples.push_back({ x_mm, y_mm, rgba, sample_weight });
};
auto accumulate_constant_surface_triangle_samples = [&](const Vec3d &p0,
const Vec3d &p1,
const Vec3d &p2,
const std::array<float, 4> &rgba) {
const float max_world_edge_mm = std::max({
float((p1 - p0).norm()),
float((p2 - p1).norm()),
float((p0 - p2).norm())
});
if (!std::isfinite(max_world_edge_mm))
return;
const double tri_area_mm2 = 0.5 * ((p1 - p0).cross(p2 - p0)).norm();
if (!std::isfinite(tri_area_mm2))
return;
const float world_sample_pitch_mm = high_resolution_texture_sampling ? 0.08f : 0.16f;
const int max_bary_steps = high_resolution_texture_sampling ? 80 : 40;
const int bary_steps = std::clamp(int(std::ceil(max_world_edge_mm / world_sample_pitch_mm)), 1, max_bary_steps);
const int sample_count = bary_steps * (bary_steps + 1) / 2;
if (sample_count <= 0)
return;
const float area_weight = std::max(0.05f, float(tri_area_mm2)) / float(sample_count);
if (!std::isfinite(area_weight))
return;
const float inv_steps = 1.f / float(bary_steps);
for (int i = 0; i < bary_steps; ++i) {
for (int j = 0; j < (bary_steps - i); ++j) {
const float b1 = (float(i) + 0.33333334f) * inv_steps;
const float b2 = (float(j) + 0.33333334f) * inv_steps;
const float b0 = 1.f - b1 - b2;
if (b0 < 0.f)
continue;
const Vec3d world_pos = p0 * double(b0) + p1 * double(b1) + p2 * double(b2);
float sample_weight = area_weight;
if (use_layer_weighting) {
const float dz = std::abs(float(world_pos.z()) - layer_z_mm);
const float z_norm = dz / safe_layer_z_falloff_mm;
const float z_weight = std::exp(-0.5f * z_norm * z_norm);
if (!std::isfinite(z_weight))
continue;
sample_weight *= z_weight;
}
if (sample_weight <= EPSILON)
continue;
accumulate_sample(float(world_pos.x()), float(world_pos.y()), rgba, sample_weight);
}
}
};
const Transform3d object_trafo = print_object.trafo_centered();
for (const ModelVolume *volume : model_object->volumes) {
if (volume == nullptr)
@@ -6811,26 +6865,10 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode(
if (!p0.allFinite() || !p1.allFinite() || !p2.allFinite())
continue;
const Vec3d world_pos = (p0 + p1 + p2) / 3.0;
std::array<float, 4> rgba = unpack_rgba_u32(facet.rgba);
rgba[3] = 1.f;
const double tri_area_mm2 = 0.5 * ((p1 - p0).cross(p2 - p0)).norm();
if (!std::isfinite(tri_area_mm2))
continue;
float sample_weight = std::max(0.05f, float(tri_area_mm2));
if (use_layer_weighting) {
const float dz = std::abs(float(world_pos.z()) - layer_z_mm);
const float z_norm = dz / safe_layer_z_falloff_mm;
const float z_weight = std::exp(-0.5f * z_norm * z_norm);
if (!std::isfinite(z_weight))
continue;
sample_weight *= z_weight;
}
if (sample_weight <= EPSILON)
continue;
accumulate_sample(float(world_pos.x()), float(world_pos.y()), rgba, sample_weight);
accumulate_constant_surface_triangle_samples(p0, p1, p2, rgba);
}
continue;
}

View File

@@ -123,6 +123,24 @@ bool model_volume_has_any_texture_preview_data(const ModelVolume &model_volume)
model_volume.imported_texture_height > 0);
}
bool texture_preview_has_surface_gradient_state(size_t states_count,
unsigned int base_filament_id,
size_t num_physical,
const TextureMappingManager *texture_mgr)
{
if (texture_mgr == nullptr)
return false;
for (size_t state_id = 0; state_id < states_count; ++state_id) {
const unsigned int filament_id = state_id == 0 ? base_filament_id : unsigned(state_id);
const TextureMappingZone *zone = texture_mgr->zone_from_id(filament_id);
if (zone != nullptr && zone->enabled && !zone->deleted && zone->is_2d_gradient())
return true;
}
return false;
}
} // namespace
@@ -628,6 +646,8 @@ void GLVolume::simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_obj
const unsigned int base_filament_id = model_volume->extruder_id() > 0 ? unsigned(model_volume->extruder_id()) : 0u;
const TextureMappingZone *base_zone = texture_mgr != nullptr ? texture_mgr->zone_from_id(base_filament_id) : nullptr;
const bool has_mmu_segmentation = !model_volume->mmu_segmentation_facets.empty();
const bool has_texture_mapping_color_preview_data = model_volume_has_texture_mapping_color_preview_data(*model_volume);
const bool has_texture_preview_data = model_volume_has_texture_preview_data(*model_volume);
const bool has_texture_preview =
base_zone != nullptr &&
base_zone->enabled &&
@@ -682,25 +702,31 @@ void GLVolume::simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_obj
fallback_color);
state_colors.insert(state_colors.end(), extruder_colors.begin(), extruder_colors.end());
build_mmu_texture_preview_models(*model_volume,
triangles_per_type,
state_colors,
base_filament_id,
num_physical,
texture_mgr,
mmuseg_texture_preview_models,
mmuseg_texture_preview_colors,
mmuseg_texture_preview_filament_ids);
build_mmu_vertex_color_preview_models(*model_volume,
triangles_per_type,
state_colors,
base_filament_id,
num_physical,
texture_mgr,
this->world_matrix(),
mmuseg_vertex_color_preview_models,
mmuseg_vertex_color_preview_colors,
mmuseg_vertex_color_preview_filament_ids);
if (!has_texture_mapping_color_preview_data && has_texture_preview_data) {
build_mmu_texture_preview_models(*model_volume,
triangles_per_type,
state_colors,
base_filament_id,
num_physical,
texture_mgr,
mmuseg_texture_preview_models,
mmuseg_texture_preview_colors,
mmuseg_texture_preview_filament_ids);
}
if (has_texture_mapping_color_preview_data ||
!has_texture_preview_data ||
texture_preview_has_surface_gradient_state(triangles_per_type.size(), base_filament_id, num_physical, texture_mgr)) {
build_mmu_vertex_color_preview_models(*model_volume,
triangles_per_type,
state_colors,
base_filament_id,
num_physical,
texture_mgr,
this->world_matrix(),
mmuseg_vertex_color_preview_models,
mmuseg_vertex_color_preview_colors,
mmuseg_vertex_color_preview_filament_ids);
}
mmuseg_ts = model_volume->mmu_segmentation_facets.timestamp();
mmuseg_texture_preview_visual_signature = preview_visual_signature;
}
@@ -814,6 +840,21 @@ void GLVolume::simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_obj
glFrontFace(GL_CCW);
}
void GLVolume::invalidate_texture_mapping_preview()
{
mmuseg_models.clear();
mmuseg_texture_preview_models.clear();
mmuseg_texture_preview_colors.clear();
mmuseg_texture_preview_filament_ids.clear();
mmuseg_vertex_color_preview_models.clear();
mmuseg_vertex_color_preview_colors.clear();
mmuseg_vertex_color_preview_filament_ids.clear();
mmuseg_texture_preview.reset();
mmuseg_texture_preview_signature = 0;
mmuseg_texture_preview_visual_signature = 0;
mmuseg_ts = 0;
}
bool GLVolume::is_sla_support() const { return this->composite_id.volume_id == -int(slaposSupportTree); }
bool GLVolume::is_sla_pad() const { return this->composite_id.volume_id == -int(slaposPad); }
@@ -1261,6 +1302,19 @@ bool GLWipeTowerVolume::IsTransparent() {
return false;
}
void GLVolumeCollection::invalidate_texture_mapping_preview_for_object(int object_idx)
{
bool changed = false;
for (GLVolume *volume : volumes) {
if (volume == nullptr || volume->object_idx() != object_idx)
continue;
volume->invalidate_texture_mapping_preview();
changed = true;
}
if (changed)
clear_texture_preview_simulation_cache();
}
std::vector<int> GLVolumeCollection::load_object(
const ModelObject *model_object,
int obj_idx,

View File

@@ -359,6 +359,7 @@ public:
//BBS: add simple render function for thumbnail
void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector<ColorRGBA>& extruder_colors, bool ban_light =false);
void invalidate_texture_mapping_preview();
void set_bounding_boxes_as_dirty() {
m_transformed_bounding_box.reset();
@@ -553,6 +554,7 @@ public:
// Clear the geometry
void clear() { clear_texture_preview_simulation_cache(); for (auto *v : volumes) delete v; volumes.clear(); }
void invalidate_texture_mapping_preview_for_object(int object_idx);
bool empty() const { return volumes.empty(); }
void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); }

View File

@@ -73,6 +73,7 @@
#include <float.h>
#include <algorithm>
#include <cmath>
#include <limits>
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
@@ -1352,6 +1353,12 @@ void GLCanvas3D::reset_volumes()
_set_warning_notification(EWarning::ObjectOutside, false);
}
void GLCanvas3D::invalidate_texture_mapping_preview_for_object(size_t object_idx)
{
if (object_idx <= size_t(std::numeric_limits<int>::max()))
m_volumes.invalidate_texture_mapping_preview_for_object(int(object_idx));
}
//BBS: get current plater's bounding box
BoundingBoxf3 GLCanvas3D::_get_current_partplate_print_volume()
{

View File

@@ -768,6 +768,7 @@ public:
unsigned int get_volumes_count() const { return (unsigned int)m_volumes.volumes.size(); }
const GLVolumeCollection& get_volumes() const { return m_volumes; }
void reset_volumes();
void invalidate_texture_mapping_preview_for_object(size_t object_idx);
ModelInstanceEPrintVolumeState check_volumes_outside_state(ObjectFilamentResults* object_results = nullptr) const;
void check_volumes_outside_state(GLVolumeCollection& volumes) const { check_volumes_outside_state(volumes, nullptr, false); }
bool is_all_plates_selected() { return m_sel_plate_toolbar.m_all_plates_stats_item && m_sel_plate_toolbar.m_all_plates_stats_item->selected; }

File diff suppressed because it is too large Load Diff

View File

@@ -1559,6 +1559,63 @@ std::optional<ColorRGBA> sample_texture_mapping_color_preview(
return unpack_vertex_color(color_facets[found->second.front()].rgba);
}
std::vector<Vec3f> clip_preview_triangle_to_triangle(const std::array<Vec3f, 3> &subject, const std::array<Vec3f, 3> &clip)
{
std::vector<Vec3f> polygon(subject.begin(), subject.end());
const float tolerance = -1e-5f;
for (size_t side = 0; side < 3 && !polygon.empty(); ++side) {
std::vector<Vec3f> clipped;
clipped.reserve(polygon.size() + 1);
auto weight_for_side = [&clip, side](const Vec3f &point) {
Vec3f weights = Vec3f::Zero();
if (!barycentric_weights(point, clip[0], clip[1], clip[2], weights))
return -std::numeric_limits<float>::max();
return weights[side];
};
Vec3f previous = polygon.back();
float previous_weight = weight_for_side(previous);
bool previous_inside = previous_weight >= tolerance;
for (const Vec3f &current : polygon) {
const float current_weight = weight_for_side(current);
const bool current_inside = current_weight >= tolerance;
if (current_inside != previous_inside) {
const float denom = previous_weight - current_weight;
if (std::abs(denom) > k_epsilon) {
const float t = std::clamp(previous_weight / denom, 0.f, 1.f);
clipped.emplace_back(previous + (current - previous) * t);
}
}
if (current_inside)
clipped.emplace_back(current);
previous = current;
previous_weight = current_weight;
previous_inside = current_inside;
}
polygon = std::move(clipped);
}
return polygon;
}
bool preview_polygon_has_area(const std::vector<Vec3f> &polygon, const Vec3f &normal)
{
if (polygon.size() < 3)
return false;
float area = 0.f;
for (size_t idx = 1; idx + 1 < polygon.size(); ++idx)
area += std::abs((polygon[idx] - polygon[0]).cross(polygon[idx + 1] - polygon[0]).dot(normal));
return area > k_epsilon;
}
bool build_texture_mapping_color_preview_model_for_state(
const ModelVolume &model_volume,
const std::vector<TriangleSelector::FacetStateTriangle> &state_triangles,
@@ -1624,25 +1681,23 @@ bool build_texture_mapping_color_preview_model_for_state(
bool emitted_color_facets = false;
auto color_facets_for_triangle = facets_by_source_triangle.find(triangle.source_triangle);
if (color_facets_for_triangle != facets_by_source_triangle.end()) {
const float tolerance = -1e-4f;
for (const size_t facet_idx : color_facets_for_triangle->second) {
if (facet_idx >= color_facets.size())
continue;
const ColorFacetTriangle &facet = color_facets[facet_idx];
const Vec3f centroid = (facet.vertices[0] + facet.vertices[1] + facet.vertices[2]) / 3.f;
Vec3f weights = Vec3f::Zero();
if (!barycentric_weights(centroid, triangle.vertices[0], triangle.vertices[1], triangle.vertices[2], weights))
continue;
if (weights.x() < tolerance || weights.y() < tolerance || weights.z() < tolerance)
const std::vector<Vec3f> clipped = clip_preview_triangle_to_triangle(facet.vertices, triangle.vertices);
if (!preview_polygon_has_area(clipped, normal))
continue;
const ColorRGBA color = preview_color(unpack_vertex_color(facet.rgba));
geometry.add_vertex(facet.vertices[0] + offset, normal, color);
geometry.add_vertex(facet.vertices[1] + offset, normal, color);
geometry.add_vertex(facet.vertices[2] + offset, normal, color);
geometry.add_triangle(vertex_index, vertex_index + 1, vertex_index + 2);
vertex_index += 3;
for (size_t poly_idx = 1; poly_idx + 1 < clipped.size(); ++poly_idx) {
geometry.add_vertex(clipped[0] + offset, normal, color);
geometry.add_vertex(clipped[poly_idx] + offset, normal, color);
geometry.add_vertex(clipped[poly_idx + 1] + offset, normal, color);
geometry.add_triangle(vertex_index, vertex_index + 1, vertex_index + 2);
vertex_index += 3;
}
emitted_color_facets = true;
}
}

View File

@@ -53,6 +53,9 @@ bool build_mmu_vertex_color_preview_models(
size_t model_volume_texture_preview_signature(const ModelVolume &model_volume);
size_t model_volume_texture_mapping_color_preview_signature(const ModelVolume &model_volume);
bool model_volume_has_texture_preview_data(const ModelVolume &model_volume);
bool model_volume_has_vertex_color_preview_data(const ModelVolume &model_volume);
bool model_volume_has_texture_mapping_color_preview_data(const ModelVolume &model_volume);
bool ensure_model_volume_texture_preview(const ModelVolume &model_volume,
GUI::GLTexture &texture,