diff options
author | Jeroen Bakker <jbakker> | 2022-04-15 17:39:50 +0300 |
---|---|---|
committer | Jeroen Bakker <jeroen@blender.org> | 2022-04-15 17:40:01 +0300 |
commit | e96a809a68ef40225d5eddfd790644538e171a8d (patch) | |
tree | 1c679db4cda3d0ea7d6189e63dbfce020f57abf6 /source/blender/blenkernel/BKE_pbvh_pixels.hh | |
parent | 25196f8a369c354bf870b6144e3f43aa0a1d38e0 (diff) |
PBVH Pixel extractor.
This patch contains an initial pixel extractor for PBVH and an initial paint brush implementation.
PBVH is an accelleration structure blender uses internally to speed up 3d painting operations.
At this moment it is extensively used by sculpt, vertex painting and weight painting.
For the 3d texturing brush we will be using the PBVH for texture painting.
Currently PBVH is organized to work on geometry (vertices, polygons and triangles).
For texture painting this should be extended it to use pixels.
{F12995467}
Screen recording has been done on a Mac Mini with a 6 core 3.3 GHZ Intel processor.
# Scope
This patch only contains an extending uv seams to fix uv seams. This is not actually we want, but was easy to add
to make the brush usable.
Pixels are places in the PBVH_Leaf nodes. We want to introduce a special node for pixels, but that will be done
in a separate patch to keep the code review small. This reduces the painting performance when using
low and medium poly assets.
In workbench textures aren't forced to be shown. For now use Material/Rendered view.
# Rasterization process
The rasterization process will generate the pixel information for a leaf node. In the future those
leaf nodes will be split up into multiple leaf nodes to increase the performance when there
isn't enough geometry. For this patch this was left out of scope.
In order to do so every polygon should be uniquely assigned to a leaf node.
For each leaf node
for each polygon
If polygon not assigned
assign polygon to node.
Polygons are to complicated to be used directly we have to split the polygons into triangles.
For each leaf node
for each polygon
extract triangles from polygon.
The list of triangles can be stored inside the leaf node. The list of polygons aren't needed anymore.
Each triangle has:
poly_index.
vert_indices
delta barycentric coordinate between x steps.
Each triangle is rasterized in rows. Sequential pixels (in uv space) are stored in a single structure.
image position
barycentric coordinate of the first pixel
number of pixels
triangle index inside the leaf node.
During the performed experiments we used a fairly simple rasterization process by
finding the UV bounds of an triangle and calculate the barycentric coordinates per
pixel inside the bounds. Even for complex models and huge images this process is
normally finished within 0.5 second. It could be that we want to change this algorithm
to reduce hickups when nodes are initialized during a stroke.
Reviewed By: brecht
Maniphest Tasks: T96710
Differential Revision: https://developer.blender.org/D14504
Diffstat (limited to 'source/blender/blenkernel/BKE_pbvh_pixels.hh')
-rw-r--r-- | source/blender/blenkernel/BKE_pbvh_pixels.hh | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/source/blender/blenkernel/BKE_pbvh_pixels.hh b/source/blender/blenkernel/BKE_pbvh_pixels.hh new file mode 100644 index 00000000000..35eb340d0a1 --- /dev/null +++ b/source/blender/blenkernel/BKE_pbvh_pixels.hh @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +#pragma once + +#include "BLI_math.h" +#include "BLI_math_vec_types.hh" +#include "BLI_rect.h" +#include "BLI_vector.hh" + +#include "DNA_image_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_image.h" +#include "BKE_image_wrappers.hh" + +#include "IMB_imbuf_types.h" + +namespace blender::bke::pbvh::pixels { + +struct TrianglePaintInput { + int3 vert_indices; + /** + * Delta barycentric coordinates between 2 neighbouring UV's in the U direction. + * + * Only the first two coordinates are stored. The third should be recalculated + */ + float2 delta_barycentric_coord_u; + + /** + * Initially only the vert indices are known. + * + * delta_barycentric_coord_u is initialized in a later stage as it requires image tile + * dimensions. + */ + TrianglePaintInput(const int3 vert_indices) + : vert_indices(vert_indices), delta_barycentric_coord_u(0.0f, 0.0f) + { + } +}; + +/** + * Data shared between pixels that belong to the same triangle. + * + * Data is stored as a list of structs, grouped by usage to improve performance (improves CPU + * cache prefetching). + */ +struct Triangles { + /** Data accessed by the inner loop of the painting brush. */ + Vector<TrianglePaintInput> paint_input; + + public: + void append(const int3 vert_indices) + { + this->paint_input.append(TrianglePaintInput(vert_indices)); + } + + TrianglePaintInput &get_paint_input(const int index) + { + return paint_input[index]; + } + + const TrianglePaintInput &get_paint_input(const int index) const + { + return paint_input[index]; + } + + void clear() + { + paint_input.clear(); + } + + uint64_t size() const + { + return paint_input.size(); + } + + uint64_t mem_size() const + { + return paint_input.size() * sizeof(TrianglePaintInput); + } +}; + +/** + * Encode sequential pixels to reduce memory footprint. + */ +struct PackedPixelRow { + /** Barycentric coordinate of the first pixel. */ + float2 start_barycentric_coord; + /** Image coordinate starting of the first pixel. */ + ushort2 start_image_coordinate; + /** Number of sequential pixels encoded in this package. */ + ushort num_pixels; + /** Reference to the pbvh triangle index. */ + ushort triangle_index; +}; + +/** + * Node pixel data containing the pixels for a single UDIM tile. + */ +struct UDIMTilePixels { + /** UDIM Tile number. */ + short tile_number; + + struct { + bool dirty : 1; + } flags; + + /* Dirty region of the tile in image space. */ + rcti dirty_region; + + Vector<PackedPixelRow> pixel_rows; + + UDIMTilePixels() + { + flags.dirty = false; + BLI_rcti_init_minmax(&dirty_region); + } + + void mark_dirty(const PackedPixelRow &pixel_row) + { + int2 start_image_coord(pixel_row.start_image_coordinate.x, pixel_row.start_image_coordinate.y); + BLI_rcti_do_minmax_v(&dirty_region, start_image_coord); + BLI_rcti_do_minmax_v(&dirty_region, start_image_coord + int2(pixel_row.num_pixels + 1, 0)); + flags.dirty = true; + } + + void clear_dirty() + { + BLI_rcti_init_minmax(&dirty_region); + flags.dirty = false; + } +}; + +struct NodeData { + struct { + bool dirty : 1; + } flags; + + Vector<UDIMTilePixels> tiles; + Triangles triangles; + + NodeData() + { + flags.dirty = false; + } + + UDIMTilePixels *find_tile_data(const image::ImageTileWrapper &image_tile) + { + for (UDIMTilePixels &tile : tiles) { + if (tile.tile_number == image_tile.get_tile_number()) { + return &tile; + } + } + return nullptr; + } + + void mark_region(Image &image, const image::ImageTileWrapper &image_tile, ImBuf &image_buffer) + { + UDIMTilePixels *tile = find_tile_data(image_tile); + if (tile && tile->flags.dirty) { + BKE_image_partial_update_mark_region( + &image, image_tile.image_tile, &image_buffer, &tile->dirty_region); + tile->clear_dirty(); + } + } + + void clear_data() + { + tiles.clear(); + triangles.clear(); + } + + static void free_func(void *instance) + { + NodeData *node_data = static_cast<NodeData *>(instance); + MEM_delete(node_data); + } +}; + +NodeData &BKE_pbvh_pixels_node_data_get(PBVHNode &node); +void BKE_pbvh_pixels_mark_image_dirty(PBVHNode &node, Image &image, ImageUser &image_user); + +} // namespace blender::bke::pbvh::pixels |