From 0cd7177420959f6caf6eab08145e32fe085c685f Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 8 Mar 2022 15:17:33 +0100 Subject: Use different rasterization. --- source/blender/editors/sculpt_paint/sculpt.c | 9 + .../blender/editors/sculpt_paint/sculpt_intern.h | 1 + .../editors/sculpt_paint/sculpt_texture_paint_b.cc | 311 +++++++++++++++++++-- 3 files changed, 303 insertions(+), 18 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index f6b334f2175..f19c3c66c9a 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -3171,6 +3171,10 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe return; } + if (brush->sculpt_tool == SCULPT_TOOL_TEXTURE_PAINT && type != PBVH_FACES) { + 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)) { @@ -3216,6 +3220,11 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe } } + if (brush->sculpt_tool == SCULPT_TOOL_TEXTURE_PAINT) { + /* TODO should perhaps move to higher level.... doing this per step is not needed. */ + SCULPT_init_texture_paint(ob); + } + /* For anchored brushes with spherical falloff, we start off with zero radius, thus we have no * PBVH nodes on the first brush step. */ if (totnode || diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 39aaf4d1276..93bb8fb4fe3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -1614,6 +1614,7 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in /* Paint Brush. */ void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); void SCULPT_do_texture_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); +void SCULPT_init_texture_paint(Object *ob); void SCULPT_flush_texture_paint(Object *ob); /* Smear Brush. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_texture_paint_b.cc b/source/blender/editors/sculpt_paint/sculpt_texture_paint_b.cc index 05708cc47bb..de450b061f4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_texture_paint_b.cc +++ b/source/blender/editors/sculpt_paint/sculpt_texture_paint_b.cc @@ -60,20 +60,18 @@ struct NodeData { BLI_rcti_init_minmax(&dirty_region); } - void init_pixels(Object *ob, PBVHNode *node, ImBuf *image_buffer); + void init_pixels_rasterization(Object *ob, PBVHNode *node, ImBuf *image_buffer); + void flush(ImBuf &image_buffer) { flags.dirty = false; - int pixels_flushed = 0; for (PixelData &pixel : pixels) { if (pixel.flags.dirty) { const int pixel_offset = (pixel.pixel_pos[1] * image_buffer.x + pixel.pixel_pos[0]) * 4; copy_v4_v4(&image_buffer.rect_float[pixel_offset], pixel.content); pixel.flags.dirty = false; - pixels_flushed += 1; } } - printf("%s: %d pixels flushed\n", __func__, pixels_flushed); } void mark_region(Image &image, ImBuf &image_buffer) @@ -198,9 +196,8 @@ using RasterizerType = Rasterizer(ob->data); MLoopUV *ldata_uv = static_cast(CustomData_get_layer(&mesh->ldata, CD_MLOOPUV)); if (ldata_uv == nullptr) { @@ -228,7 +225,6 @@ void NodeData::init_pixels(Object *ob, PBVHNode *node, ImBuf *image_buffer) } const MLoop *loopstart = &ss->mloop[p->loopstart]; - for (int triangle = 0; triangle < p->totloop - 2; triangle++) { const int v1_index = loopstart[0].v; const int v2_index = loopstart[triangle + 1].v; @@ -264,6 +260,7 @@ static void do_task_cb_ex(void *__restrict userdata, const Brush *brush = data->brush; PBVHNode *node = data->nodes[n]; NodeData *node_data = static_cast(BKE_pbvh_node_texture_paint_data_get(node)); + BLI_assert(node_data != nullptr); SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( @@ -275,10 +272,274 @@ static void do_task_cb_ex(void *__restrict userdata, } const float falloff_strength = BKE_brush_curve_strength(brush, sqrtf(test.dist), test.radius); interp_v3_v3v3(pixel.content, pixel.content, brush->rgb, falloff_strength); + pixel.content[3] = 1.0f; pixel.flags.dirty = true; BLI_rcti_do_minmax_v(&node_data->dirty_region, pixel.pixel_pos); + node_data->flags.dirty = true; } - node_data->flags.dirty = true; +} + +static void init_rasterization_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + TexturePaintingUserData *data = static_cast(userdata); + Object *ob = data->ob; + SculptSession *ss = ob->sculpt; + PBVHNode *node = data->nodes[n]; + + NodeData *node_data = static_cast(BKE_pbvh_node_texture_paint_data_get(node)); + // TODO: reinit when texturing on different image? + if (node_data != nullptr) { + return; + } + + TIMEIT_START(init_texture_paint_for_node); + node_data = MEM_new(__func__); + node_data->init_pixels_rasterization(ob, node, ss->mode.texture_paint.drawing_target); + BKE_pbvh_node_texture_paint_data_set(node, node_data, NodeData::free_func); + TIMEIT_END(init_texture_paint_for_node); +} + +static void init_using_rasterization(Object *ob, int totnode, PBVHNode **nodes) +{ + TIMEIT_START(init_using_rasterization); + TexturePaintingUserData data = {nullptr}; + data.ob = ob; + data.nodes = nodes; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + + BLI_task_parallel_range(0, totnode, &data, init_rasterization_task_cb_ex, &settings); + TIMEIT_END(init_using_rasterization); +} + +struct BucketEntry { + PBVHNode *node; + const MPoly *poly; +}; +struct Bucket { + static const int Size = 16; + Vector entries; + rctf bounds; +}; + +static bool init_using_intersection(SculptSession *ss, + Bucket &bucket, + ImBuf *image_buffer, + MVert *mvert, + MLoopUV *ldata_uv, + float2 uv, + int2 xy) +{ + const int pixel_offset = xy[1] * image_buffer->x + xy[0]; + for (BucketEntry &entry : bucket.entries) { + const MPoly *p = entry.poly; + + const MLoop *loopstart = &ss->mloop[p->loopstart]; + for (int triangle = 0; triangle < p->totloop - 2; triangle++) { + const int v1_loop_index = p->loopstart; + const int v2_loop_index = p->loopstart + triangle + 1; + const int v3_loop_index = p->loopstart + triangle + 2; + const float2 v1_uv = ldata_uv[v1_loop_index].uv; + const float2 v2_uv = ldata_uv[v2_loop_index].uv; + const float2 v3_uv = ldata_uv[v3_loop_index].uv; + float3 weights; + barycentric_weights_v2(v1_uv, v2_uv, v3_uv, uv, weights); + if (weights[0] < 0.0 || weights[0] > 1.0 || weights[1] < 0.0 || weights[1] > 1.0 || + weights[2] < 0.0 || weights[2] > 1.0) { + continue; + } + + const int v1_index = loopstart[0].v; + const int v2_index = loopstart[triangle + 1].v; + const int v3_index = loopstart[triangle + 2].v; + const float3 v1_pos = mvert[v1_index].co; + const float3 v2_pos = mvert[v2_index].co; + const float3 v3_pos = mvert[v3_index].co; + float3 local_pos; + interp_v3_v3v3v3(local_pos, v1_pos, v2_pos, v3_pos, weights); + + PixelData new_pixel; + new_pixel.local_pos = local_pos; + new_pixel.pixel_pos = xy; + copy_v4_fl(&image_buffer->rect_float[pixel_offset * 4], 1.0); + new_pixel.content = float4(image_buffer->rect_float[pixel_offset * 4]); + new_pixel.flags.dirty = false; + + PBVHNode *node = entry.node; + NodeData *node_data = static_cast(BKE_pbvh_node_texture_paint_data_get(node)); + node_data->pixels.append(new_pixel); + return true; + } + } + return false; +} + +static bool init_using_intersection(SculptSession *ss, + PBVHNode *node, + NodeData *node_data, + ImBuf *image_buffer, + MVert *mvert, + MLoopUV *ldata_uv, + float2 uv, + int2 xy) +{ + const int pixel_offset = xy[1] * image_buffer->x + xy[0]; + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + MeshElemMap *vert_map = &ss->pmap[vd.index]; + for (int j = 0; j < ss->pmap[vd.index].count; j++) { + const MPoly *p = &ss->mpoly[vert_map->indices[j]]; + if (p->totloop < 3) { + continue; + } + + const MLoop *loopstart = &ss->mloop[p->loopstart]; + for (int triangle = 0; triangle < p->totloop - 2; triangle++) { + const int v1_loop_index = p->loopstart; + const int v2_loop_index = p->loopstart + triangle + 1; + const int v3_loop_index = p->loopstart + triangle + 2; + const float2 v1_uv = ldata_uv[v1_loop_index].uv; + const float2 v2_uv = ldata_uv[v2_loop_index].uv; + const float2 v3_uv = ldata_uv[v3_loop_index].uv; + float3 weights; + barycentric_weights_v2(v1_uv, v2_uv, v3_uv, uv, weights); + if (weights[0] < 0.0 || weights[0] > 1.0 || weights[1] < 0.0 || weights[1] > 1.0 || + weights[2] < 0.0 || weights[2] > 1.0) { + continue; + } + + const int v1_index = loopstart[0].v; + const int v2_index = loopstart[triangle + 1].v; + const int v3_index = loopstart[triangle + 2].v; + const float3 v1_pos = mvert[v1_index].co; + const float3 v2_pos = mvert[v2_index].co; + const float3 v3_pos = mvert[v3_index].co; + float3 local_pos; + interp_v2_v2v2v2(local_pos, v1_pos, v2_pos, v3_pos, weights); + + PixelData new_pixel; + new_pixel.local_pos = local_pos; + new_pixel.pixel_pos = xy; + new_pixel.content = float4(image_buffer->rect_float[pixel_offset * 4]); + copy_v4_fl(&image_buffer->rect_float[pixel_offset * 4], 1.0); + new_pixel.flags.dirty = false; + node_data->pixels.append(new_pixel); + return true; + } + } + } + BKE_pbvh_vertex_iter_end; + + return false; +} + +static void init_using_intersection(Object *ob, int totnode, PBVHNode **nodes) +{ + TIMEIT_START(init_using_intersection); + + Vector nodes_to_initialize; + for (int n = 0; n < totnode; n++) { + PBVHNode *node = nodes[n]; + NodeData *node_data = static_cast(BKE_pbvh_node_texture_paint_data_get(node)); + if (node_data != nullptr) { + continue; + } + node_data = MEM_new(__func__); + BKE_pbvh_node_texture_paint_data_set(node, node_data, NodeData::free_func); + nodes_to_initialize.append(node); + } + if (nodes_to_initialize.size() == 0) { + return; + } + + Mesh *mesh = static_cast(ob->data); + MLoopUV *ldata_uv = static_cast(CustomData_get_layer(&mesh->ldata, CD_MLOOPUV)); + if (ldata_uv == nullptr) { + return; + } + + SculptSession *ss = ob->sculpt; + MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); + ImBuf *image_buffer = ss->mode.texture_paint.drawing_target; + int pixels_added = 0; + Bucket bucket; + for (int y_bucket = 0; y_bucket < image_buffer->y; y_bucket += Bucket::Size) { + for (int x_bucket = 0; x_bucket < image_buffer->x; x_bucket += Bucket::Size) { + bucket.entries.clear(); + BLI_rctf_init(&bucket.bounds, + float(x_bucket) / image_buffer->x, + float(x_bucket + Bucket::Size) / image_buffer->x, + float(y_bucket) / image_buffer->y, + float(y_bucket + Bucket::Size) / image_buffer->y); + print_rctf_id(&bucket.bounds); + printf("%d pixels already added.\n", pixels_added); + + for (int n = 0; n < nodes_to_initialize.size(); n++) { + PBVHNode *node = nodes_to_initialize[n]; + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + MeshElemMap *vert_map = &ss->pmap[vd.index]; + for (int j = 0; j < ss->pmap[vd.index].count; j++) { + const MPoly *p = &ss->mpoly[vert_map->indices[j]]; + if (p->totloop < 3) { + continue; + } + + rctf poly_bound; + BLI_rctf_init_minmax(&poly_bound); + for (int triangle = 0; triangle < p->totloop - 2; triangle++) { + const int v1_loop_index = p->loopstart; + const int v2_loop_index = p->loopstart + triangle + 1; + const int v3_loop_index = p->loopstart + triangle + 2; + const float2 v1_uv = ldata_uv[v1_loop_index].uv; + const float2 v2_uv = ldata_uv[v2_loop_index].uv; + const float2 v3_uv = ldata_uv[v3_loop_index].uv; + BLI_rctf_do_minmax_v(&poly_bound, v1_uv); + BLI_rctf_do_minmax_v(&poly_bound, v2_uv); + BLI_rctf_do_minmax_v(&poly_bound, v3_uv); + } + if (BLI_rctf_isect(&bucket.bounds, &poly_bound, nullptr)) { + BucketEntry entry; + entry.node = node; + entry.poly = p; + bucket.entries.append(entry); + } + } + } + BKE_pbvh_vertex_iter_end; + } + printf("Loaded %ld entries in bucket\n", bucket.entries.size()); + if (bucket.entries.size() == 0) { + continue; + } + + for (int y = y_bucket; y < image_buffer->y && y < y_bucket + Bucket::Size; y++) { + for (int x = x_bucket; x < image_buffer->x && x < x_bucket + Bucket::Size; x++) { + float2 uv(float(x) / image_buffer->x, float(y) / image_buffer->y); +#if 0 + for (int n = 0; n < nodes_to_initialize.size(); n++) { + PBVHNode *node = nodes_to_initialize[n]; + NodeData *node_data = static_cast( + BKE_pbvh_node_texture_paint_data_get(node)); + if (init_using_intersection( + ss, node, node_data, image_buffer, mvert, ldata_uv, uv, int2(x, y))) { + pixels_added++; + break; + } + } +#else + if (init_using_intersection(ss, bucket, image_buffer, mvert, ldata_uv, uv, int2(x, y))) { + pixels_added++; + } +#endif + } + } + } + } + TIMEIT_END(init_using_intersection); } struct ImageData { @@ -319,16 +580,6 @@ void SCULPT_do_texture_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int return; } - for (int n = 0; n < totnode; n++) { - PBVHNode *node = nodes[n]; - NodeData *data = static_cast(BKE_pbvh_node_texture_paint_data_get(node)); - if (data == nullptr) { - NodeData *node_data = MEM_new(__func__); - node_data->init_pixels(ob, node, image_data.image_buffer); - BKE_pbvh_node_texture_paint_data_set(node, node_data, NodeData::free_func); - } - } - ss->mode.texture_paint.drawing_target = image_data.image_buffer; TexturePaintingUserData data = {nullptr}; @@ -346,6 +597,30 @@ void SCULPT_do_texture_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int ss->mode.texture_paint.drawing_target = nullptr; } +void SCULPT_init_texture_paint(Object *ob) +{ + SculptSession *ss = ob->sculpt; + ImageData image_data; + if (!ImageData::init_active_image(ob, &image_data)) { + return; + } + ss->mode.texture_paint.drawing_target = image_data.image_buffer; + PBVHNode **nodes; + int totnode; + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + const bool do_rasterization = false; + if (do_rasterization) { + init_using_rasterization(ob, totnode, nodes); + } + else { + init_using_intersection(ob, totnode, nodes); + } + + MEM_freeN(nodes); + + ss->mode.texture_paint.drawing_target = nullptr; +} + void SCULPT_flush_texture_paint(Object *ob) { ImageData image_data; -- cgit v1.2.3