diff options
-rw-r--r-- | source/blender/blenkernel/BKE_pbvh.h | 10 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_pbvh_pixels.hh | 7 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/pbvh.c | 30 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/pbvh_intern.h | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/pbvh_pixels.cc | 372 | ||||
-rw-r--r-- | source/blender/draw/engines/basic/basic_engine.c | 12 | ||||
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_materials.c | 11 | ||||
-rw-r--r-- | source/blender/draw/intern/DRW_render.h | 4 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager_data.cc | 7 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 64 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 8 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_paint_color.c | 12 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_paint_image.cc | 71 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_undo.c | 2 |
14 files changed, 546 insertions, 66 deletions
diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 42cd1536dcf..c3100dd2345 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -125,7 +125,8 @@ typedef enum { PBVH_UpdateTopology = 1 << 13, PBVH_UpdateColor = 1 << 14, PBVH_RebuildPixels = 1 << 15, - PBVH_TopologyUpdated = 1 << 16, /* Used internally by pbvh_bmesh.c */ + PBVH_TexLeaf = 1 << 16, + PBVH_TopologyUpdated = 1 << 17, /* Used internally by pbvh_bmesh.c */ } PBVHNodeFlags; @@ -299,7 +300,12 @@ void BKE_pbvh_search_callback(PBVH *pbvh, void BKE_pbvh_search_gather( PBVH *pbvh, BKE_pbvh_SearchCallback scb, void *search_data, PBVHNode ***array, int *tot); - +void BKE_pbvh_search_gather_ex(PBVH *pbvh, + BKE_pbvh_SearchCallback scb, + void *search_data, + PBVHNode ***r_array, + int *r_tot, + PBVHNodeFlags leaf_flag); /* Ray-cast * the hit callback is called for all leaf nodes intersecting the ray; * it's up to the callback to find the primitive within the leaves that is diff --git a/source/blender/blenkernel/BKE_pbvh_pixels.hh b/source/blender/blenkernel/BKE_pbvh_pixels.hh index ad8eca2b36f..b8043a846b2 100644 --- a/source/blender/blenkernel/BKE_pbvh_pixels.hh +++ b/source/blender/blenkernel/BKE_pbvh_pixels.hh @@ -148,7 +148,7 @@ struct NodeData { Vector<UDIMTilePixels> tiles; Vector<UDIMTileUndo> undo_regions; - Triangles triangles; + Triangles *triangles = nullptr; NodeData() { @@ -169,6 +169,10 @@ struct NodeData { { undo_regions.clear(); for (UDIMTilePixels &tile : tiles) { + if (tile.pixel_rows.size() == 0) { + continue; + } + rcti region; BLI_rcti_init_minmax(®ion); for (PackedPixelRow &pixel_row : tile.pixel_rows) { @@ -201,7 +205,6 @@ struct NodeData { void clear_data() { tiles.clear(); - triangles.clear(); } static void free_func(void *instance) diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 65a906e6580..2b989885ebc 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -941,7 +941,9 @@ void BKE_pbvh_free(PBVH *pbvh) if (node->bm_other_verts) { BLI_gset_free(node->bm_other_verts, NULL); } + } + if (node->flag & (PBVH_Leaf | PBVH_TexLeaf)) { pbvh_pixels_free(node); } } @@ -1013,7 +1015,7 @@ static void pbvh_stack_push(PBVHIter *iter, PBVHNode *node, bool revisiting) iter->stacksize++; } -static PBVHNode *pbvh_iter_next(PBVHIter *iter) +static PBVHNode *pbvh_iter_next(PBVHIter *iter, PBVHNodeFlags leaf_flag) { /* purpose here is to traverse tree, visiting child nodes before their * parents, this order is necessary for e.g. computing bounding boxes */ @@ -1040,7 +1042,7 @@ static PBVHNode *pbvh_iter_next(PBVHIter *iter) continue; /* don't traverse, outside of search zone */ } - if (node->flag & PBVH_Leaf) { + if (node->flag & leaf_flag) { /* immediately hit leaf node */ return node; } @@ -1085,8 +1087,12 @@ static PBVHNode *pbvh_iter_next_occluded(PBVHIter *iter) return NULL; } -void BKE_pbvh_search_gather( - PBVH *pbvh, BKE_pbvh_SearchCallback scb, void *search_data, PBVHNode ***r_array, int *r_tot) +void BKE_pbvh_search_gather_ex(PBVH *pbvh, + BKE_pbvh_SearchCallback scb, + void *search_data, + PBVHNode ***r_array, + int *r_tot, + PBVHNodeFlags leaf_flag) { PBVHIter iter; PBVHNode **array = NULL, *node; @@ -1094,8 +1100,8 @@ void BKE_pbvh_search_gather( pbvh_iter_begin(&iter, pbvh, scb, search_data); - while ((node = pbvh_iter_next(&iter))) { - if (node->flag & PBVH_Leaf) { + while ((node = pbvh_iter_next(&iter, leaf_flag))) { + if (node->flag & leaf_flag) { if (UNLIKELY(tot == space)) { /* resize array if needed */ space = (tot == 0) ? 32 : space * 2; @@ -1118,6 +1124,12 @@ void BKE_pbvh_search_gather( *r_tot = tot; } +void BKE_pbvh_search_gather( + PBVH *pbvh, BKE_pbvh_SearchCallback scb, void *search_data, PBVHNode ***r_array, int *r_tot) +{ + BKE_pbvh_search_gather_ex(pbvh, scb, search_data, r_array, r_tot, PBVH_Leaf); +} + void BKE_pbvh_search_callback(PBVH *pbvh, BKE_pbvh_SearchCallback scb, void *search_data, @@ -1129,7 +1141,7 @@ void BKE_pbvh_search_callback(PBVH *pbvh, pbvh_iter_begin(&iter, pbvh, scb, search_data); - while ((node = pbvh_iter_next(&iter))) { + while ((node = pbvh_iter_next(&iter, PBVH_Leaf))) { if (node->flag & PBVH_Leaf) { hcb(node, hit_data); } @@ -1867,7 +1879,7 @@ void BKE_pbvh_redraw_BB(PBVH *pbvh, float bb_min[3], float bb_max[3]) pbvh_iter_begin(&iter, pbvh, NULL, NULL); - while ((node = pbvh_iter_next(&iter))) { + while ((node = pbvh_iter_next(&iter, PBVH_Leaf))) { if (node->flag & PBVH_UpdateRedraw) { BB_expand_with_bb(&bb, &node->vb); } @@ -1887,7 +1899,7 @@ void BKE_pbvh_get_grid_updates(PBVH *pbvh, bool clear, void ***r_gridfaces, int pbvh_iter_begin(&iter, pbvh, NULL, NULL); - while ((node = pbvh_iter_next(&iter))) { + while ((node = pbvh_iter_next(&iter, PBVH_Leaf))) { if (node->flag & PBVH_UpdateNormals) { for (uint i = 0; i < node->totprim; i++) { void *face = pbvh->gridfaces[node->prim_indices[i]]; diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index 368a9ffa1ea..34eb969adac 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -150,6 +150,8 @@ struct PBVH { int totvert; int leaf_limit; + int pixel_leaf_limit; + int depth_limit; /* Mesh data */ struct Mesh *mesh; diff --git a/source/blender/blenkernel/intern/pbvh_pixels.cc b/source/blender/blenkernel/intern/pbvh_pixels.cc index df616d4e087..cbbd25d3f4b 100644 --- a/source/blender/blenkernel/intern/pbvh_pixels.cc +++ b/source/blender/blenkernel/intern/pbvh_pixels.cc @@ -14,20 +14,18 @@ #include "BLI_math.h" #include "BLI_task.h" +#include "PIL_time.h" +#include "BKE_global.h" #include "BKE_image_wrappers.hh" #include "bmesh.h" #include "pbvh_intern.h" -namespace blender::bke::pbvh::pixels { +#include <atomic> -/** - * During debugging this check could be enabled. - * It will write to each image pixel that is covered by the PBVH. - */ -constexpr bool USE_WATERTIGHT_CHECK = false; +namespace blender::bke::pbvh::pixels { /** * Calculate the delta of two neighbor UV coordinates in the given image buffer. @@ -55,6 +53,313 @@ static float2 calc_barycentric_delta_x(const ImBuf *image_buffer, return calc_barycentric_delta(uvs, start_uv, end_uv); } +int count_node_pixels(PBVHNode &node) +{ + if (!node.pixels.node_data) { + return 0; + } + + NodeData &data = BKE_pbvh_pixels_node_data_get(node); + + int totpixel = 0; + + for (UDIMTilePixels &tile : data.tiles) { + for (PackedPixelRow &row : tile.pixel_rows) { + totpixel += row.num_pixels; + } + } + + return totpixel; +} + +struct SplitQueueData { + ThreadQueue *new_nodes; + TaskPool *pool; + + PBVH *pbvh; + Mesh *mesh; + Image *image; + ImageUser *image_user; +}; + +struct SplitNodePair { + SplitNodePair *parent; + PBVHNode node; + int children_offset = 0; + int depth = 0; + int source_index = -1; + bool is_old = false; + SplitQueueData *tdata; + + SplitNodePair(SplitNodePair *node_parent = nullptr) : parent(node_parent) + { + memset(static_cast<void *>(&node), 0, sizeof(PBVHNode)); + } +}; + +static void split_thread_job(TaskPool *__restrict pool, void *taskdata); + +static void split_pixel_node(PBVH *pbvh, + SplitNodePair *split, + Mesh *mesh, + Image *image, + ImageUser *image_user, + SplitQueueData *tdata) +{ + BB cb; + PBVHNode *node = &split->node; + + cb = node->vb; + + if (count_node_pixels(*node) <= pbvh->pixel_leaf_limit || split->depth >= pbvh->depth_limit) { + BKE_pbvh_pixels_node_data_get(split->node).rebuild_undo_regions(); + return; + } + + /* Find widest axis and its midpoint */ + const int axis = BB_widest_axis(&cb); + const float mid = (cb.bmax[axis] + cb.bmin[axis]) * 0.5f; + + node->flag = (PBVHNodeFlags)((int)node->flag & (int)~PBVH_TexLeaf); + + SplitNodePair *split1 = MEM_new<SplitNodePair>("split_pixel_node split1", split); + SplitNodePair *split2 = MEM_new<SplitNodePair>("split_pixel_node split1", split); + + split1->depth = split->depth + 1; + split2->depth = split->depth + 1; + + PBVHNode *child1 = &split1->node; + PBVHNode *child2 = &split2->node; + + child1->flag = PBVH_TexLeaf; + child2->flag = PBVH_TexLeaf; + + child1->vb = cb; + child1->vb.bmax[axis] = mid; + + child2->vb = cb; + child2->vb.bmin[axis] = mid; + + NodeData &data = BKE_pbvh_pixels_node_data_get(split->node); + + NodeData *data1 = MEM_new<NodeData>(__func__); + NodeData *data2 = MEM_new<NodeData>(__func__); + child1->pixels.node_data = static_cast<void *>(data1); + child2->pixels.node_data = static_cast<void *>(data2); + + data1->triangles = data.triangles; + data2->triangles = data.triangles; + + data1->tiles.resize(data.tiles.size()); + data2->tiles.resize(data.tiles.size()); + + for (int i : IndexRange(data.tiles.size())) { + UDIMTilePixels &tile = data.tiles[i]; + UDIMTilePixels &tile1 = data1->tiles[i]; + UDIMTilePixels &tile2 = data2->tiles[i]; + + tile1.tile_number = tile2.tile_number = tile.tile_number; + tile1.flags.dirty = tile2.flags.dirty = 0; + } + + ImageUser image_user2 = *image_user; + + for (int i : IndexRange(data.tiles.size())) { + const UDIMTilePixels &tile = data.tiles[i]; + + image_user2.tile = tile.tile_number; + + ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &image_user2, nullptr); + if (image_buffer == nullptr) { + continue; + } + + const MVert *mvert = BKE_pbvh_get_verts(pbvh); + + for (const PackedPixelRow &row : tile.pixel_rows) { + UDIMTilePixels *tile1 = &data1->tiles[i]; + UDIMTilePixels *tile2 = &data2->tiles[i]; + + TrianglePaintInput &tri = data.triangles->paint_input[row.triangle_index]; + + float verts[3][3]; + + copy_v3_v3(verts[0], mvert[tri.vert_indices[0]].co); + copy_v3_v3(verts[1], mvert[tri.vert_indices[1]].co); + copy_v3_v3(verts[2], mvert[tri.vert_indices[2]].co); + + float2 delta = tri.delta_barycentric_coord_u; + float2 uv1 = row.start_barycentric_coord; + float2 uv2 = row.start_barycentric_coord + delta * (float)row.num_pixels; + + float co1[3]; + float co2[3]; + + interp_barycentric_tri_v3(verts, uv1[0], uv1[1], co1); + interp_barycentric_tri_v3(verts, uv2[0], uv2[1], co2); + + /* Are we spanning the midpoint? */ + if ((co1[axis] <= mid) != (co2[axis] <= mid)) { + PackedPixelRow row1 = row; + float t; + + if (mid < co1[axis]) { + t = 1.0f - (mid - co2[axis]) / (co1[axis] - co2[axis]); + + SWAP(UDIMTilePixels *, tile1, tile2); + } + else { + t = (mid - co1[axis]) / (co2[axis] - co1[axis]); + } + + int num_pixels = (int)floorf((float)row.num_pixels * t); + + if (num_pixels) { + row1.num_pixels = num_pixels; + tile1->pixel_rows.append(row1); + } + + if (num_pixels != row.num_pixels) { + PackedPixelRow row2 = row; + + row2.num_pixels = row.num_pixels - num_pixels; + + row2.start_barycentric_coord = row.start_barycentric_coord + + tri.delta_barycentric_coord_u * (float)num_pixels; + row2.start_image_coordinate = row.start_image_coordinate; + row2.start_image_coordinate[0] += num_pixels; + + tile2->pixel_rows.append(row2); + } + } + else if (co1[axis] <= mid && co2[axis] <= mid) { + tile1->pixel_rows.append(row); + } + else { + tile2->pixel_rows.append(row); + } + } + + BKE_image_release_ibuf(image, image_buffer, nullptr); + } + + if (node->flag & PBVH_Leaf) { + data.clear_data(); + } + else { + pbvh_pixels_free(node); + } + + data.undo_regions.clear(); + + BLI_thread_queue_push(tdata->new_nodes, static_cast<void *>(split1)); + BLI_thread_queue_push(tdata->new_nodes, static_cast<void *>(split2)); + + BLI_task_pool_push(tdata->pool, split_thread_job, static_cast<void *>(split1), false, nullptr); + BLI_task_pool_push(tdata->pool, split_thread_job, static_cast<void *>(split2), false, nullptr); +} + +static void split_flush_final_nodes(SplitQueueData *tdata) +{ + PBVH *pbvh = tdata->pbvh; + Vector<SplitNodePair *> splits; + + while (!BLI_thread_queue_is_empty(tdata->new_nodes)) { + SplitNodePair *newsplit = static_cast<SplitNodePair *>(BLI_thread_queue_pop(tdata->new_nodes)); + + splits.append(newsplit); + + if (newsplit->is_old) { + continue; + } + + if (!newsplit->parent->children_offset) { + newsplit->parent->children_offset = pbvh->totnode; + + pbvh_grow_nodes(pbvh, pbvh->totnode + 2); + newsplit->source_index = newsplit->parent->children_offset; + } + else { + newsplit->source_index = newsplit->parent->children_offset + 1; + } + } + + for (SplitNodePair *split : splits) { + BLI_assert(split->source_index != -1); + + split->node.children_offset = split->children_offset; + pbvh->nodes[split->source_index] = split->node; + } + + for (SplitNodePair *split : splits) { + MEM_delete<SplitNodePair>(split); + } +} + +static void split_thread_job(TaskPool *__restrict pool, void *taskdata) +{ + + SplitQueueData *tdata = static_cast<SplitQueueData *>(BLI_task_pool_user_data(pool)); + SplitNodePair *split = static_cast<SplitNodePair *>(taskdata); + + split_pixel_node(tdata->pbvh, split, tdata->mesh, tdata->image, tdata->image_user, tdata); +} + +static void split_pixel_nodes(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image_user) +{ + if (G.debug_value == 891) { + return; + } + + if (!pbvh->depth_limit) { + pbvh->depth_limit = 40; /* TODO: move into a constant */ + } + + if (!pbvh->pixel_leaf_limit) { + pbvh->pixel_leaf_limit = 256 * 256; /* TODO: move into a constant */ + } + + SplitQueueData tdata; + TaskPool *pool = BLI_task_pool_create_suspended(&tdata, TASK_PRIORITY_HIGH); + + tdata.pool = pool; + tdata.pbvh = pbvh; + tdata.mesh = mesh; + tdata.image = image; + tdata.image_user = image_user; + + tdata.new_nodes = BLI_thread_queue_init(); + + /* Set up initial jobs before initializing threads. */ + for (int i : IndexRange(pbvh->totnode)) { + if (pbvh->nodes[i].flag & PBVH_TexLeaf) { + SplitNodePair *split = MEM_new<SplitNodePair>("split_pixel_nodes split"); + + split->source_index = i; + split->is_old = true; + split->node = pbvh->nodes[i]; + split->tdata = &tdata; + + BLI_task_pool_push(pool, split_thread_job, static_cast<void *>(split), false, nullptr); + + BLI_thread_queue_push(tdata.new_nodes, static_cast<void *>(split)); + } + } + + BLI_task_pool_work_and_wait(pool); + BLI_task_pool_free(pool); + + split_flush_final_nodes(&tdata); + + BLI_thread_queue_free(tdata.new_nodes); +} + +/** + * During debugging this check could be enabled. + * It will write to each image pixel that is covered by the PBVH. + */ +constexpr bool USE_WATERTIGHT_CHECK = false; + static void extract_barycentric_pixels(UDIMTilePixels &tile_data, const ImBuf *image_buffer, const int triangle_index, @@ -97,9 +402,15 @@ static void extract_barycentric_pixels(UDIMTilePixels &tile_data, static void init_triangles(PBVH *pbvh, PBVHNode *node, NodeData *node_data, const MLoop *mloop) { + if (node_data->triangles) { + MEM_delete<Triangles>(node_data->triangles); + } + + node_data->triangles = MEM_new<Triangles>("triangles"); + for (int i = 0; i < node->totprim; i++) { const MLoopTri *lt = &pbvh->looptri[node->prim_indices[i]]; - node_data->triangles.append( + node_data->triangles->append( int3(mloop[lt->tri[0]].v, mloop[lt->tri[1]].v, mloop[lt->tri[2]].v)); } } @@ -133,7 +444,7 @@ static void do_encode_pixels(void *__restrict userdata, float2 tile_offset = float2(image_tile.get_tile_offset()); UDIMTilePixels tile_data; - Triangles &triangles = node_data->triangles; + Triangles &triangles = *node_data->triangles; for (int triangle_index = 0; triangle_index < triangles.size(); triangle_index++) { const MLoopTri *lt = &pbvh->looptri[node->prim_indices[triangle_index]]; float2 uvs[3] = { @@ -173,6 +484,9 @@ static bool should_pixels_be_updated(PBVHNode *node) if ((node->flag & PBVH_Leaf) == 0) { return false; } + if (node->children_offset != 0) { + return false; + } if ((node->flag & PBVH_RebuildPixels) != 0) { return true; } @@ -228,6 +542,11 @@ static bool find_nodes_to_update(PBVH *pbvh, Vector<PBVHNode *> &r_nodes_to_upda else { NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data); node_data->clear_data(); + + if (node_data->triangles && (node->flag & PBVH_Leaf)) { + MEM_delete<Triangles>(node_data->triangles); + node_data->triangles = nullptr; + } } } @@ -276,18 +595,18 @@ static void apply_watertight_check(PBVH *pbvh, Image *image, ImageUser *image_us BKE_image_partial_update_mark_full_update(image); } -static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image_user) +static bool update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image_user) { Vector<PBVHNode *> nodes_to_update; if (!find_nodes_to_update(pbvh, nodes_to_update)) { - return; + return false; } const MLoopUV *ldata_uv = static_cast<const MLoopUV *>( CustomData_get_layer(&mesh->ldata, CD_MLOOPUV)); if (ldata_uv == nullptr) { - return; + return false; } for (PBVHNode *node : nodes_to_update) { @@ -321,6 +640,15 @@ static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image node->flag = static_cast<PBVHNodeFlags>(node->flag & ~PBVH_RebuildPixels); } + /* Add PBVH_TexLeaf flag */ + for (int i : IndexRange(pbvh->totnode)) { + PBVHNode &node = pbvh->nodes[i]; + + if (node.flag & PBVH_Leaf) { + node.flag = (PBVHNodeFlags)((int)node.flag | (int)PBVH_TexLeaf); + } + } + //#define DO_PRINT_STATISTICS #ifdef DO_PRINT_STATISTICS /* Print some statistics about compression ratio. */ @@ -347,6 +675,8 @@ static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image float(compressed_data_len) / num_pixels); } #endif + + return true; } NodeData &BKE_pbvh_pixels_node_data_get(PBVHNode &node) @@ -376,7 +706,6 @@ void BKE_pbvh_pixels_mark_image_dirty(PBVHNode &node, Image &image, ImageUser &i node_data->flags.dirty = false; } } - } // namespace blender::bke::pbvh::pixels extern "C" { @@ -384,12 +713,29 @@ using namespace blender::bke::pbvh::pixels; void BKE_pbvh_build_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image_user) { - update_pixels(pbvh, mesh, image, image_user); + if (update_pixels(pbvh, mesh, image, image_user)) { + double time_ms = PIL_check_seconds_timer(); + + split_pixel_nodes(pbvh, mesh, image, image_user); + + time_ms = PIL_check_seconds_timer() - time_ms; + + printf("Nodes split time: %.2fms\n", time_ms * 1000.0); + } } void pbvh_pixels_free(PBVHNode *node) { NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data); + + if (!node_data) { + return; + } + + if (node_data->triangles && (node->flag & PBVH_Leaf)) { + MEM_delete<Triangles>(node_data->triangles); + } + MEM_delete(node_data); node->pixels.node_data = nullptr; } diff --git a/source/blender/draw/engines/basic/basic_engine.c b/source/blender/draw/engines/basic/basic_engine.c index eee6a5a7cb5..c7da286c064 100644 --- a/source/blender/draw/engines/basic/basic_engine.c +++ b/source/blender/draw/engines/basic/basic_engine.c @@ -10,9 +10,11 @@ #include "DRW_render.h" +#include "BKE_global.h" #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" +#include "BKE_pbvh.h" #include "BLI_alloca.h" @@ -221,6 +223,16 @@ static void basic_cache_populate(void *vedata, Object *ob) DRW_shgroup_call(shgrp, geom, ob); } } + + if (G.debug_value == 889 && ob->sculpt && ob->sculpt->pbvh) { + int debug_node_nr = 0; + DRW_debug_modelmat(ob->obmat); + BKE_pbvh_draw_debug_cb( + ob->sculpt->pbvh, + (void (*)(void *d, const float min[3], const float max[3], PBVHNodeFlags f)) + DRW_sculpt_debug_cb, + &debug_node_nr); + } } } diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index f79e4e1d568..c454a58ffe9 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -15,6 +15,7 @@ #include "BLI_rand.h" #include "BLI_string_utils.h" +#include "BKE_global.h" #include "BKE_paint.h" #include "BKE_particle.h" @@ -882,6 +883,16 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, *cast_shadow = *cast_shadow || (matcache[i].shadow_grp != NULL); } } + + if (G.debug_value == 889 && ob->sculpt && ob->sculpt->pbvh) { + int debug_node_nr = 0; + DRW_debug_modelmat(ob->obmat); + BKE_pbvh_draw_debug_cb( + ob->sculpt->pbvh, + (void (*)(void *d, const float min[3], const float max[3], PBVHNodeFlags f)) + DRW_sculpt_debug_cb, + &debug_node_nr); + } } /* Motion Blur Vectors. */ diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index b9444c58191..7920d9fc71b 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -17,6 +17,7 @@ #include "BKE_context.h" #include "BKE_layer.h" #include "BKE_material.h" +#include "BKE_pbvh.h" #include "BKE_scene.h" #include "BLT_translation.h" @@ -949,6 +950,9 @@ void DRW_mesh_batch_cache_get_attributes(struct Object *object, struct DRW_Attributes **r_attrs, struct DRW_MeshCDMask **r_cd_needed); +void DRW_sculpt_debug_cb( + PBVHNode *node, void *user_data, const float bmin[3], const float bmax[3], PBVHNodeFlags flag); + #ifdef __cplusplus } #endif diff --git a/source/blender/draw/intern/draw_manager_data.cc b/source/blender/draw/intern/draw_manager_data.cc index 9768f1ce9e7..0312eecc7b7 100644 --- a/source/blender/draw/intern/draw_manager_data.cc +++ b/source/blender/draw/intern/draw_manager_data.cc @@ -1250,7 +1250,7 @@ static void sculpt_draw_cb(DRWSculptCallbackData *scd, } } -static void sculpt_debug_cb( +void DRW_sculpt_debug_cb( PBVHNode *node, void *user_data, const float bmin[3], const float bmax[3], PBVHNodeFlags flag) { int *debug_node_nr = (int *)user_data; @@ -1265,7 +1265,8 @@ static void sculpt_debug_cb( DRW_debug_bbox(&bb, (float[4]){0.5f, 0.5f, 0.5f, 0.6f}); } #else /* Color coded leaf bounds. */ - if (flag & PBVH_Leaf) { + if (flag & (PBVH_Leaf | PBVH_TexLeaf)) { + DRW_debug_bbox(&bb, SCULPT_DEBUG_COLOR((*debug_node_nr)++)); int color = (*debug_node_nr)++; color += BKE_pbvh_debug_draw_gen_get(node); @@ -1365,7 +1366,7 @@ static void drw_sculpt_generate_calls(DRWSculptCallbackData *scd) BKE_pbvh_draw_debug_cb( pbvh, (void (*)(PBVHNode * n, void *d, const float min[3], const float max[3], PBVHNodeFlags f)) - sculpt_debug_cb, + DRW_sculpt_debug_cb, &debug_node_nr); } } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index c46fd019725..37a630b8065 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -2665,12 +2665,13 @@ static PBVHNode **sculpt_pbvh_gather_cursor_update(Object *ob, return nodes; } -static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, - Sculpt *sd, - const Brush *brush, - bool use_original, - float radius_scale, - int *r_totnode) +static PBVHNode **sculpt_pbvh_gather_generic_intern(Object *ob, + Sculpt *sd, + const Brush *brush, + bool use_original, + float radius_scale, + int *r_totnode, + PBVHNodeFlags flag) { SculptSession *ss = ob->sculpt; PBVHNode **nodes = NULL; @@ -2686,7 +2687,8 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, .ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK, .center = NULL, }; - BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode); + + BKE_pbvh_search_gather_ex(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode, flag); } else { struct DistRayAABB_Precalc dist_ray_to_aabb_precalc; @@ -2701,11 +2703,33 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc, .ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK, }; - BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_circle_cb, &data, &nodes, r_totnode); + BKE_pbvh_search_gather_ex(ss->pbvh, SCULPT_search_circle_cb, &data, &nodes, r_totnode, flag); } return nodes; } +static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, + Sculpt *sd, + const Brush *brush, + bool use_original, + float radius_scale, + int *r_totnode) +{ + return sculpt_pbvh_gather_generic_intern( + ob, sd, brush, use_original, radius_scale, r_totnode, PBVH_Leaf); +} + +static PBVHNode **sculpt_pbvh_gather_texpaint(Object *ob, + Sculpt *sd, + const Brush *brush, + bool use_original, + float radius_scale, + int *r_totnode) +{ + return sculpt_pbvh_gather_generic_intern( + ob, sd, brush, use_original, radius_scale, r_totnode, PBVH_TexLeaf); +} + /* Calculate primary direction of movement for many brushes. */ static void calc_sculpt_normal( Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]) @@ -3328,8 +3352,8 @@ static void do_brush_action(Sculpt *sd, PaintModeSettings *paint_mode_settings) { SculptSession *ss = ob->sculpt; - int totnode; - PBVHNode **nodes; + int totnode, texnodes_num = 0; + PBVHNode **nodes, **texnodes = NULL; /* Check for unsupported features. */ PBVHType type = BKE_pbvh_type(ss->pbvh); @@ -3342,6 +3366,19 @@ static void do_brush_action(Sculpt *sd, BKE_pbvh_ensure_node_loops(ss->pbvh); } + const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : + ss->cache->original; + + if (sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob)) { + sculpt_pbvh_update_pixels(paint_mode_settings, ss, ob); + + texnodes = sculpt_pbvh_gather_texpaint(ob, sd, brush, use_original, 1.0f, &texnodes_num); + + if (!texnodes_num) { + return; + } + } + /* Build a list of all nodes that are potentially within the brush's area of influence */ if (SCULPT_tool_needs_all_pbvh_nodes(brush)) { @@ -3352,8 +3389,6 @@ static void do_brush_action(Sculpt *sd, nodes = SCULPT_cloth_brush_affected_nodes_gather(ss, brush, &totnode); } else { - const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : - ss->cache->original; float radius_scale = 1.0f; /* Corners of square brushes can go outside the brush radius. */ @@ -3368,6 +3403,7 @@ static void do_brush_action(Sculpt *sd, } nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode); } + const bool use_pixels = sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob); if (use_pixels) { sculpt_pbvh_update_pixels(paint_mode_settings, ss, ob); @@ -3420,6 +3456,7 @@ static void do_brush_action(Sculpt *sd, /* Only act if some verts are inside the brush area. */ if (totnode == 0) { + MEM_SAFE_FREE(texnodes); return; } float location[3]; @@ -3567,7 +3604,7 @@ static void do_brush_action(Sculpt *sd, SCULPT_do_displacement_smear_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_PAINT: - SCULPT_do_paint_brush(paint_mode_settings, sd, ob, nodes, totnode); + SCULPT_do_paint_brush(paint_mode_settings, sd, ob, nodes, totnode, texnodes, texnodes_num); break; case SCULPT_TOOL_SMEAR: SCULPT_do_smear_brush(sd, ob, nodes, totnode); @@ -3611,6 +3648,7 @@ static void do_brush_action(Sculpt *sd, } MEM_SAFE_FREE(nodes); + MEM_SAFE_FREE(texnodes); /* Update average stroke position. */ copy_v3_v3(location, ss->cache->true_location); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index bf47b64d176..59b0f7f5925 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -1743,7 +1743,9 @@ void SCULPT_do_paint_brush(struct PaintModeSettings *paint_mode_settings, Sculpt *sd, Object *ob, PBVHNode **nodes, - int totnode) ATTR_NONNULL(); + int totnode, + PBVHNode **texnodes, + int texnodes_num) ATTR_NONNULL(); /** * \brief Get the image canvas for painting on the given object. @@ -1760,7 +1762,9 @@ void SCULPT_do_paint_brush_image(struct PaintModeSettings *paint_mode_settings, Sculpt *sd, Object *ob, PBVHNode **nodes, - int totnode) ATTR_NONNULL(); + int totnode, + PBVHNode **texnodes, + int texnode_num) ATTR_NONNULL(); bool SCULPT_use_image_paint_brush(struct PaintModeSettings *settings, Object *ob) ATTR_NONNULL(); /* Smear Brush. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index ee716d1107a..2826a7c4df3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -249,11 +249,17 @@ static void sample_wet_paint_reduce(const void *__restrict UNUSED(userdata), add_v4_v4(join->color, swptd->color); } -void SCULPT_do_paint_brush( - PaintModeSettings *paint_mode_settings, Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings, + Sculpt *sd, + Object *ob, + PBVHNode **nodes, + int totnode, + PBVHNode **texnodes, + int texnodes_num) { if (SCULPT_use_image_paint_brush(paint_mode_settings, ob)) { - SCULPT_do_paint_brush_image(paint_mode_settings, sd, ob, nodes, totnode); + SCULPT_do_paint_brush_image( + paint_mode_settings, sd, ob, nodes, totnode, texnodes, texnodes_num); return; } diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc index d3b3100458d..cb4bb1cde5c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc @@ -1,6 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2022 Blender Foundation. All rights reserved. */ +/* Paint a color made from hash of node pointer. */ +//#define DEBUG_PIXEL_NODES + #include "DNA_image_types.h" #include "DNA_object_types.h" @@ -9,6 +12,9 @@ #include "BLI_math.h" #include "BLI_math_color_blend.h" #include "BLI_task.h" +#ifdef DEBUG_PIXEL_NODES +# include "BLI_rand.h" +#endif #include "IMB_colormanagement.h" #include "IMB_imbuf.h" @@ -181,6 +187,15 @@ template<typename ImageBuffer> class PaintingKernel { automask_data); float4 paint_color = brush_color * falloff_strength * brush_strength; float4 buffer_color; + +#ifdef DEBUG_PIXEL_NODES + if ((pixel_row.start_image_coordinate.y >> 3) & 1) { + paint_color[0] *= 0.5f; + paint_color[1] *= 0.5f; + paint_color[2] *= 0.5f; + } +#endif + blend_color_mix_float(buffer_color, color, paint_color); buffer_color *= brush->alpha; IMB_blend_color_float(color, color, buffer_color, static_cast<IMB_BlendMode>(brush->blend)); @@ -193,20 +208,18 @@ template<typename ImageBuffer> class PaintingKernel { return pixels_painted; } - void init_brush_color(ImBuf *image_buffer) + void init_brush_color(ImBuf *image_buffer, float in_brush_color[3]) { const char *to_colorspace = image_accessor.get_colorspace_name(image_buffer); if (last_used_color_space == to_colorspace) { return; } - copy_v3_v3(brush_color, - ss->cache->invert ? BKE_brush_secondary_color_get(ss->scene, brush) : - BKE_brush_color_get(ss->scene, brush)); + /* NOTE: Brush colors are stored in sRGB. We use math color to follow other areas that * use brush colors. From there on we use IMB_colormanagement to convert the brush color to the * colorspace of the texture. This isn't ideal, but would need more refactoring to make sure * that brush colors are stored in scene linear by default. */ - srgb_to_linearrgb_v3_v3(brush_color, brush_color); + srgb_to_linearrgb_v3_v3(brush_color, in_brush_color); brush_color[3] = 1.0f; const char *from_colorspace = IMB_colormanagement_role_colorspace_name_get( @@ -314,11 +327,27 @@ static void do_paint_pixels(void *__restrict userdata, const int thread_id = BLI_task_parallel_thread_id(tls); MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); - std::vector<bool> brush_test = init_triangle_brush_test(ss, node_data.triangles, mvert); + std::vector<bool> brush_test = init_triangle_brush_test(ss, *node_data.triangles, mvert); PaintingKernel<ImageBufferFloat4> kernel_float4(ss, brush, thread_id, mvert); PaintingKernel<ImageBufferByte4> kernel_byte4(ss, brush, thread_id, mvert); + float brush_color[4]; + +#ifdef DEBUG_PIXEL_NODES + RNG *rng = BLI_rng_new(POINTER_AS_UINT(node)); + + brush_color[0] = BLI_rng_get_float(rng); + brush_color[1] = BLI_rng_get_float(rng); + brush_color[2] = BLI_rng_get_float(rng); +#else + copy_v3_v3(brush_color, + ss->cache->invert ? BKE_brush_secondary_color_get(ss->scene, brush) : + BKE_brush_color_get(ss->scene, brush)); +#endif + + brush_color[3] = 1.0f; + AutomaskingNodeData automask_data; SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); @@ -336,10 +365,10 @@ static void do_paint_pixels(void *__restrict userdata, } if (image_buffer->rect_float != nullptr) { - kernel_float4.init_brush_color(image_buffer); + kernel_float4.init_brush_color(image_buffer, brush_color); } else { - kernel_byte4.init_brush_color(image_buffer); + kernel_byte4.init_brush_color(image_buffer, brush_color); } for (const PackedPixelRow &pixel_row : tile_data.pixel_rows) { @@ -349,11 +378,11 @@ static void do_paint_pixels(void *__restrict userdata, bool pixels_painted = false; if (image_buffer->rect_float != nullptr) { pixels_painted = kernel_float4.paint( - node_data.triangles, pixel_row, image_buffer, &automask_data); + *node_data.triangles, pixel_row, image_buffer, &automask_data); } else { pixels_painted = kernel_byte4.paint( - node_data.triangles, pixel_row, image_buffer, &automask_data); + *node_data.triangles, pixel_row, image_buffer, &automask_data); } if (pixels_painted) { @@ -497,27 +526,33 @@ bool SCULPT_use_image_paint_brush(PaintModeSettings *settings, Object *ob) return BKE_paint_canvas_image_get(settings, ob, &image, &image_user); } -void SCULPT_do_paint_brush_image( - PaintModeSettings *paint_mode_settings, Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +void SCULPT_do_paint_brush_image(PaintModeSettings *paint_mode_settings, + Sculpt *sd, + Object *ob, + PBVHNode **nodes, + int totnode, + PBVHNode **texnodes, + int texnodes_num) { Brush *brush = BKE_paint_brush(&sd->paint); TexturePaintingUserData data = {nullptr}; data.ob = ob; data.brush = brush; - data.nodes = nodes; + data.nodes = texnodes; if (!ImageData::init_active_image(ob, &data.image_data, paint_mode_settings)) { return; } TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_push_undo_tile, &settings); - BLI_task_parallel_range(0, totnode, &data, do_paint_pixels, &settings); + BKE_pbvh_parallel_range_settings(&settings, true, texnodes_num); + BLI_task_parallel_range(0, texnodes_num, &data, do_push_undo_tile, &settings); + BLI_task_parallel_range(0, texnodes_num, &data, do_paint_pixels, &settings); TaskParallelSettings settings_flush; - BKE_pbvh_parallel_range_settings(&settings_flush, false, totnode); - BLI_task_parallel_range(0, totnode, &data, do_mark_dirty_regions, &settings_flush); + + BKE_pbvh_parallel_range_settings(&settings_flush, false, texnodes_num); + BLI_task_parallel_range(0, texnodes_num, &data, do_mark_dirty_regions, &settings_flush); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 13e3dd64521..518e7477b4d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -2068,7 +2068,7 @@ static UndoSculpt *sculpt_undo_get_nodes(void) { UndoStack *ustack = ED_undo_stack_get(); UndoStep *us = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_SCULPT); - return sculpt_undosys_step_get_nodes(us); + return us ? sculpt_undosys_step_get_nodes(us) : NULL; } /** \} */ |