Blender V4.3
pbvh_pixels.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2022 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BKE_attribute.hh"
6#include "BKE_customdata.hh"
7#include "BKE_mesh.hh"
8#include "BKE_pbvh_api.hh"
9#include "BKE_pbvh_pixels.hh"
10
11#include "DNA_image_types.h"
12#include "DNA_object_types.h"
13
14#include "BLI_listbase.h"
15#include "BLI_math_geom.h"
16#include "BLI_math_vector.h"
17#include "BLI_task.h"
18
19#include "BKE_global.hh"
20#include "BKE_image_wrappers.hh"
21#include "BKE_paint.hh"
22
23#include "pbvh_intern.hh"
24#include "pbvh_pixels_copy.hh"
25#include "pbvh_uv_islands.hh"
26
28
33 const float2 start_uv,
34 const float2 end_uv)
35{
36
37 float3 start_barycentric;
38 barycentric_weights_v2(uvs[0], uvs[1], uvs[2], start_uv, start_barycentric);
39 float3 end_barycentric;
40 barycentric_weights_v2(uvs[0], uvs[1], uvs[2], end_uv, end_barycentric);
41 float3 barycentric = end_barycentric - start_barycentric;
42 return float2(barycentric.x, barycentric.y);
43}
44
45static float2 calc_barycentric_delta_x(const ImBuf *image_buffer,
46 const float2 uvs[3],
47 const int x,
48 const int y)
49{
50 const float2 start_uv(float(x) / image_buffer->x, float(y) / image_buffer->y);
51 const float2 end_uv(float(x + 1) / image_buffer->x, float(y) / image_buffer->y);
52 return calc_barycentric_delta(uvs, start_uv, end_uv);
53}
54
59constexpr bool USE_WATERTIGHT_CHECK = false;
60
62 const ImBuf *image_buffer,
63 const uv_islands::UVIslandsMask &uv_mask,
64 const int uv_island_index,
65 const int uv_primitive_index,
66 const float2 uvs[3],
67 const float2 tile_offset,
68 const int minx,
69 const int miny,
70 const int maxx,
71 const int maxy)
72{
73 for (int y = miny; y < maxy; y++) {
74 bool start_detected = false;
75 PackedPixelRow pixel_row;
76 pixel_row.uv_primitive_index = uv_primitive_index;
77 pixel_row.num_pixels = 0;
78 int x;
79
80 for (x = minx; x < maxx; x++) {
81 float2 uv((float(x) + 0.5f) / image_buffer->x, (float(y) + 0.5f) / image_buffer->y);
83 barycentric_weights_v2(uvs[0], uvs[1], uvs[2], uv, barycentric_weights);
84
86 const bool is_masked = uv_mask.is_masked(uv_island_index, uv + tile_offset);
87 if (!start_detected && is_inside && is_masked) {
88 start_detected = true;
89 pixel_row.start_image_coordinate = ushort2(x, y);
91 }
92 else if (start_detected && (!is_inside || !is_masked)) {
93 break;
94 }
95 }
96
97 if (!start_detected) {
98 continue;
99 }
100 pixel_row.num_pixels = x - pixel_row.start_image_coordinate.x;
101 tile_data.pixel_rows.append(pixel_row);
102 }
103}
104
106static void update_geom_primitives(Tree &pbvh, const uv_islands::MeshData &mesh_data)
107{
108 PBVHData &pbvh_data = data_get(pbvh);
109 pbvh_data.vert_tris.reinitialize(mesh_data.corner_tris.size());
111 mesh_data.corner_verts, mesh_data.corner_tris, pbvh_data.vert_tris);
112}
113
124
126
127 UVPrimitiveLookup(const uint64_t geom_primitive_len, uv_islands::UVIslands &uv_islands)
128 {
129 lookup.append_n_times(Vector<Entry>(), geom_primitive_len);
130
131 uint64_t uv_island_index = 0;
132 for (uv_islands::UVIsland &uv_island : uv_islands.islands) {
134 uv_island.uv_primitives)
135 {
136 for (uv_islands::UVPrimitive &uv_primitive : uv_primitives) {
137 lookup[uv_primitive.primitive_i].append_as(Entry(&uv_primitive, uv_island_index));
138 }
139 }
140 uv_island_index++;
141 }
142 }
143};
144
145static void do_encode_pixels(const uv_islands::MeshData &mesh_data,
146 const uv_islands::UVIslandsMask &uv_masks,
147 const UVPrimitiveLookup &uv_prim_lookup,
148 Image &image,
149 ImageUser &image_user,
150 MeshNode &node)
151{
152 NodeData *node_data = static_cast<NodeData *>(node.pixels_);
153
154 LISTBASE_FOREACH (ImageTile *, tile, &image.tiles) {
155 image::ImageTileWrapper image_tile(tile);
156 image_user.tile = image_tile.get_tile_number();
157 ImBuf *image_buffer = BKE_image_acquire_ibuf(&image, &image_user, nullptr);
158 if (image_buffer == nullptr) {
159 continue;
160 }
161
162 UDIMTilePixels tile_data;
163 tile_data.tile_number = image_tile.get_tile_number();
164 float2 tile_offset = float2(image_tile.get_tile_offset());
165
166 for (const int face : node.faces()) {
167 for (const int tri : bke::mesh::face_triangles_range(mesh_data.faces, face)) {
168 for (const UVPrimitiveLookup::Entry &entry : uv_prim_lookup.lookup[tri]) {
169 uv_islands::UVBorder uv_border = entry.uv_primitive->extract_border();
170 float2 uvs[3] = {
171 entry.uv_primitive->get_uv_vertex(mesh_data, 0)->uv - tile_offset,
172 entry.uv_primitive->get_uv_vertex(mesh_data, 1)->uv - tile_offset,
173 entry.uv_primitive->get_uv_vertex(mesh_data, 2)->uv - tile_offset,
174 };
175 const float minv = clamp_f(min_fff(uvs[0].y, uvs[1].y, uvs[2].y), 0.0f, 1.0f);
176 const int miny = floor(minv * image_buffer->y);
177 const float maxv = clamp_f(max_fff(uvs[0].y, uvs[1].y, uvs[2].y), 0.0f, 1.0f);
178 const int maxy = min_ii(ceil(maxv * image_buffer->y), image_buffer->y);
179 const float minu = clamp_f(min_fff(uvs[0].x, uvs[1].x, uvs[2].x), 0.0f, 1.0f);
180 const int minx = floor(minu * image_buffer->x);
181 const float maxu = clamp_f(max_fff(uvs[0].x, uvs[1].x, uvs[2].x), 0.0f, 1.0f);
182 const int maxx = min_ii(ceil(maxu * image_buffer->x), image_buffer->x);
183
184 /* TODO: Perform bounds check */
185 int uv_prim_index = node_data->uv_primitives.size();
186 node_data->uv_primitives.append(tri);
187 UVPrimitivePaintInput &paint_input = node_data->uv_primitives.last();
188
189 /* Calculate barycentric delta */
191 image_buffer, uvs, minx, miny);
192
193 /* Extract the pixels. */
195 image_buffer,
196 uv_masks,
197 entry.uv_island_index,
198 uv_prim_index,
199 uvs,
200 tile_offset,
201 minx,
202 miny,
203 maxx,
204 maxy);
205 }
206 }
207 }
208 BKE_image_release_ibuf(&image, image_buffer, nullptr);
209
210 if (tile_data.pixel_rows.is_empty()) {
211 continue;
212 }
213
214 node_data->tiles.append(tile_data);
215 }
216}
217
218static bool should_pixels_be_updated(const Node &node)
219{
220 if ((node.flag_ & (PBVH_Leaf | PBVH_TexLeaf)) == 0) {
221 return false;
222 }
223 if (node.children_offset_ != 0) {
224 return false;
225 }
226 if ((node.flag_ & PBVH_RebuildPixels) != 0) {
227 return true;
228 }
229 NodeData *node_data = static_cast<NodeData *>(node.pixels_);
230 if (node_data != nullptr) {
231 return false;
232 }
233 return true;
234}
235
237{
238 int result = 0;
239 for (Node &node : pbvh.nodes<MeshNode>()) {
240 if (should_pixels_be_updated(node)) {
241 result++;
242 }
243 }
244 return result;
245}
246
256static bool find_nodes_to_update(Tree &pbvh, Vector<MeshNode *> &r_nodes_to_update)
257{
258 int nodes_to_update_len = count_nodes_to_update(pbvh);
259 if (nodes_to_update_len == 0) {
260 return false;
261 }
262
263 /* Init or reset Tree pixel data when changes detected. */
264 if (pbvh.pixels_ == nullptr) {
265 PBVHData *pbvh_data = MEM_new<PBVHData>(__func__);
266 pbvh.pixels_ = pbvh_data;
267 }
268 else {
269 PBVHData *pbvh_data = static_cast<PBVHData *>(pbvh.pixels_);
270 pbvh_data->clear_data();
271 }
272
273 r_nodes_to_update.reserve(nodes_to_update_len);
274
275 for (MeshNode &node : pbvh.nodes<MeshNode>()) {
276 if (!should_pixels_be_updated(node)) {
277 continue;
278 }
279 r_nodes_to_update.append(&node);
280 node.flag_ = static_cast<PBVHNodeFlags>(node.flag_ | PBVH_RebuildPixels);
281
282 if (node.pixels_ == nullptr) {
283 NodeData *node_data = MEM_new<NodeData>(__func__);
284 node.pixels_ = node_data;
285 }
286 else {
287 NodeData *node_data = static_cast<NodeData *>(node.pixels_);
288 node_data->clear_data();
289 }
290 }
291
292 return true;
293}
294
295static void apply_watertight_check(Tree &pbvh, Image &image, ImageUser &image_user)
296{
297 ImageUser watertight = image_user;
298 LISTBASE_FOREACH (ImageTile *, tile_data, &image.tiles) {
299 image::ImageTileWrapper image_tile(tile_data);
300 watertight.tile = image_tile.get_tile_number();
301 ImBuf *image_buffer = BKE_image_acquire_ibuf(&image, &watertight, nullptr);
302 if (image_buffer == nullptr) {
303 continue;
304 }
305 for (Node &node : pbvh.nodes<MeshNode>()) {
306 if ((node.flag_ & PBVH_Leaf) == 0) {
307 continue;
308 }
309 NodeData *node_data = static_cast<NodeData *>(node.pixels_);
310 UDIMTilePixels *tile_node_data = node_data->find_tile_data(image_tile);
311 if (tile_node_data == nullptr) {
312 continue;
313 }
314
315 for (PackedPixelRow &pixel_row : tile_node_data->pixel_rows) {
316 int pixel_offset = pixel_row.start_image_coordinate.y * image_buffer->x +
317 pixel_row.start_image_coordinate.x;
318 for (int x = 0; x < pixel_row.num_pixels; x++) {
319 if (image_buffer->float_buffer.data) {
320 copy_v4_fl(&image_buffer->float_buffer.data[pixel_offset * 4], 1.0);
321 }
322 if (image_buffer->byte_buffer.data) {
323 uint8_t *dest = &image_buffer->byte_buffer.data[pixel_offset * 4];
324 copy_v4_uchar(dest, 255);
325 }
326 pixel_offset += 1;
327 }
328 }
329 }
330 BKE_image_release_ibuf(&image, image_buffer, nullptr);
331 }
333}
334
335static bool update_pixels(const Depsgraph &depsgraph,
336 const Object &object,
337 Tree &pbvh,
338 Image &image,
339 ImageUser &image_user)
340{
341 Vector<MeshNode *> nodes_to_update;
342 if (!find_nodes_to_update(pbvh, nodes_to_update)) {
343 return false;
344 }
345
346 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
347 const StringRef active_uv_name = CustomData_get_active_layer_name(&mesh.corner_data,
349 if (active_uv_name.is_empty()) {
350 return false;
351 }
352
353 const AttributeAccessor attributes = mesh.attributes();
354 const VArraySpan uv_map = *attributes.lookup<float2>(active_uv_name, AttrDomain::Corner);
355
356 uv_islands::MeshData mesh_data(mesh.faces(),
357 mesh.corner_tris(),
358 mesh.corner_verts(),
359 uv_map,
361 uv_islands::UVIslands islands(mesh_data);
362
364 ImageUser tile_user = image_user;
365 LISTBASE_FOREACH (ImageTile *, tile_data, &image.tiles) {
366 image::ImageTileWrapper image_tile(tile_data);
367 tile_user.tile = image_tile.get_tile_number();
368 ImBuf *tile_buffer = BKE_image_acquire_ibuf(&image, &tile_user, nullptr);
369 if (tile_buffer == nullptr) {
370 continue;
371 }
372 uv_masks.add_tile(float2(image_tile.get_tile_x_offset(), image_tile.get_tile_y_offset()),
373 ushort2(tile_buffer->x, tile_buffer->y));
374 BKE_image_release_ibuf(&image, tile_buffer, nullptr);
375 }
376 uv_masks.add(mesh_data, islands);
377 uv_masks.dilate(image.seam_margin);
378
379 islands.extract_borders();
380 islands.extend_borders(mesh_data, uv_masks);
381 update_geom_primitives(pbvh, mesh_data);
382
383 UVPrimitiveLookup uv_primitive_lookup(mesh_data.corner_tris.size(), islands);
384
385 threading::parallel_for(nodes_to_update.index_range(), 1, [&](const IndexRange range) {
386 for (const int i : range) {
387 do_encode_pixels(
388 mesh_data, uv_masks, uv_primitive_lookup, image, image_user, *nodes_to_update[i]);
389 }
390 });
392 apply_watertight_check(pbvh, image, image_user);
393 }
394
395 /* Add solution for non-manifold parts of the model. */
396 copy_update(pbvh, image, image_user, mesh_data);
397
398 /* Rebuild the undo regions. */
399 for (Node *node : nodes_to_update) {
400 NodeData *node_data = static_cast<NodeData *>(node->pixels_);
401 node_data->rebuild_undo_regions();
402 }
403
404 /* Clear the UpdatePixels flag. */
405 for (Node *node : nodes_to_update) {
406 node->flag_ = static_cast<PBVHNodeFlags>(node->flag_ & ~PBVH_RebuildPixels);
407 }
408
409 /* Add PBVH_TexLeaf flag */
410 for (Node &node : pbvh.nodes<MeshNode>()) {
411 if (node.flag_ & PBVH_Leaf) {
412 node.flag_ = (PBVHNodeFlags)(int(node.flag_) | int(PBVH_TexLeaf));
413 }
414 }
415
416// #define DO_PRINT_STATISTICS
417#ifdef DO_PRINT_STATISTICS
418 /* Print some statistics about compression ratio. */
419 {
420 int compressed_data_len = 0;
421 int num_pixels = 0;
422 for (int n = 0; n < pbvh->totnode; n++) {
423 Node *node = &pbvh->nodes[n];
424 if ((node->flag & PBVH_Leaf) == 0) {
425 continue;
426 }
427 NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
428 for (const UDIMTilePixels &tile_data : node_data->tiles) {
429 compressed_data_len += tile_data.encoded_pixels.size() * sizeof(PackedPixelRow);
430 for (const PackedPixelRow &encoded_pixels : tile_data.encoded_pixels) {
431 num_pixels += encoded_pixels.num_pixels;
432 }
433 }
434 }
435 printf("Encoded %lld pixels in %lld bytes (%f bytes per pixel)\n",
436 num_pixels,
437 compressed_data_len,
438 float(compressed_data_len) / num_pixels);
439 }
440#endif
441
442 return true;
443}
444
446{
447 BLI_assert(node.pixels_ != nullptr);
448 NodeData *node_data = static_cast<NodeData *>(node.pixels_);
449 return *node_data;
450}
451
453{
454 BLI_assert(pbvh.pixels_ != nullptr);
455 PBVHData *data = static_cast<PBVHData *>(pbvh.pixels_);
456 return *data;
457}
458
459void mark_image_dirty(Node &node, Image &image, ImageUser &image_user)
460{
461 BLI_assert(node.pixels_ != nullptr);
462 NodeData *node_data = static_cast<NodeData *>(node.pixels_);
463 if (node_data->flags.dirty) {
464 ImageUser local_image_user = image_user;
465 LISTBASE_FOREACH (ImageTile *, tile, &image.tiles) {
466 image::ImageTileWrapper image_tile(tile);
467 local_image_user.tile = image_tile.get_tile_number();
468 ImBuf *image_buffer = BKE_image_acquire_ibuf(&image, &local_image_user, nullptr);
469 if (image_buffer == nullptr) {
470 continue;
471 }
472
473 node_data->mark_region(image, image_tile, *image_buffer);
474 BKE_image_release_ibuf(&image, image_buffer, nullptr);
475 }
476 node_data->flags.dirty = false;
477 }
478}
479
481{
482 NodeData *node_data = static_cast<NodeData *>(node.pixels_);
483 node_data->collect_dirty_tiles(r_dirty_tiles);
484}
485
486} // namespace blender::bke::pbvh::pixels
487
488namespace blender::bke::pbvh {
489
490void build_pixels(const Depsgraph &depsgraph, Object &object, Image &image, ImageUser &image_user)
491{
492 Tree &pbvh = *object::pbvh_get(object);
493 pixels::update_pixels(depsgraph, object, pbvh, image, image_user);
494}
495
497{
498 pixels::NodeData *node_data = static_cast<pixels::NodeData *>(node->pixels_);
499
500 if (!node_data) {
501 return;
502 }
503
504 MEM_delete(node_data);
505 node->pixels_ = nullptr;
506}
507
508void pixels_free(Tree *pbvh)
509{
510 pixels::PBVHData *pbvh_data = static_cast<pixels::PBVHData *>(pbvh->pixels_);
511 MEM_delete(pbvh_data);
512 pbvh->pixels_ = nullptr;
513}
514
515} // namespace blender::bke::pbvh
CustomData interface, see also DNA_customdata_types.h.
const char * CustomData_get_active_layer_name(const CustomData *data, eCustomDataType type)
ImBuf * BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
void BKE_image_release_ibuf(Image *ima, ImBuf *ibuf, void *lock)
void BKE_image_partial_update_mark_full_update(Image *image)
Mark the whole image to be updated.
PBVHNodeFlags
Definition BKE_pbvh.hh:29
@ PBVH_RebuildPixels
Definition BKE_pbvh.hh:39
@ PBVH_TexLeaf
Definition BKE_pbvh.hh:40
@ PBVH_Leaf
Definition BKE_pbvh.hh:30
A BVH for high poly meshes.
#define BLI_assert(a)
Definition BLI_assert.h:50
struct Entry Entry
#define LISTBASE_FOREACH(type, var, list)
MINLINE float max_fff(float a, float b, float c)
MINLINE int min_ii(int a, int b)
MINLINE float clamp_f(float value, float min, float max)
MINLINE float min_fff(float a, float b, float c)
int barycentric_inside_triangle_v2(const float w[3])
void barycentric_weights_v2(const float v1[2], const float v2[2], const float v3[2], const float co[2], float w[3])
MINLINE void copy_v4_uchar(unsigned char r[4], unsigned char a)
MINLINE void copy_v4_fl(float r[4], float f)
@ CD_PROP_FLOAT2
Object is a sort of wrapper for general info.
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr bool is_empty() const
void append(const T &value)
IndexRange index_range() const
void reserve(const int64_t min_capacity)
Span< NodeT > nodes() const
pixels::PBVHData * pixels_
#define printf
const Depsgraph * depsgraph
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static bool is_inside(int x, int y, int cols, int rows)
Definition filesel.cc:768
ccl_gpu_kernel_postfix ccl_global KernelWorkTile * tiles
ccl_global const KernelWorkTile * tile
ccl_device_inline float2 floor(const float2 a)
ccl_device_inline float3 ceil(const float3 a)
static bool barycentric_weights(const float v1[3], const float v2[3], const float v3[3], const float co[3], const float n[3], float w[3])
void vert_tris_from_corner_tris(Span< int > corner_verts, Span< int3 > corner_tris, MutableSpan< int3 > vert_tris)
IndexRange face_triangles_range(OffsetIndices< int > faces, int face_i)
Definition BKE_mesh.hh:296
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:2846
NodeData & node_data_get(blender::bke::pbvh::Node &node)
static void update_geom_primitives(Tree &pbvh, const uv_islands::MeshData &mesh_data)
constexpr bool USE_WATERTIGHT_CHECK
static float2 calc_barycentric_delta(const float2 uvs[3], const float2 start_uv, const float2 end_uv)
void mark_image_dirty(blender::bke::pbvh::Node &node, Image &image, ImageUser &image_user)
static int count_nodes_to_update(Tree &pbvh)
static void apply_watertight_check(Tree &pbvh, Image &image, ImageUser &image_user)
void copy_update(blender::bke::pbvh::Tree &pbvh, Image &image, ImageUser &image_user, const uv_islands::MeshData &mesh_data)
static void do_encode_pixels(const uv_islands::MeshData &mesh_data, const uv_islands::UVIslandsMask &uv_masks, const UVPrimitiveLookup &uv_prim_lookup, Image &image, ImageUser &image_user, MeshNode &node)
static float2 calc_barycentric_delta_x(const ImBuf *image_buffer, const float2 uvs[3], const int x, const int y)
void collect_dirty_tiles(blender::bke::pbvh::Node &node, Vector< image::TileNumber > &r_dirty_tiles)
static bool should_pixels_be_updated(const Node &node)
static bool find_nodes_to_update(Tree &pbvh, Vector< MeshNode * > &r_nodes_to_update)
static void extract_barycentric_pixels(UDIMTilePixels &tile_data, const ImBuf *image_buffer, const uv_islands::UVIslandsMask &uv_mask, const int uv_island_index, const int uv_primitive_index, const float2 uvs[3], const float2 tile_offset, const int minx, const int miny, const int maxx, const int maxy)
static bool update_pixels(const Depsgraph &depsgraph, const Object &object, Tree &pbvh, Image &image, ImageUser &image_user)
PBVHData & data_get(blender::bke::pbvh::Tree &pbvh)
void node_pixels_free(blender::bke::pbvh::Node *node)
void pixels_free(blender::bke::pbvh::Tree *pbvh)
void build_pixels(const Depsgraph &depsgraph, Object &object, Image &image, ImageUser &image_user)
Span< float3 > vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:2482
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
VecBase< float, 2 > float2
VecBase< uint16_t, 2 > ushort2
unsigned char uint8_t
Definition stdint.h:78
unsigned __int64 uint64_t
Definition stdint.h:90
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
void mark_region(Image &image, const image::ImageTileWrapper &image_tile, ImBuf &image_buffer)
UDIMTilePixels * find_tile_data(const image::ImageTileWrapper &image_tile)
struct blender::bke::pbvh::pixels::NodeData::@53 flags
Vector< UVPrimitivePaintInput > uv_primitives
void collect_dirty_tiles(Vector< image::TileNumber > &r_dirty_tiles)
Entry(uv_islands::UVPrimitive *uv_primitive, uint64_t uv_island_index)
UVPrimitiveLookup(const uint64_t geom_primitive_len, uv_islands::UVIslands &uv_islands)
void add(const MeshData &mesh_data, const UVIslands &islands)
bool is_masked(const uint16_t island_index, const float2 uv) const
void add_tile(float2 udim_offset, ushort2 resolution)