Blender  V3.3
pbvh_pixels.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2022 Blender Foundation. All rights reserved. */
3 
4 #include "BKE_customdata.h"
5 #include "BKE_mesh_mapping.h"
6 #include "BKE_pbvh.h"
7 #include "BKE_pbvh_pixels.hh"
8 
9 #include "DNA_image_types.h"
10 #include "DNA_mesh_types.h"
11 #include "DNA_meshdata_types.h"
12 #include "DNA_object_types.h"
13 
14 #include "BLI_math.h"
15 #include "BLI_task.h"
16 
17 #include "BKE_image_wrappers.hh"
18 
19 #include "bmesh.h"
20 
21 #include "pbvh_intern.h"
22 
24 
29 constexpr bool USE_WATERTIGHT_CHECK = false;
30 
34 static float2 calc_barycentric_delta(const float2 uvs[3],
35  const float2 start_uv,
36  const float2 end_uv)
37 {
38 
39  float3 start_barycentric;
40  barycentric_weights_v2(uvs[0], uvs[1], uvs[2], start_uv, start_barycentric);
41  float3 end_barycentric;
42  barycentric_weights_v2(uvs[0], uvs[1], uvs[2], end_uv, end_barycentric);
43  float3 barycentric = end_barycentric - start_barycentric;
44  return float2(barycentric.x, barycentric.y);
45 }
46 
47 static float2 calc_barycentric_delta_x(const ImBuf *image_buffer,
48  const float2 uvs[3],
49  const int x,
50  const int y)
51 {
52  const float2 start_uv(float(x) / image_buffer->x, float(y) / image_buffer->y);
53  const float2 end_uv(float(x + 1) / image_buffer->x, float(y) / image_buffer->y);
54  return calc_barycentric_delta(uvs, start_uv, end_uv);
55 }
56 
58  const ImBuf *image_buffer,
59  const int triangle_index,
60  const float2 uvs[3],
61  const int minx,
62  const int miny,
63  const int maxx,
64  const int maxy)
65 {
66  for (int y = miny; y < maxy; y++) {
67  bool start_detected = false;
68  PackedPixelRow pixel_row;
69  pixel_row.triangle_index = triangle_index;
70  pixel_row.num_pixels = 0;
71  int x;
72 
73  for (x = minx; x < maxx; x++) {
74  float2 uv((float(x) + 0.5f) / image_buffer->x, (float(y) + 0.5f) / image_buffer->y);
76  barycentric_weights_v2(uvs[0], uvs[1], uvs[2], uv, barycentric_weights);
77 
79  if (!start_detected && is_inside) {
80  start_detected = true;
81  pixel_row.start_image_coordinate = ushort2(x, y);
83  }
84  else if (start_detected && !is_inside) {
85  break;
86  }
87  }
88 
89  if (!start_detected) {
90  continue;
91  }
92  pixel_row.num_pixels = x - pixel_row.start_image_coordinate.x;
93  tile_data.pixel_rows.append(pixel_row);
94  }
95 }
96 
97 static void init_triangles(PBVH *pbvh, PBVHNode *node, NodeData *node_data, const MLoop *mloop)
98 {
99  for (int i = 0; i < node->totprim; i++) {
100  const MLoopTri *lt = &pbvh->looptri[node->prim_indices[i]];
101  node_data->triangles.append(
102  int3(mloop[lt->tri[0]].v, mloop[lt->tri[1]].v, mloop[lt->tri[2]].v));
103  }
104 }
105 
112 };
113 
114 static void do_encode_pixels(void *__restrict userdata,
115  const int n,
116  const TaskParallelTLS *__restrict UNUSED(tls))
117 {
118  EncodePixelsUserData *data = static_cast<EncodePixelsUserData *>(userdata);
119  Image *image = data->image;
120  ImageUser image_user = *data->image_user;
121  PBVH *pbvh = data->pbvh;
122  PBVHNode *node = (*data->nodes)[n];
123  NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
124  LISTBASE_FOREACH (ImageTile *, tile, &data->image->tiles) {
125  image::ImageTileWrapper image_tile(tile);
126  image_user.tile = image_tile.get_tile_number();
127  ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &image_user, nullptr);
128  if (image_buffer == nullptr) {
129  continue;
130  }
131 
132  float2 tile_offset = float2(image_tile.get_tile_offset());
133  UDIMTilePixels tile_data;
134 
135  Triangles &triangles = node_data->triangles;
136  for (int triangle_index = 0; triangle_index < triangles.size(); triangle_index++) {
137  const MLoopTri *lt = &pbvh->looptri[node->prim_indices[triangle_index]];
138  float2 uvs[3] = {
139  float2(data->ldata_uv[lt->tri[0]].uv) - tile_offset,
140  float2(data->ldata_uv[lt->tri[1]].uv) - tile_offset,
141  float2(data->ldata_uv[lt->tri[2]].uv) - tile_offset,
142  };
143 
144  const float minv = clamp_f(min_fff(uvs[0].y, uvs[1].y, uvs[2].y), 0.0f, 1.0f);
145  const int miny = floor(minv * image_buffer->y);
146  const float maxv = clamp_f(max_fff(uvs[0].y, uvs[1].y, uvs[2].y), 0.0f, 1.0f);
147  const int maxy = min_ii(ceil(maxv * image_buffer->y), image_buffer->y);
148  const float minu = clamp_f(min_fff(uvs[0].x, uvs[1].x, uvs[2].x), 0.0f, 1.0f);
149  const int minx = floor(minu * image_buffer->x);
150  const float maxu = clamp_f(max_fff(uvs[0].x, uvs[1].x, uvs[2].x), 0.0f, 1.0f);
151  const int maxx = min_ii(ceil(maxu * image_buffer->x), image_buffer->x);
152 
153  TrianglePaintInput &triangle = triangles.get_paint_input(triangle_index);
154  triangle.delta_barycentric_coord_u = calc_barycentric_delta_x(image_buffer, uvs, minx, miny);
156  tile_data, image_buffer, triangle_index, uvs, minx, miny, maxx, maxy);
157  }
158 
159  BKE_image_release_ibuf(image, image_buffer, nullptr);
160 
161  if (tile_data.pixel_rows.is_empty()) {
162  continue;
163  }
164 
165  tile_data.tile_number = image_tile.get_tile_number();
166  node_data->tiles.append(tile_data);
167  }
168 }
169 
171 {
172  if ((node->flag & PBVH_Leaf) == 0) {
173  return false;
174  }
175  if ((node->flag & PBVH_RebuildPixels) != 0) {
176  return true;
177  }
178  NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
179  if (node_data != nullptr) {
180  return false;
181  }
182  return true;
183 }
184 
186 {
187  int64_t result = 0;
188  for (int n = 0; n < pbvh->totnode; n++) {
189  PBVHNode *node = &pbvh->nodes[n];
191  result++;
192  }
193  }
194  return result;
195 }
196 
206 static bool find_nodes_to_update(PBVH *pbvh, Vector<PBVHNode *> &r_nodes_to_update)
207 {
208  int64_t nodes_to_update_len = count_nodes_to_update(pbvh);
209  if (nodes_to_update_len == 0) {
210  return false;
211  }
212 
213  r_nodes_to_update.reserve(nodes_to_update_len);
214 
215  for (int n = 0; n < pbvh->totnode; n++) {
216  PBVHNode *node = &pbvh->nodes[n];
218  continue;
219  }
220  r_nodes_to_update.append(node);
221  node->flag = static_cast<PBVHNodeFlags>(node->flag | PBVH_RebuildPixels);
222 
223  if (node->pixels.node_data == nullptr) {
224  NodeData *node_data = MEM_new<NodeData>(__func__);
225  node->pixels.node_data = node_data;
226  }
227  else {
228  NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
229  node_data->clear_data();
230  }
231  }
232 
233  return true;
234 }
235 
236 static void apply_watertight_check(PBVH *pbvh, Image *image, ImageUser *image_user)
237 {
238  ImageUser watertight = *image_user;
239  LISTBASE_FOREACH (ImageTile *, tile_data, &image->tiles) {
240  image::ImageTileWrapper image_tile(tile_data);
241  watertight.tile = image_tile.get_tile_number();
242  ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &watertight, nullptr);
243  if (image_buffer == nullptr) {
244  continue;
245  }
246  for (int n = 0; n < pbvh->totnode; n++) {
247  PBVHNode *node = &pbvh->nodes[n];
248  if ((node->flag & PBVH_Leaf) == 0) {
249  continue;
250  }
251  NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
252  UDIMTilePixels *tile_node_data = node_data->find_tile_data(image_tile);
253  if (tile_node_data == nullptr) {
254  continue;
255  }
256 
257  for (PackedPixelRow &pixel_row : tile_node_data->pixel_rows) {
258  int pixel_offset = pixel_row.start_image_coordinate.y * image_buffer->x +
259  pixel_row.start_image_coordinate.x;
260  for (int x = 0; x < pixel_row.num_pixels; x++) {
261  if (image_buffer->rect_float) {
262  copy_v4_fl(&image_buffer->rect_float[pixel_offset * 4], 1.0);
263  }
264  if (image_buffer->rect) {
265  uint8_t *dest = static_cast<uint8_t *>(
266  static_cast<void *>(&image_buffer->rect[pixel_offset]));
267  copy_v4_uchar(dest, 255);
268  }
269  pixel_offset += 1;
270  }
271  }
272  }
273  BKE_image_release_ibuf(image, image_buffer, nullptr);
274  }
276 }
277 
278 static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image_user)
279 {
280  Vector<PBVHNode *> nodes_to_update;
281 
282  if (!find_nodes_to_update(pbvh, nodes_to_update)) {
283  return;
284  }
285 
286  const MLoopUV *ldata_uv = static_cast<const MLoopUV *>(
288  if (ldata_uv == nullptr) {
289  return;
290  }
291 
292  for (PBVHNode *node : nodes_to_update) {
293  NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
294  init_triangles(pbvh, node, node_data, mesh->mloop);
295  }
296 
298  user_data.pbvh = pbvh;
299  user_data.image = image;
300  user_data.image_user = image_user;
301  user_data.ldata_uv = ldata_uv;
302  user_data.nodes = &nodes_to_update;
303 
304  TaskParallelSettings settings;
305  BKE_pbvh_parallel_range_settings(&settings, true, nodes_to_update.size());
306  BLI_task_parallel_range(0, nodes_to_update.size(), &user_data, do_encode_pixels, &settings);
307  if (USE_WATERTIGHT_CHECK) {
308  apply_watertight_check(pbvh, image, image_user);
309  }
310 
311  /* Rebuild the undo regions. */
312  for (PBVHNode *node : nodes_to_update) {
313  NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
314  node_data->rebuild_undo_regions();
315  }
316 
317  /* Clear the UpdatePixels flag. */
318  for (PBVHNode *node : nodes_to_update) {
319  node->flag = static_cast<PBVHNodeFlags>(node->flag & ~PBVH_RebuildPixels);
320  }
321 
322 //#define DO_PRINT_STATISTICS
323 #ifdef DO_PRINT_STATISTICS
324  /* Print some statistics about compression ratio. */
325  {
326  int64_t compressed_data_len = 0;
327  int64_t num_pixels = 0;
328  for (int n = 0; n < pbvh->totnode; n++) {
329  PBVHNode *node = &pbvh->nodes[n];
330  if ((node->flag & PBVH_Leaf) == 0) {
331  continue;
332  }
333  NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
334  compressed_data_len += node_data->triangles.mem_size();
335  for (const UDIMTilePixels &tile_data : node_data->tiles) {
336  compressed_data_len += tile_data.encoded_pixels.size() * sizeof(PackedPixelRow);
337  for (const PackedPixelRow &encoded_pixels : tile_data.encoded_pixels) {
338  num_pixels += encoded_pixels.num_pixels;
339  }
340  }
341  }
342  printf("Encoded %lld pixels in %lld bytes (%f bytes per pixel)\n",
343  num_pixels,
344  compressed_data_len,
345  float(compressed_data_len) / num_pixels);
346  }
347 #endif
348 }
349 
351 {
352  BLI_assert(node.pixels.node_data != nullptr);
353  NodeData *node_data = static_cast<NodeData *>(node.pixels.node_data);
354  return *node_data;
355 }
356 
358 {
359  BLI_assert(node.pixels.node_data != nullptr);
360  NodeData *node_data = static_cast<NodeData *>(node.pixels.node_data);
361  if (node_data->flags.dirty) {
362  ImageUser local_image_user = image_user;
363  LISTBASE_FOREACH (ImageTile *, tile, &image.tiles) {
364  image::ImageTileWrapper image_tile(tile);
365  local_image_user.tile = image_tile.get_tile_number();
366  ImBuf *image_buffer = BKE_image_acquire_ibuf(&image, &local_image_user, nullptr);
367  if (image_buffer == nullptr) {
368  continue;
369  }
370 
371  node_data->mark_region(image, image_tile, *image_buffer);
372  BKE_image_release_ibuf(&image, image_buffer, nullptr);
373  }
374  node_data->flags.dirty = false;
375  }
376 }
377 
378 } // namespace blender::bke::pbvh::pixels
379 
380 extern "C" {
381 using namespace blender::bke::pbvh::pixels;
382 
384 {
385  update_pixels(pbvh, mesh, image, image_user);
386 }
387 
389 {
390  NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
391  MEM_delete(node_data);
392  node->pixels.node_data = nullptr;
393 }
394 }
CustomData interface, see also DNA_customdata_types.h.
void * CustomData_get_layer(const struct CustomData *data, int type)
void BKE_image_release_ibuf(struct Image *ima, struct ImBuf *ibuf, void *lock)
struct ImBuf * BKE_image_acquire_ibuf(struct Image *ima, struct ImageUser *iuser, void **r_lock)
void BKE_image_partial_update_mark_full_update(struct Image *image)
Mark the whole image to be updated.
A BVH for high poly meshes.
void BKE_pbvh_parallel_range_settings(struct TaskParallelSettings *settings, bool use_threading, int totnode)
Definition: pbvh.c:3211
PBVHNodeFlags
Definition: BKE_pbvh.h:63
@ PBVH_RebuildPixels
Definition: BKE_pbvh.h:81
@ PBVH_Leaf
Definition: BKE_pbvh.h:64
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
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])
Definition: math_geom.c:3663
void barycentric_weights_v2(const float v1[2], const float v2[2], const float v3[2], const float co[2], float w[3])
Definition: math_geom.c:3700
MINLINE void copy_v4_uchar(unsigned char r[4], unsigned char a)
MINLINE void copy_v4_fl(float r[4], float f)
void BLI_task_parallel_range(int start, int stop, void *userdata, TaskParallelRangeFunc func, const TaskParallelSettings *settings)
Definition: task_range.cc:94
#define UNUSED(x)
@ CD_MLOOPUV
Object is a sort of wrapper for general info.
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
int64_t size() const
Definition: BLI_vector.hh:694
void append(const T &value)
Definition: BLI_vector.hh:433
void reserve(const int64_t min_capacity)
Definition: BLI_vector.hh:340
OperationNode * node
void * user_data
SyclQueue void * dest
depth_tx normal_tx diffuse_light_tx specular_light_tx volume_light_tx environment_tx ambient_occlusion_tx aov_value_tx in_weight_img image(1, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "out_weight_img") .image(3
static bool is_inside(int x, int y, int cols, int rows)
Definition: filesel.c:706
ccl_gpu_kernel_postfix ccl_global float int num_pixels
ccl_global const KernelWorkTile * tile
ccl_device_inline float3 ceil(const float3 &a)
Definition: math_float3.h:363
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])
Definition: math_geom.c:3571
NodeData & BKE_pbvh_pixels_node_data_get(PBVHNode &node)
Definition: pbvh_pixels.cc:350
constexpr bool USE_WATERTIGHT_CHECK
Definition: pbvh_pixels.cc:29
static void do_encode_pixels(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls))
Definition: pbvh_pixels.cc:114
static float2 calc_barycentric_delta(const float2 uvs[3], const float2 start_uv, const float2 end_uv)
Definition: pbvh_pixels.cc:34
static void init_triangles(PBVH *pbvh, PBVHNode *node, NodeData *node_data, const MLoop *mloop)
Definition: pbvh_pixels.cc:97
static void extract_barycentric_pixels(UDIMTilePixels &tile_data, const ImBuf *image_buffer, const int triangle_index, const float2 uvs[3], const int minx, const int miny, const int maxx, const int maxy)
Definition: pbvh_pixels.cc:57
static int64_t count_nodes_to_update(PBVH *pbvh)
Definition: pbvh_pixels.cc:185
static bool should_pixels_be_updated(PBVHNode *node)
Definition: pbvh_pixels.cc:170
static float2 calc_barycentric_delta_x(const ImBuf *image_buffer, const float2 uvs[3], const int x, const int y)
Definition: pbvh_pixels.cc:47
static void apply_watertight_check(PBVH *pbvh, Image *image, ImageUser *image_user)
Definition: pbvh_pixels.cc:236
void BKE_pbvh_pixels_mark_image_dirty(PBVHNode &node, Image &image, ImageUser &image_user)
Definition: pbvh_pixels.cc:357
static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image_user)
Definition: pbvh_pixels.cc:278
static bool find_nodes_to_update(PBVH *pbvh, Vector< PBVHNode * > &r_nodes_to_update)
Definition: pbvh_pixels.cc:206
T floor(const T &a)
vec_base< uint16_t, 2 > ushort2
vec_base< float, 2 > float2
vec_base< int32_t, 3 > int3
void pbvh_pixels_free(PBVHNode *node)
Definition: pbvh_pixels.cc:388
void BKE_pbvh_build_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image_user)
Definition: pbvh_pixels.cc:383
__int64 int64_t
Definition: stdint.h:89
unsigned char uint8_t
Definition: stdint.h:78
unsigned int * rect
float * rect_float
unsigned int tri[3]
unsigned int v
struct MLoop * mloop
CustomData ldata
const struct MLoopTri * looptri
Definition: pbvh_intern.h:154
int totnode
Definition: pbvh_intern.h:137
PBVHNode * nodes
Definition: pbvh_intern.h:136
void mark_region(Image &image, const image::ImageTileWrapper &image_tile, ImBuf &image_buffer)
struct blender::bke::pbvh::pixels::NodeData::@58 flags
UDIMTilePixels * find_tile_data(const image::ImageTileWrapper &image_tile)
void append(const int3 vert_indices)
TrianglePaintInput & get_paint_input(const int index)