60 return int2{uv * resolution};
67 const float2 &uv_0 = uv_map[tri[0]];
68 const float2 &uv_1 = uv_map[tri[1]];
69 const float2 &uv_2 = uv_map[tri[2]];
78 return {min_cell, max_cell};
92 LocalData &local_data = data_per_thread.local();
93 for (const int tri_i : tris_range) {
94 const int3 &tri = corner_tris[tri_i];
97 const Bounds<int2> cell_bounds = tri_to_cell_bounds(tri, resolution, uv_map);
98 const TriWithRange tri_with_range{tri_i, cell_bounds.min.x, cell_bounds.max.x};
101 for (int cell_y = cell_bounds.min.y; cell_y <= cell_bounds.max.y; cell_y++) {
102 LocalRowData &row = *local_data.rows.lookup_or_add_cb(
103 cell_y, [&]() { return local_data.allocator.construct<LocalRowData>(); });
104 row.tris.append(local_data.allocator, tri_with_range);
105 row.x_min = std::min<int>(row.x_min, cell_bounds.min.x);
106 row.x_max = std::max<int>(row.x_max, cell_bounds.max.x);
122 Vector<const LocalRowData *, 32> local_rows;
123 for (const int y : all_ys.slice(all_ys_range)) {
124 Row &row = lookup_grid.rows[y - y_bounds.min];
127 for (const LocalData *local_data : local_data_vec) {
128 if (const destruct_ptr<LocalRowData> *local_row = local_data->rows.lookup_ptr(y)) {
129 local_rows.append(local_row->get());
133 int x_min = INT32_MAX;
134 int x_max = INT32_MIN;
135 for (const LocalRowData *local_row : local_rows) {
136 x_min = std::min(x_min, local_row->x_min);
137 x_max = std::max(x_max, local_row->x_max);
140 const int x_num = x_max - x_min + 1;
141 row.offsets.reinitialize(x_num + 1);
144 MutableSpan<int> counts = row.offsets;
146 for (const LocalRowData *local_row : local_rows) {
147 for (const TriWithRange &tri_with_range : local_row->tris) {
148 for (int x = tri_with_range.x_min; x <= tri_with_range.x_max; x++) {
153 offset_indices::accumulate_counts_to_offsets(counts);
155 const int tri_indices_num = row.offsets.last();
156 row.tri_indices.reinitialize(tri_indices_num);
159 Array<int, 1000> current_offsets(x_num, 0);
160 for (const LocalRowData *local_row : local_rows) {
161 for (const TriWithRange &tri_with_range : local_row->tris) {
162 for (int x = tri_with_range.x_min; x <= tri_with_range.x_max; x++) {
163 const int offset_x = x - x_min;
164 row.tri_indices[row.offsets[offset_x] + current_offsets[offset_x]] =
165 tri_with_range.tri_index;
166 current_offsets[offset_x]++;
178 : uv_map_(uv_map), corner_tris_(corner_tris), lookup_grid_(std::make_unique<
LookupGrid>())
183 resolution_ = std::max<int>(3, std::sqrt(corner_tris.
size()) * 3);
193 for (
const LocalData &local_data : data_per_thread) {
194 local_data_vec.
append(&local_data);
195 for (
const int y : local_data.rows.keys()) {
201 lookup_grid_->y_min = y_bounds.
min;
203 const int rows_num = y_bounds.
max - y_bounds.
min + 1;
204 lookup_grid_->rows.reinitialize(rows_num);
206 finish_rows(all_ys, local_data_vec, y_bounds, *lookup_grid_);
212 if (cell.y < lookup_grid.
y_min) {
215 if (cell.y >= lookup_grid.
y_min + lookup_grid.
rows.size()) {
218 const Row &row = lookup_grid.
rows[cell.y - lookup_grid.
y_min];
219 if (cell.x < row.
x_min) {
222 if (cell.x > row.
x_max) {
229 const int tris_num = row.
offsets[cell.x - row.
x_min + 1] - offset;
245 const float edge_epsilon = 0.00001f;
249 const float area_epsilon = 0.00001f;
251 for (
const int tri_i : tri_indices) {
252 const int3 &tri = corner_tris_[tri_i];
253 const float2 &uv_0 = uv_map_[tri[0]];
254 const float2 &uv_1 = uv_map_[tri[1]];
255 const float2 &uv_2 = uv_map_[tri[2]];
263 const float x_dist = std::max(-bary_weights.x, bary_weights.x - 1.0f);
264 const float y_dist = std::max(-bary_weights.y, bary_weights.y - 1.0f);
265 const float z_dist = std::max(-bary_weights.z, bary_weights.z - 1.0f);
266 const float dist = std::max({x_dist, y_dist, z_dist});
268 if (dist <= 0.0f && best_dist <= 0.0f) {
269 const float worse_dist = std::max(dist, best_dist);
271 if (worse_dist < -edge_epsilon) {
272 const int3 &best_tri = corner_tris_[tri_i];
274 uv_map_[best_tri[0]], uv_map_[best_tri[1]], uv_map_[best_tri[2]]);
275 const float current_tri_area =
area_tri_v2(uv_0, uv_1, uv_2);
276 if (best_tri_area > area_epsilon && current_tri_area > area_epsilon) {
283 if (dist < best_dist) {
285 best_bary_weights = bary_weights;
286 best_tri_index = tri_i;
292 if (best_dist < edge_epsilon) {
306 for (const int i : range) {
307 r_results[i] = this->sample(query_uvs[i]);
MINLINE float area_tri_v2(const float v1[2], const float v2[2], const float v3[2])
bool barycentric_coords_v2(const float v1[2], const float v2[2], const float v3[2], const float co[2], float w[3])
Span< T > as_span() const
constexpr Span slice(int64_t start, int64_t size) const
constexpr int64_t size() const
constexpr int64_t size() const
constexpr IndexRange index_range() const
constexpr bool is_empty() const
Span< Key > as_span() const
void append(const T &value)
ReverseUVSampler(Span< float2 > uv_map, Span< int3 > corner_tris)
Result sample(const float2 &query_uv) const
void sample_many(Span< float2 > query_uvs, MutableSpan< Result > r_results) const
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
static void finish_rows(const Span< int > all_ys, const Span< const LocalData * > local_data_vec, const Bounds< int > y_bounds, ReverseUVSampler::LookupGrid &lookup_grid)
static Span< int > lookup_tris_in_cell(const int2 cell, const ReverseUVSampler::LookupGrid &lookup_grid)
static void sort_tris_into_rows(const Span< float2 > uv_map, const Span< int3 > corner_tris, const int resolution, threading::EnumerableThreadSpecific< LocalData > &data_per_thread)
static Bounds< int2 > tri_to_cell_bounds(const int3 &tri, const int resolution, const Span< float2 > uv_map)
static int2 uv_to_cell(const float2 &uv, const int resolution)
T clamp(const T &a, const T &min, const T &max)
T min(const T &a, const T &b)
T max(const T &a, const T &b)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
VecBase< float, 3 > float3
Map< int, destruct_ptr< LocalRowData > > rows
LinearAllocator allocator
linear_allocator::ChunkedList< TriWithRange, 8 > tris