Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
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.cc315
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 &region_to_update = data->region_to_update[n];
- BLI_rctf_init_minmax(&region_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(&region_to_update, v1.uv);
- BLI_rctf_do_minmax_v(&region_to_update, v2.uv);
- BLI_rctf_do_minmax_v(&region_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, &region_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