Files
test/source/blender/geometry/intern/reverse_uv_sampler.cc
Campbell Barton e955c94ed3 License Headers: Set copyright to "Blender Authors", add AUTHORS
Listing the "Blender Foundation" as copyright holder implied the Blender
Foundation holds copyright to files which may include work from many
developers.

While keeping copyright on headers makes sense for isolated libraries,
Blender's own code may be refactored or moved between files in a way
that makes the per file copyright holders less meaningful.

Copyright references to the "Blender Foundation" have been replaced with
"Blender Authors", with the exception of `./extern/` since these this
contains libraries which are more isolated, any changed to license
headers there can be handled on a case-by-case basis.

Some directories in `./intern/` have also been excluded:

- `./intern/cycles/` it's own `AUTHORS` file is planned.
- `./intern/opensubdiv/`.

An "AUTHORS" file has been added, using the chromium projects authors
file as a template.

Design task: #110784

Ref !110783.
2023-08-16 00:20:26 +10:00

114 lines
3.8 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "GEO_reverse_uv_sampler.hh"
#include "BLI_math_geom.h"
#include "BLI_math_vector.hh"
#include "BLI_task.hh"
#include "BLI_timeit.hh"
namespace blender::geometry {
static int2 uv_to_cell_key(const float2 &uv, const int resolution)
{
return int2{uv * resolution};
}
ReverseUVSampler::ReverseUVSampler(const Span<float2> uv_map, const Span<MLoopTri> looptris)
: uv_map_(uv_map), looptris_(looptris)
{
resolution_ = std::max<int>(3, std::sqrt(looptris.size()) * 2);
for (const int looptri_index : looptris.index_range()) {
const MLoopTri &looptri = looptris[looptri_index];
const float2 &uv_0 = uv_map_[looptri.tri[0]];
const float2 &uv_1 = uv_map_[looptri.tri[1]];
const float2 &uv_2 = uv_map_[looptri.tri[2]];
const int2 key_0 = uv_to_cell_key(uv_0, resolution_);
const int2 key_1 = uv_to_cell_key(uv_1, resolution_);
const int2 key_2 = uv_to_cell_key(uv_2, resolution_);
const int2 min_key = math::min(math::min(key_0, key_1), key_2);
const int2 max_key = math::max(math::max(key_0, key_1), key_2);
for (int key_x = min_key.x; key_x <= max_key.x; key_x++) {
for (int key_y = min_key.y; key_y <= max_key.y; key_y++) {
const int2 key{key_x, key_y};
looptris_by_cell_.add(key, looptri_index);
}
}
}
}
ReverseUVSampler::Result ReverseUVSampler::sample(const float2 &query_uv) const
{
const int2 cell_key = uv_to_cell_key(query_uv, resolution_);
const Span<int> looptri_indices = looptris_by_cell_.lookup(cell_key);
float best_dist = FLT_MAX;
float3 best_bary_weights;
int best_looptri;
/* The distance to an edge that is allowed to be inside or outside the triangle. Without this,
* the lookup can fail for floating point accuracy reasons when the uv is almost exact on an
* edge. */
const float edge_epsilon = 0.00001f;
for (const int looptri_index : looptri_indices) {
const MLoopTri &looptri = looptris_[looptri_index];
const float2 &uv_0 = uv_map_[looptri.tri[0]];
const float2 &uv_1 = uv_map_[looptri.tri[1]];
const float2 &uv_2 = uv_map_[looptri.tri[2]];
float3 bary_weights;
if (!barycentric_coords_v2(uv_0, uv_1, uv_2, query_uv, bary_weights)) {
continue;
}
/* If #query_uv is in the triangle, the distance is <= 0. Otherwise, the larger the distance,
* the further away the uv is from the triangle. */
const float x_dist = std::max(-bary_weights.x, bary_weights.x - 1.0f);
const float y_dist = std::max(-bary_weights.y, bary_weights.y - 1.0f);
const float z_dist = std::max(-bary_weights.z, bary_weights.z - 1.0f);
const float dist = MAX3(x_dist, y_dist, z_dist);
if (dist <= 0.0f && best_dist <= 0.0f) {
const float worse_dist = std::max(dist, best_dist);
/* Allow ignoring multiple triangle intersections if the uv is almost exactly on an edge. */
if (worse_dist < -edge_epsilon) {
/* The uv sample is in multiple triangles. */
return Result{ResultType::Multiple};
}
}
if (dist < best_dist) {
best_dist = dist;
best_bary_weights = bary_weights;
best_looptri = looptri_index;
}
}
/* Allow using the closest (but not intersecting) triangle if the uv is almost exactly on an
* edge. */
if (best_dist < edge_epsilon) {
return Result{ResultType::Ok, best_looptri, math::clamp(best_bary_weights, 0.0f, 1.0f)};
}
return Result{};
}
void ReverseUVSampler::sample_many(const Span<float2> query_uvs,
MutableSpan<Result> r_results) const
{
BLI_assert(query_uvs.size() == r_results.size());
threading::parallel_for(query_uvs.index_range(), 256, [&](const IndexRange range) {
for (const int i : range) {
r_results[i] = this->sample(query_uvs[i]);
}
});
}
} // namespace blender::geometry