Blender V5.0
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_paint_bvh.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
18#include "BKE_image_wrappers.hh"
19#include "BKE_paint.hh"
20
21#include "pbvh_intern.hh"
22#include "pbvh_pixels_copy.hh"
23#include "pbvh_uv_islands.hh"
24
26
31 const float2 start_uv,
32 const float2 end_uv)
33{
34
35 float3 start_barycentric;
36 barycentric_weights_v2(uvs[0], uvs[1], uvs[2], start_uv, start_barycentric);
37 float3 end_barycentric;
38 barycentric_weights_v2(uvs[0], uvs[1], uvs[2], end_uv, end_barycentric);
39 float3 barycentric = end_barycentric - start_barycentric;
40 return float2(barycentric.x, barycentric.y);
41}
42
43static float2 calc_barycentric_delta_x(const ImBuf *image_buffer,
44 const float2 uvs[3],
45 const int x,
46 const int y)
47{
48 const float2 start_uv(float(x) / image_buffer->x, float(y) / image_buffer->y);
49 const float2 end_uv(float(x + 1) / image_buffer->x, float(y) / image_buffer->y);
50 return calc_barycentric_delta(uvs, start_uv, end_uv);
51}
52
57constexpr bool USE_WATERTIGHT_CHECK = false;
58
60 const ImBuf *image_buffer,
61 const uv_islands::UVIslandsMask &uv_mask,
62 const int uv_island_index,
63 const int uv_primitive_index,
64 const float2 uvs[3],
65 const float2 tile_offset,
66 const int minx,
67 const int miny,
68 const int maxx,
69 const int maxy)
70{
71 for (int y = miny; y < maxy; y++) {
72 bool start_detected = false;
73 PackedPixelRow pixel_row;
74 pixel_row.uv_primitive_index = uv_primitive_index;
75 pixel_row.num_pixels = 0;
76 int x;
77
78 for (x = minx; x < maxx; x++) {
79 float2 uv((float(x) + 0.5f) / image_buffer->x, (float(y) + 0.5f) / image_buffer->y);
81 barycentric_weights_v2(uvs[0], uvs[1], uvs[2], uv, barycentric_weights);
82
84 const bool is_masked = uv_mask.is_masked(uv_island_index, uv + tile_offset);
85 if (!start_detected && is_inside && is_masked) {
86 start_detected = true;
87 pixel_row.start_image_coordinate = ushort2(x, y);
89 }
90 else if (start_detected && (!is_inside || !is_masked)) {
91 break;
92 }
93 }
94
95 if (!start_detected) {
96 continue;
97 }
98 pixel_row.num_pixels = x - pixel_row.start_image_coordinate.x;
99 tile_data.pixel_rows.append(pixel_row);
100 }
101}
102
105{
106 PBVHData &pbvh_data = data_get(pbvh);
107 pbvh_data.vert_tris.reinitialize(mesh_data.corner_tris.size());
109 mesh_data.corner_verts, mesh_data.corner_tris, pbvh_data.vert_tris);
110}
111
122
124
126 {
127 lookup.append_n_times(Vector<Entry>(), geom_primitive_len);
128
129 uint64_t uv_island_index = 0;
130 for (uv_islands::UVIsland &uv_island : uv_islands.islands) {
131 for (uv_islands::UVPrimitive &uv_primitive : uv_island.uv_primitives) {
132 lookup[uv_primitive.primitive_i].append_as(Entry(&uv_primitive, uv_island_index));
133 }
134 uv_island_index++;
135 }
136 }
137};
138
139static void do_encode_pixels(const uv_islands::MeshData &mesh_data,
140 const uv_islands::UVIslandsMask &uv_masks,
141 const UVPrimitiveLookup &uv_prim_lookup,
142 Image &image,
143 ImageUser &image_user,
144 MeshNode &node)
145{
146 NodeData *node_data = static_cast<NodeData *>(node.pixels_);
147
148 LISTBASE_FOREACH (ImageTile *, tile, &image.tiles) {
149 image::ImageTileWrapper image_tile(tile);
150 image_user.tile = image_tile.get_tile_number();
151 ImBuf *image_buffer = BKE_image_acquire_ibuf(&image, &image_user, nullptr);
152 if (image_buffer == nullptr) {
153 continue;
154 }
155
156 UDIMTilePixels tile_data;
157 tile_data.tile_number = image_tile.get_tile_number();
158 float2 tile_offset = float2(image_tile.get_tile_offset());
159
160 for (const int face : node.faces()) {
161 for (const int tri : bke::mesh::face_triangles_range(mesh_data.faces, face)) {
162 for (const UVPrimitiveLookup::Entry &entry : uv_prim_lookup.lookup[tri]) {
163 uv_islands::UVBorder uv_border = entry.uv_primitive->extract_border();
164 float2 uvs[3] = {
165 entry.uv_primitive->get_uv_vertex(mesh_data, 0)->uv - tile_offset,
166 entry.uv_primitive->get_uv_vertex(mesh_data, 1)->uv - tile_offset,
167 entry.uv_primitive->get_uv_vertex(mesh_data, 2)->uv - tile_offset,
168 };
169 const float minv = clamp_f(min_fff(uvs[0].y, uvs[1].y, uvs[2].y), 0.0f, 1.0f);
170 const int miny = floor(minv * image_buffer->y);
171 const float maxv = clamp_f(max_fff(uvs[0].y, uvs[1].y, uvs[2].y), 0.0f, 1.0f);
172 const int maxy = min_ii(ceil(maxv * image_buffer->y), image_buffer->y);
173 const float minu = clamp_f(min_fff(uvs[0].x, uvs[1].x, uvs[2].x), 0.0f, 1.0f);
174 const int minx = floor(minu * image_buffer->x);
175 const float maxu = clamp_f(max_fff(uvs[0].x, uvs[1].x, uvs[2].x), 0.0f, 1.0f);
176 const int maxx = min_ii(ceil(maxu * image_buffer->x), image_buffer->x);
177
178 /* TODO: Perform bounds check */
179 int uv_prim_index = node_data->uv_primitives.size();
180 node_data->uv_primitives.append(tri);
181 UVPrimitivePaintInput &paint_input = node_data->uv_primitives.last();
182
183 /* Calculate barycentric delta */
185 image_buffer, uvs, minx, miny);
186
187 /* Extract the pixels. */
189 image_buffer,
190 uv_masks,
191 entry.uv_island_index,
192 uv_prim_index,
193 uvs,
194 tile_offset,
195 minx,
196 miny,
197 maxx,
198 maxy);
199 }
200 }
201 }
202 BKE_image_release_ibuf(&image, image_buffer, nullptr);
203
204 if (tile_data.pixel_rows.is_empty()) {
205 continue;
206 }
207
208 node_data->tiles.append(tile_data);
209 }
210}
211
212static bool should_pixels_be_updated(const Node &node)
213{
214 if ((node.flag_ & (Node::Leaf | Node::TexLeaf)) == 0) {
215 return false;
216 }
217 if (node.children_offset_ != 0) {
218 return false;
219 }
220 if ((node.flag_ & Node::RebuildPixels) != 0) {
221 return true;
222 }
223 NodeData *node_data = static_cast<NodeData *>(node.pixels_);
224 if (node_data != nullptr) {
225 return false;
226 }
227 return true;
228}
229
231{
232 int result = 0;
233 for (Node &node : pbvh.nodes<MeshNode>()) {
234 if (should_pixels_be_updated(node)) {
235 result++;
236 }
237 }
238 return result;
239}
240
250static bool find_nodes_to_update(Tree &pbvh, Vector<MeshNode *> &r_nodes_to_update)
251{
252 int nodes_to_update_len = count_nodes_to_update(pbvh);
253 if (nodes_to_update_len == 0) {
254 return false;
255 }
256
257 /* Init or reset Tree pixel data when changes detected. */
258 if (pbvh.pixels_ == nullptr) {
259 PBVHData *pbvh_data = MEM_new<PBVHData>(__func__);
260 pbvh.pixels_ = pbvh_data;
261 }
262 else {
263 PBVHData *pbvh_data = static_cast<PBVHData *>(pbvh.pixels_);
264 pbvh_data->clear_data();
265 }
266
267 r_nodes_to_update.reserve(nodes_to_update_len);
268
269 for (MeshNode &node : pbvh.nodes<MeshNode>()) {
270 if (!should_pixels_be_updated(node)) {
271 continue;
272 }
273 r_nodes_to_update.append(&node);
274 node.flag_ = (node.flag_ | Node::RebuildPixels);
275
276 if (node.pixels_ == nullptr) {
277 NodeData *node_data = MEM_new<NodeData>(__func__);
278 node.pixels_ = node_data;
279 }
280 else {
281 NodeData *node_data = static_cast<NodeData *>(node.pixels_);
282 node_data->clear_data();
283 }
284 }
285
286 return true;
287}
288
290{
291 ImageUser watertight = image_user;
292 LISTBASE_FOREACH (ImageTile *, tile_data, &image.tiles) {
293 image::ImageTileWrapper image_tile(tile_data);
294 watertight.tile = image_tile.get_tile_number();
295 ImBuf *image_buffer = BKE_image_acquire_ibuf(&image, &watertight, nullptr);
296 if (image_buffer == nullptr) {
297 continue;
298 }
299 for (Node &node : pbvh.nodes<MeshNode>()) {
300 if ((node.flag_ & Node::Leaf) == 0) {
301 continue;
302 }
303 NodeData *node_data = static_cast<NodeData *>(node.pixels_);
304 UDIMTilePixels *tile_node_data = node_data->find_tile_data(image_tile);
305 if (tile_node_data == nullptr) {
306 continue;
307 }
308
309 for (PackedPixelRow &pixel_row : tile_node_data->pixel_rows) {
310 int pixel_offset = pixel_row.start_image_coordinate.y * image_buffer->x +
311 pixel_row.start_image_coordinate.x;
312 for (int x = 0; x < pixel_row.num_pixels; x++) {
313 if (image_buffer->float_buffer.data) {
314 copy_v4_fl(&image_buffer->float_buffer.data[pixel_offset * 4], 1.0);
315 }
316 if (image_buffer->byte_buffer.data) {
317 uint8_t *dest = &image_buffer->byte_buffer.data[pixel_offset * 4];
318 dest[0] = dest[1] = dest[2] = dest[3] = 255;
319 }
320 pixel_offset += 1;
321 }
322 }
323 }
324 BKE_image_release_ibuf(&image, image_buffer, nullptr);
325 }
327}
328
329static bool update_pixels(const Depsgraph &depsgraph,
330 const Object &object,
331 Tree &pbvh,
332 Image &image,
333 ImageUser &image_user)
334{
335 Vector<MeshNode *> nodes_to_update;
336 if (!find_nodes_to_update(pbvh, nodes_to_update)) {
337 return false;
338 }
339
340 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
341 const StringRef active_uv_name = CustomData_get_active_layer_name(&mesh.corner_data,
343 if (active_uv_name.is_empty()) {
344 return false;
345 }
346
347 const AttributeAccessor attributes = mesh.attributes();
348 const VArraySpan uv_map = *attributes.lookup<float2>(active_uv_name, AttrDomain::Corner);
349
350 uv_islands::MeshData mesh_data(mesh.faces(),
351 mesh.corner_tris(),
352 mesh.corner_verts(),
353 uv_map,
355 uv_islands::UVIslands islands(mesh_data);
356
358 ImageUser tile_user = image_user;
359 LISTBASE_FOREACH (ImageTile *, tile_data, &image.tiles) {
360 image::ImageTileWrapper image_tile(tile_data);
361 tile_user.tile = image_tile.get_tile_number();
362 ImBuf *tile_buffer = BKE_image_acquire_ibuf(&image, &tile_user, nullptr);
363 if (tile_buffer == nullptr) {
364 continue;
365 }
366 uv_masks.add_tile(float2(image_tile.get_tile_x_offset(), image_tile.get_tile_y_offset()),
367 ushort2(tile_buffer->x, tile_buffer->y));
368 BKE_image_release_ibuf(&image, tile_buffer, nullptr);
369 }
370 uv_masks.add(mesh_data, islands);
371 uv_masks.dilate(image.seam_margin);
372
373 islands.extract_borders();
374 islands.extend_borders(mesh_data, uv_masks);
375 update_geom_primitives(pbvh, mesh_data);
376
377 UVPrimitiveLookup uv_primitive_lookup(mesh_data.corner_tris.size(), islands);
378
379 threading::parallel_for(nodes_to_update.index_range(), 1, [&](const IndexRange range) {
380 for (const int i : range) {
381 do_encode_pixels(
382 mesh_data, uv_masks, uv_primitive_lookup, image, image_user, *nodes_to_update[i]);
383 }
384 });
386 apply_watertight_check(pbvh, image, image_user);
387 }
388
389 /* Add solution for non-manifold parts of the model. */
390 copy_update(pbvh, image, image_user, mesh_data);
391
392 /* Rebuild the undo regions. */
393 for (Node *node : nodes_to_update) {
394 NodeData *node_data = static_cast<NodeData *>(node->pixels_);
395 node_data->rebuild_undo_regions();
396 }
397
398 /* Clear the UpdatePixels flag. */
399 for (Node *node : nodes_to_update) {
400 node->flag_ &= ~Node::RebuildPixels;
401 }
402
403 /* Add Node::TexLeaf flag */
404 for (Node &node : pbvh.nodes<MeshNode>()) {
405 if (node.flag_ & Node::Leaf) {
406 node.flag_ |= Node::TexLeaf;
407 }
408 }
409
410// #define DO_PRINT_STATISTICS
411#ifdef DO_PRINT_STATISTICS
412 /* Print some statistics about compression ratio. */
413 {
414 int compressed_data_len = 0;
415 int num_pixels = 0;
416 for (int n = 0; n < pbvh->totnode; n++) {
417 Node *node = &pbvh->nodes[n];
418 if ((node->flag & Node::Leaf) == 0) {
419 continue;
420 }
421 NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
422 for (const UDIMTilePixels &tile_data : node_data->tiles) {
423 compressed_data_len += tile_data.encoded_pixels.size() * sizeof(PackedPixelRow);
424 for (const PackedPixelRow &encoded_pixels : tile_data.encoded_pixels) {
425 num_pixels += encoded_pixels.num_pixels;
426 }
427 }
428 }
429 printf("Encoded %lld pixels in %lld bytes (%f bytes per pixel)\n",
430 num_pixels,
431 compressed_data_len,
432 float(compressed_data_len) / num_pixels);
433 }
434#endif
435
436 return true;
437}
438
440{
441 BLI_assert(node.pixels_ != nullptr);
442 NodeData *node_data = static_cast<NodeData *>(node.pixels_);
443 return *node_data;
444}
445
447{
448 BLI_assert(pbvh.pixels_ != nullptr);
449 PBVHData *data = static_cast<PBVHData *>(pbvh.pixels_);
450 return *data;
451}
452
453void mark_image_dirty(Node &node, Image &image, ImageUser &image_user)
454{
455 BLI_assert(node.pixels_ != nullptr);
456 NodeData *node_data = static_cast<NodeData *>(node.pixels_);
457 if (node_data->flags.dirty) {
458 ImageUser local_image_user = image_user;
459 LISTBASE_FOREACH (ImageTile *, tile, &image.tiles) {
460 image::ImageTileWrapper image_tile(tile);
461 local_image_user.tile = image_tile.get_tile_number();
462 ImBuf *image_buffer = BKE_image_acquire_ibuf(&image, &local_image_user, nullptr);
463 if (image_buffer == nullptr) {
464 continue;
465 }
466
467 node_data->mark_region(image, image_tile, *image_buffer);
468 BKE_image_release_ibuf(&image, image_buffer, nullptr);
469 }
470 node_data->flags.dirty = false;
471 }
472}
473
475{
476 NodeData *node_data = static_cast<NodeData *>(node.pixels_);
477 node_data->collect_dirty_tiles(r_dirty_tiles);
478}
479
480} // namespace blender::bke::pbvh::pixels
481
482namespace blender::bke::pbvh {
483
484void build_pixels(const Depsgraph &depsgraph, Object &object, Image &image, ImageUser &image_user)
485{
486 Tree &pbvh = *object::pbvh_get(object);
487 pixels::update_pixels(depsgraph, object, pbvh, image, image_user);
488}
489
491{
492 pixels::NodeData *node_data = static_cast<pixels::NodeData *>(node->pixels_);
493
494 if (!node_data) {
495 return;
496 }
497
498 MEM_delete(node_data);
499 node->pixels_ = nullptr;
500}
501
503{
504 pixels::PBVHData *pbvh_data = static_cast<pixels::PBVHData *>(pbvh->pixels_);
505 MEM_delete(pbvh_data);
506 pbvh->pixels_ = nullptr;
507}
508
509} // 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.
A BVH for high poly meshes.
#define BLI_assert(a)
Definition BLI_assert.h:46
#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_fl(float r[4], float f)
@ CD_PROP_FLOAT2
Object is a sort of wrapper for general info.
BMesh const char void * data
BPy_StructRNA * depsgraph
unsigned long long int uint64_t
constexpr bool is_empty() const
void append(const T &value)
IndexRange index_range() const
void reserve(const int64_t min_capacity)
GAttributeReader lookup(const StringRef attribute_id) const
pixels::NodeData * pixels_
static bool is_inside(int x, int y, int cols, int rows)
Definition filesel.cc:764
#define printf(...)
#define floor
#define ceil
const ccl_global KernelWorkTile * tile
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:359
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:3052
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:1040
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:93
VecBase< float, 2 > float2
VecBase< uint16_t, 2 > ushort2
VecBase< float, 3 > float3
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::@126372137103364156366300331140353251345266233066 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)
void extend_borders(const MeshData &mesh_data, const UVIslandsMask &islands_mask)