diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint/sculpt_texture_paint_b.cc')
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_texture_paint_b.cc | 315 |
1 files changed, 241 insertions, 74 deletions
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 d3e93574c1a..9ad8be1d8e1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_texture_paint_b.cc +++ b/source/blender/editors/sculpt_paint/sculpt_texture_paint_b.cc @@ -37,6 +37,63 @@ namespace blender::ed::sculpt_paint::texture_paint { +struct PixelData { + struct { + bool dirty : 1; + } flags; + int2 pixel_pos; + float3 local_pos; + float4 content; +}; + +struct NodeData { + struct { + bool dirty : 1; + } flags; + + Vector<PixelData> pixels; + rcti dirty_region; + + NodeData() + { + flags.dirty = false; + BLI_rcti_init_minmax(&dirty_region); + } + + void init_pixels(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) + { + printf("%s", __func__); + print_rcti_id(&dirty_region); + BKE_image_partial_update_mark_region( + &image, static_cast<ImageTile *>(image.tiles.first), &image_buffer, &dirty_region); + BLI_rcti_init_minmax(&dirty_region); + } + + static void free_func(void *instance) + { + NodeData *node_data = static_cast<NodeData *>(instance); + MEM_delete(node_data); + } +}; + +namespace shaders { + using namespace imbuf::rasterizer; struct VertexInput { @@ -58,66 +115,110 @@ class VertexShader : public AbstractVertexShader<VertexInput, float3> { } }; -class FragmentShader : public AbstractFragmentShader<float3, float4> { +struct FragmentOutput { + float3 local_pos; +}; + +class FragmentShader : public AbstractFragmentShader<float3, FragmentOutput> { public: - float4 color; - const Brush *brush = nullptr; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn; + ImBuf *image_buffer; + public: void fragment(const FragmentInputType &input, FragmentOutputType *r_output) override { - copy_v4_v4(*r_output, color); - float strength = sculpt_brush_test_sq_fn(&test, input) ? - BKE_brush_curve_strength(brush, sqrtf(test.dist), test.radius) : - 0.0f; + r_output->local_pos = input; + } +}; + +struct NodeDataPair { + ImBuf *image_buffer; + NodeData *node_data; - (*r_output)[3] *= strength; + struct { + /* Rasterizer doesn't support glCoord yet, so for now we just store them in a runtime section. + */ + int2 last_known_pixel_pos; + } runtime; +}; + +class AddPixel : public AbstractBlendMode<FragmentOutput, NodeDataPair> { + public: + void blend(NodeDataPair *dest, const FragmentOutput &source) const override + { + PixelData new_pixel; + new_pixel.local_pos = source.local_pos; + new_pixel.pixel_pos = dest->runtime.last_known_pixel_pos; + const int pixel_offset = new_pixel.pixel_pos[1] * dest->image_buffer->x + + new_pixel.pixel_pos[0]; + new_pixel.content = float4(dest->image_buffer->rect_float[pixel_offset * 4]); + new_pixel.flags.dirty = false; + + dest->node_data->pixels.append(new_pixel); + dest->runtime.last_known_pixel_pos[0] += 1; } }; -using RasterizerType = Rasterizer<VertexShader, FragmentShader, AlphaBlendMode>; +class NodeDataDrawingTarget : public AbstractDrawingTarget<NodeDataPair, NodeDataPair> { + private: + NodeDataPair *active_ = nullptr; -struct TexturePaintingUserData { - Object *ob; - Brush *brush; - PBVHNode **nodes; - Vector<rctf> region_to_update; + public: + uint64_t get_width() const + { + return active_->image_buffer->x; + } + uint64_t get_height() const + { + return active_->image_buffer->y; + }; + NodeDataPair *get_pixel_ptr(uint64_t x, uint64_t y) + { + active_->runtime.last_known_pixel_pos = int2(x, y); + return active_; + }; + int64_t get_pixel_stride() const + { + return 0; + }; + bool has_active_target() const + { + return active_ != nullptr; + } + void activate(NodeDataPair *instance) + { + active_ = instance; + }; + void deactivate() + { + active_ = nullptr; + } }; -static void do_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - TexturePaintingUserData *data = static_cast<TexturePaintingUserData *>(userdata); - Object *ob = data->ob; - SculptSession *ss = ob->sculpt; - const Brush *brush = data->brush; - ImBuf *drawing_target = ss->mode.texture_paint.drawing_target; - RasterizerType rasterizer; +using RasterizerType = Rasterizer<VertexShader, FragmentShader, AddPixel, NodeDataDrawingTarget>; + +} // namespace shaders +void NodeData::init_pixels(Object *ob, PBVHNode *node, ImBuf *image_buffer) +{ Mesh *mesh = static_cast<Mesh *>(ob->data); MLoopUV *ldata_uv = static_cast<MLoopUV *>(CustomData_get_layer(&mesh->ldata, CD_MLOOPUV)); if (ldata_uv == nullptr) { return; } - rasterizer.activate_drawing_target(drawing_target); - rasterizer.vertex_shader().image_size = float2(drawing_target->x, drawing_target->y); - srgb_to_linearrgb_v3_v3(rasterizer.fragment_shader().color, brush->rgb); - FragmentShader &fragment_shader = rasterizer.fragment_shader(); - fragment_shader.color[3] = 1.0f; - fragment_shader.brush = brush; - fragment_shader.sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &fragment_shader.test, brush->falloff_shape); - - PBVHVertexIter vd; + shaders::RasterizerType rasterizer; + shaders::NodeDataPair node_data_pair; + rasterizer.vertex_shader().image_size = float2(image_buffer->x, image_buffer->y); + rasterizer.fragment_shader().image_buffer = image_buffer; + node_data_pair.node_data = this; + node_data_pair.image_buffer = image_buffer; + rasterizer.activate_drawing_target(&node_data_pair); + SculptSession *ss = ob->sculpt; MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); - rctf ®ion_to_update = data->region_to_update[n]; - BLI_rctf_init_minmax(®ion_to_update); - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + 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]]; @@ -125,12 +226,7 @@ static void do_task_cb_ex(void *__restrict userdata, continue; } - float poly_center[3]; const MLoop *loopstart = &ss->mloop[p->loopstart]; - BKE_mesh_calc_poly_center(p, &ss->mloop[p->loopstart], mvert, poly_center); - if (!fragment_shader.sculpt_brush_test_sq_fn(&fragment_shader.test, poly_center)) { - continue; - } for (int triangle = 0; triangle < p->totloop - 2; triangle++) { const int v1_index = loopstart[0].v; @@ -140,47 +236,104 @@ static void do_task_cb_ex(void *__restrict userdata, const int v2_loop_index = p->loopstart + triangle + 1; const int v3_loop_index = p->loopstart + triangle + 2; - VertexInput v1(mvert[v1_index].co, ldata_uv[v1_loop_index].uv); - VertexInput v2(mvert[v2_index].co, ldata_uv[v2_loop_index].uv); - VertexInput v3(mvert[v3_index].co, ldata_uv[v3_loop_index].uv); + shaders::VertexInput v1(mvert[v1_index].co, ldata_uv[v1_loop_index].uv); + shaders::VertexInput v2(mvert[v2_index].co, ldata_uv[v2_loop_index].uv); + shaders::VertexInput v3(mvert[v3_index].co, ldata_uv[v3_loop_index].uv); rasterizer.draw_triangle(v1, v2, v3); - - BLI_rctf_do_minmax_v(®ion_to_update, v1.uv); - BLI_rctf_do_minmax_v(®ion_to_update, v2.uv); - BLI_rctf_do_minmax_v(®ion_to_update, v3.uv); } } } BKE_pbvh_vertex_iter_end; - rasterizer.deactivate_drawing_target(); } +struct TexturePaintingUserData { + Object *ob; + Brush *brush; + PBVHNode **nodes; +}; + +static void do_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + TexturePaintingUserData *data = static_cast<TexturePaintingUserData *>(userdata); + Object *ob = data->ob; + SculptSession *ss = ob->sculpt; + const Brush *brush = data->brush; + PBVHNode *node = data->nodes[n]; + NodeData *node_data = static_cast<NodeData *>(BKE_pbvh_node_texture_paint_data_get(node)); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, brush->falloff_shape); + + for (PixelData &pixel : node_data->pixels) { + if (!sculpt_brush_test_sq_fn(&test, pixel.local_pos)) { + continue; + } + 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.flags.dirty = true; + BLI_rcti_do_minmax_v(&node_data->dirty_region, pixel.pixel_pos); + } + node_data->flags.dirty = true; +} + +struct ImageData { + void *lock = nullptr; + Image *image = nullptr; + ImageUser *image_user = nullptr; + ImBuf *image_buffer = nullptr; + + ~ImageData() + { + BKE_image_release_ibuf(image, image_buffer, lock); + } + + static bool init_active_image(Object *ob, ImageData *r_image_data) + { + ED_object_get_active_image( + ob, 1, &r_image_data->image, &r_image_data->image_user, nullptr, nullptr); + if (r_image_data->image == nullptr) { + return false; + } + r_image_data->image_buffer = BKE_image_acquire_ibuf( + r_image_data->image, r_image_data->image_user, &r_image_data->lock); + if (r_image_data->image_buffer == nullptr) { + return false; + } + return true; + } +}; + extern "C" { void SCULPT_do_texture_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - void *lock; - Image *image; - ImageUser *image_user; - - ED_object_get_active_image(ob, 1, &image, &image_user, nullptr, nullptr); - if (image == nullptr) { + ImageData image_data; + if (!ImageData::init_active_image(ob, &image_data)) { return; } - ImBuf *image_buffer = BKE_image_acquire_ibuf(image, image_user, &lock); - if (image_buffer == nullptr) { - return; + + for (int n = 0; n < totnode; n++) { + PBVHNode *node = nodes[n]; + NodeData *data = static_cast<NodeData *>(BKE_pbvh_node_texture_paint_data_get(node)); + if (data == nullptr) { + NodeData *node_data = MEM_new<NodeData>(__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_buffer; + + ss->mode.texture_paint.drawing_target = image_data.image_buffer; TexturePaintingUserData data = {nullptr}; data.ob = ob; data.brush = brush; data.nodes = nodes; - data.region_to_update.resize(totnode); TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -189,20 +342,34 @@ void SCULPT_do_texture_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int BLI_task_parallel_range(0, totnode, &data, do_task_cb_ex, &settings); TIMEIT_END(texture_painting); - for (int i = 0; i < totnode; i++) { - rcti region_to_update; - region_to_update.xmin = data.region_to_update[i].xmin * image_buffer->x; - region_to_update.xmax = data.region_to_update[i].xmax * image_buffer->x; - region_to_update.ymin = data.region_to_update[i].ymin * image_buffer->y; - region_to_update.ymax = data.region_to_update[i].ymax * image_buffer->y; + ss->mode.texture_paint.drawing_target = nullptr; +} - /* TODO: Tiled images. */ - BKE_image_partial_update_mark_region( - image, static_cast<ImageTile *>(image->tiles.first), image_buffer, ®ion_to_update); +void SCULPT_flush_texture_paint(Object *ob) +{ + ImageData image_data; + if (!ImageData::init_active_image(ob, &image_data)) { + return; } - BKE_image_release_ibuf(image, image_buffer, lock); - ss->mode.texture_paint.drawing_target = nullptr; + SculptSession *ss = ob->sculpt; + PBVHNode **nodes; + int totnode; + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + for (int n = 0; n < totnode; n++) { + PBVHNode *node = nodes[n]; + NodeData *data = static_cast<NodeData *>(BKE_pbvh_node_texture_paint_data_get(node)); + if (data == nullptr) { + continue; + } + + if (data->flags.dirty) { + data->flush(*image_data.image_buffer); + data->mark_region(*image_data.image, *image_data.image_buffer); + } + } + + MEM_freeN(nodes); } } } // namespace blender::ed::sculpt_paint::texture_paint |