96f754f81 Improve texture sampling slightly

This commit is contained in:
sentientstardust
2026-05-02 15:00:09 +01:00
parent a9dc1858b1
commit c7390ef970
2 changed files with 203 additions and 25 deletions

View File

@@ -6503,29 +6503,52 @@ static std::array<Vec2f, 3> unwrap_triangle_uvs_for_sampling_for_gcode(const Vec
std::array<Vec2f, 3> out { uv0, uv1, uv2 };
auto unwrap_axis = [&out](bool use_u_axis) {
float values[3] = {
std::array<float, 3> values = {
use_u_axis ? out[0].x() : out[0].y(),
use_u_axis ? out[1].x() : out[1].y(),
use_u_axis ? out[2].x() : out[2].y()
};
const float v_min = std::min({ values[0], values[1], values[2] });
const float v_max = std::max({ values[0], values[1], values[2] });
if (v_max - v_min <= 0.5f)
if (!std::all_of(values.begin(), values.end(), [](float value) { return std::isfinite(value); }))
return;
for (size_t i = 0; i < 3; ++i) {
if (values[i] < 0.5f)
values[i] += 1.f;
auto span = [](const std::array<float, 3> &v) {
return std::max({ v[0], v[1], v[2] }) - std::min({ v[0], v[1], v[2] });
};
const bool has_repeat_evidence = std::any_of(values.begin(), values.end(), [](float value) {
constexpr float eps = 1e-6f;
return value < -eps || value > 1.f + eps;
});
const float original_span = span(values);
if (!has_repeat_evidence || original_span <= 0.5f)
return;
std::array<float, 3> best = values;
float best_span = original_span;
for (size_t anchor = 0; anchor < values.size(); ++anchor) {
std::array<float, 3> candidate = values;
for (size_t i = 0; i < candidate.size(); ++i) {
const float delta = values[i] - values[anchor];
candidate[i] = values[anchor] + delta - std::round(delta);
}
const float candidate_span = span(candidate);
if (candidate_span + 1e-6f < best_span) {
best = candidate;
best_span = candidate_span;
}
}
if (best_span >= original_span - 1e-6f)
return;
if (use_u_axis) {
out[0].x() = values[0];
out[1].x() = values[1];
out[2].x() = values[2];
out[0].x() = best[0];
out[1].x() = best[1];
out[2].x() = best[2];
} else {
out[0].y() = values[0];
out[1].y() = values[1];
out[2].y() = values[2];
out[0].y() = best[0];
out[1].y() = best[1];
out[2].y() = best[2];
}
};
@@ -6789,10 +6812,127 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode(
samples.push_back({ x_mm, y_mm, rgba, sample_weight });
};
struct LayerPlaneSamplePoint {
Vec3d p;
Vec3f barycentric;
};
auto accumulate_layer_plane_triangle_samples = [&](const Vec3d &p0,
const Vec3d &p1,
const Vec3d &p2,
const auto &sample_rgba_for_barycentric) {
if (!use_layer_weighting)
return false;
const float z0 = float(p0.z());
const float z1 = float(p1.z());
const float z2 = float(p2.z());
if (!std::isfinite(z0) || !std::isfinite(z1) || !std::isfinite(z2))
return false;
const float min_z = std::min({ z0, z1, z2 });
const float max_z = std::max({ z0, z1, z2 });
const float z_eps = std::max(1e-5f, safe_layer_z_falloff_mm * 1e-4f);
if (layer_z_mm < min_z - z_eps || layer_z_mm > max_z + z_eps || max_z - min_z <= z_eps)
return false;
const std::array<Vec3d, 3> vertices = { p0, p1, p2 };
const std::array<Vec3f, 3> barycentrics = {
Vec3f(1.f, 0.f, 0.f),
Vec3f(0.f, 1.f, 0.f),
Vec3f(0.f, 0.f, 1.f)
};
const std::array<float, 3> zs = { z0, z1, z2 };
std::vector<LayerPlaneSamplePoint> layer_points;
layer_points.reserve(3);
auto add_layer_point = [&layer_points](const Vec3d &p, const Vec3f &barycentric) {
if (!p.allFinite() || !barycentric.allFinite())
return;
for (const LayerPlaneSamplePoint &existing : layer_points)
if ((existing.p - p).squaredNorm() <= 1e-10)
return;
layer_points.push_back({ p, barycentric });
};
const std::array<std::pair<size_t, size_t>, 3> edges = {
std::make_pair(size_t(0), size_t(1)),
std::make_pair(size_t(1), size_t(2)),
std::make_pair(size_t(2), size_t(0))
};
for (const auto &edge : edges) {
const size_t a = edge.first;
const size_t b = edge.second;
const float da = zs[a] - layer_z_mm;
const float db = zs[b] - layer_z_mm;
const bool a_on_layer = std::abs(da) <= z_eps;
const bool b_on_layer = std::abs(db) <= z_eps;
if (a_on_layer)
add_layer_point(vertices[a], barycentrics[a]);
if (b_on_layer)
add_layer_point(vertices[b], barycentrics[b]);
if (a_on_layer || b_on_layer)
continue;
if ((da < 0.f && db > 0.f) || (da > 0.f && db < 0.f)) {
const float t = (layer_z_mm - zs[a]) / (zs[b] - zs[a]);
if (!std::isfinite(t) || t < -1e-4f || t > 1.f + 1e-4f)
continue;
const float clamped_t = std::clamp(t, 0.f, 1.f);
add_layer_point(vertices[a] * double(1.f - clamped_t) + vertices[b] * double(clamped_t),
barycentrics[a] * (1.f - clamped_t) + barycentrics[b] * clamped_t);
}
}
if (layer_points.size() < 2)
return false;
size_t best_a = 0;
size_t best_b = 1;
double best_length_sq = 0.0;
for (size_t i = 0; i + 1 < layer_points.size(); ++i) {
for (size_t j = i + 1; j < layer_points.size(); ++j) {
const double length_sq = (layer_points[i].p - layer_points[j].p).squaredNorm();
if (length_sq > best_length_sq) {
best_a = i;
best_b = j;
best_length_sq = length_sq;
}
}
}
const double segment_length_mm = std::sqrt(best_length_sq);
if (!std::isfinite(segment_length_mm) || segment_length_mm <= EPSILON)
return false;
const float sample_pitch_mm = high_resolution_texture_sampling ? 0.08f : 0.16f;
const int sample_count = std::clamp(int(std::ceil(segment_length_mm / std::max(float(EPSILON), sample_pitch_mm))), 1, 2000);
const float sample_weight = std::max(0.05f, float(segment_length_mm) / float(sample_count));
for (int sample_idx = 0; sample_idx < sample_count; ++sample_idx) {
const float t = (float(sample_idx) + 0.5f) / float(sample_count);
Vec3f barycentric = layer_points[best_a].barycentric * (1.f - t) + layer_points[best_b].barycentric * t;
barycentric.x() = std::max(0.f, barycentric.x());
barycentric.y() = std::max(0.f, barycentric.y());
barycentric.z() = std::max(0.f, barycentric.z());
const float barycentric_sum = barycentric.x() + barycentric.y() + barycentric.z();
if (!std::isfinite(barycentric_sum) || barycentric_sum <= EPSILON)
continue;
barycentric /= barycentric_sum;
const Vec3d world_pos = p0 * double(barycentric.x()) + p1 * double(barycentric.y()) + p2 * double(barycentric.z());
accumulate_sample(float(world_pos.x()), float(world_pos.y()), sample_rgba_for_barycentric(barycentric), sample_weight);
}
return true;
};
auto accumulate_constant_surface_triangle_samples = [&](const Vec3d &p0,
const Vec3d &p1,
const Vec3d &p2,
const std::array<float, 4> &rgba) {
if (accumulate_layer_plane_triangle_samples(p0, p1, p2, [&rgba](const Vec3f &) { return rgba; }))
return;
const float max_world_edge_mm = std::max({
float((p1 - p0).norm()),
float((p2 - p1).norm()),
@@ -6915,6 +7055,21 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode(
continue;
const std::array<Vec2f, 3> tri_uv = unwrap_triangle_uvs_for_sampling_for_gcode(uv0, uv1, uv2);
auto sample_rgba_for_barycentric = [&](const Vec3f &barycentric) {
const Vec2f uv = tri_uv[0] * barycentric.x() + tri_uv[1] * barycentric.y() + tri_uv[2] * barycentric.z();
std::array<float, 4> rgba = sample_texture_rgba_bilinear_for_gcode(volume->imported_texture_rgba,
volume->imported_texture_width,
volume->imported_texture_height,
uv.x(),
uv.y());
rgba[3] = 1.f;
return rgba;
};
if (accumulate_layer_plane_triangle_samples(p0, p1, p2, sample_rgba_for_barycentric)) {
sampled_from_uv_texture = true;
continue;
}
const float max_uv_edge_texel = std::max({
uv_edge_texel_length(tri_uv[0], tri_uv[1]),
uv_edge_texel_length(tri_uv[1], tri_uv[2]),

View File

@@ -1722,23 +1722,46 @@ static std::array<Vec2f, 3> unwrap_projection_uvs(std::array<Vec2f, 3> uvs)
use_u_axis ? uvs[2].x() : uvs[2].y()
};
const float min_value = std::min({ values[0], values[1], values[2] });
const float max_value = std::max({ values[0], values[1], values[2] });
if (max_value - min_value <= 0.5f)
if (!std::all_of(values.begin(), values.end(), [](float value) { return std::isfinite(value); }))
return;
for (float &value : values)
if (value < 0.5f)
value += 1.f;
auto span = [](const std::array<float, 3> &v) {
return std::max({ v[0], v[1], v[2] }) - std::min({ v[0], v[1], v[2] });
};
const bool has_repeat_evidence = std::any_of(values.begin(), values.end(), [](float value) {
constexpr float eps = 1e-6f;
return value < -eps || value > 1.f + eps;
});
const float original_span = span(values);
if (!has_repeat_evidence || original_span <= 0.5f)
return;
std::array<float, 3> best = values;
float best_span = original_span;
for (size_t anchor = 0; anchor < values.size(); ++anchor) {
std::array<float, 3> candidate = values;
for (size_t i = 0; i < candidate.size(); ++i) {
const float delta = values[i] - values[anchor];
candidate[i] = values[anchor] + delta - std::round(delta);
}
const float candidate_span = span(candidate);
if (candidate_span + 1e-6f < best_span) {
best = candidate;
best_span = candidate_span;
}
}
if (best_span >= original_span - 1e-6f)
return;
if (use_u_axis) {
uvs[0].x() = values[0];
uvs[1].x() = values[1];
uvs[2].x() = values[2];
uvs[0].x() = best[0];
uvs[1].x() = best[1];
uvs[2].x() = best[2];
} else {
uvs[0].y() = values[0];
uvs[1].y() = values[1];
uvs[2].y() = values[2];
uvs[0].y() = best[0];
uvs[1].y() = best[1];
uvs[2].y() = best[2];
}
};