Subdiv: Store multires sculpt grid visibility in BitGroupVector #115687

Merged
Hans Goudey merged 8 commits from HooglyBoogly/blender:subdiv-ccg-grid-hidden-bit-group-vector into main 2023-12-02 20:05:40 +01:00
16 changed files with 303 additions and 230 deletions

View File

@ -224,7 +224,7 @@ bool BKE_paint_always_hide_test(const Object *ob);
* Returns non-zero if any of the corners of the grid
* face whose inner corner is at (x, y) are hidden, zero otherwise.
*/
bool paint_is_grid_face_hidden(const unsigned int *grid_hidden, int gridsize, int x, int y);
bool paint_is_grid_face_hidden(blender::BoundedBitSpan grid_hidden, int gridsize, int x, int y);
/**
* Return true if all vertices in the face are visible, false otherwise.
*/

View File

@ -12,7 +12,7 @@
#include <optional>
#include <string>
#include "BLI_bitmap.h"
#include "BLI_bit_group_vector.hh"
#include "BLI_compiler_compat.h"
#include "BLI_function_ref.hh"
#include "BLI_index_mask.hh"
@ -320,7 +320,7 @@ void BKE_pbvh_sync_visibility_from_verts(PBVH *pbvh, Mesh *me);
/**
* Returns the number of visible quads in the nodes' grids.
*/
int BKE_pbvh_count_grid_quads(blender::Span<const BLI_bitmap *> grid_hidden,
int BKE_pbvh_count_grid_quads(const blender::BitGroupVector<> &grid_visibility,
const int *grid_indices,
int totgrid,
int gridsize,
@ -464,7 +464,8 @@ struct PBVHVertexIter {
CCGKey key;
CCGElem *const *grids;
CCGElem *grid;
BLI_bitmap *const *grid_hidden, *gh;
const blender::BitGroupVector<> *grid_hidden;
std::optional<blender::BoundedBitSpan> gh;
const int *grid_indices;
int totgrid;
int gridsize;
@ -508,7 +509,12 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
vi.index = vi.vertex.i = vi.grid_indices[vi.g] * vi.key.grid_area - 1; \
vi.grid = CCG_elem_offset(&vi.key, vi.grids[vi.grid_indices[vi.g]], -1); \
if (mode == PBVH_ITER_UNIQUE) { \
vi.gh = vi.grid_hidden[vi.grid_indices[vi.g]]; \
if (vi.grid_hidden) { \
vi.gh.emplace((*vi.grid_hidden)[vi.grid_indices[vi.g]]); \
} \
else { \
vi.gh.reset(); \
} \
} \
} \
else { \
@ -527,7 +533,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
vi.vertex.i++; \
vi.visible = true; \
if (vi.gh) { \
if (BLI_BITMAP_TEST(vi.gh, vi.gy * vi.gridsize + vi.gx)) { \
if ((*vi.gh)[vi.gy * vi.gridsize + vi.gx]) { \
continue; \
} \
} \

View File

@ -9,7 +9,7 @@
#pragma once
#include "BLI_array.hh"
#include "BLI_bitmap.h"
#include "BLI_bit_group_vector.hh"
#include "BLI_offset_indices.hh"
#include "BLI_sys_types.h"
@ -171,7 +171,9 @@ struct SubdivCCG {
blender::Array<SubdivCCGAdjacentVertex> adjacent_verts;
blender::Array<DMFlagMat> grid_flag_mats;
blender::Array<BLI_bitmap *> grid_hidden;
/** Store the visibility of the items in each grid. If empty, everything is visible. */
blender::BitGroupVector<> grid_hidden;
/* TODO(sergey): Consider adding some accessors to a "decoded" geometry,
* to make integration with draw manager and such easy.
@ -315,5 +317,5 @@ SubdivCCGAdjacencyType BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(
const int *BKE_subdiv_ccg_start_face_grid_index_ensure(SubdivCCG &subdiv_ccg);
const int *BKE_subdiv_ccg_start_face_grid_index_get(const SubdivCCG &subdiv_ccg);
void BKE_subdiv_ccg_grid_hidden_ensure(SubdivCCG &subdiv_ccg, int grid_index);
void BKE_subdiv_ccg_grid_hidden_free(SubdivCCG &subdiv_ccg, int grid_index);
blender::BitGroupVector<> &BKE_subdiv_ccg_grid_hidden_ensure(SubdivCCG &subdiv_ccg);
void BKE_subdiv_ccg_grid_hidden_free(SubdivCCG &subdiv_ccg);

View File

@ -1282,13 +1282,14 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain
BKE_paint_runtime_init(scene->toolsettings, p);
}
bool paint_is_grid_face_hidden(const uint *grid_hidden, int gridsize, int x, int y)
bool paint_is_grid_face_hidden(const blender::BoundedBitSpan grid_hidden,
int gridsize,
int x,
int y)
{
/* Skip face if any of its corners are hidden. */
return (BLI_BITMAP_TEST(grid_hidden, y * gridsize + x) ||
BLI_BITMAP_TEST(grid_hidden, y * gridsize + x + 1) ||
BLI_BITMAP_TEST(grid_hidden, (y + 1) * gridsize + x + 1) ||
BLI_BITMAP_TEST(grid_hidden, (y + 1) * gridsize + x));
return grid_hidden[y * gridsize + x] || grid_hidden[y * gridsize + x + 1] ||
grid_hidden[(y + 1) * gridsize + x + 1] || grid_hidden[(y + 1) * gridsize + x];
}
bool paint_is_bmesh_face_hidden(const BMFace *f)
@ -2148,32 +2149,24 @@ void BKE_sculpt_sync_face_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg)
const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
".hide_poly", ATTR_DOMAIN_FACE, false);
if (hide_poly.is_single() && !hide_poly.get_internal_single()) {
/* Nothing is hidden, so we can just remove all visibility bitmaps. */
for (const int i : subdiv_ccg->grid_hidden.index_range()) {
BKE_subdiv_ccg_grid_hidden_free(*subdiv_ccg, i);
}
BKE_subdiv_ccg_grid_hidden_free(*subdiv_ccg);
return;
}
const OffsetIndices<int> faces = mesh->faces();
const VArraySpan<bool> hide_poly_span(hide_poly);
CCGKey key;
BKE_subdiv_ccg_key_top_level(key, *subdiv_ccg);
for (int i = 0; i < mesh->totloop; i++) {
const int face_index = BKE_subdiv_ccg_grid_to_face_index(*subdiv_ccg, i);
const bool is_hidden = hide_poly_span[face_index];
/* Avoid creating and modifying the grid_hidden bitmap if the base mesh face is visible and
* there is not bitmap for the grid. This is because missing grid_hidden implies grid is fully
* visible. */
if (is_hidden) {
BKE_subdiv_ccg_grid_hidden_ensure(*subdiv_ccg, i);
BitGroupVector<> &grid_hidden = BKE_subdiv_ccg_grid_hidden_ensure(*subdiv_ccg);
threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
for (const int i : range) {
const bool face_hidden = hide_poly_span[i];
for (const int corner : faces[i]) {
grid_hidden[corner].set_all(face_hidden);
}
}
BLI_bitmap *gh = subdiv_ccg->grid_hidden[i];
if (gh) {
BLI_bitmap_set_all(gh, is_hidden, key.grid_area);
}
}
});
}
static PBVH *build_pbvh_for_dynamic_topology(Object *ob)

View File

@ -45,6 +45,7 @@
#include "pbvh_intern.hh"
using blender::BitGroupVector;
using blender::float3;
using blender::MutableSpan;
using blender::Span;
@ -390,14 +391,16 @@ static void update_vb(PBVH *pbvh, PBVHNode *node, const Span<BBC> prim_bbc, int
node->orig_vb = node->vb;
}
int BKE_pbvh_count_grid_quads(const Span<const BLI_bitmap *> grid_hidden,
int BKE_pbvh_count_grid_quads(const BitGroupVector<> &grid_hidden,
const int *grid_indices,
int totgrid,
int gridsize,
int display_gridsize)
{
const int gridarea = (gridsize - 1) * (gridsize - 1);
int totquad = 0;
if (grid_hidden.is_empty()) {
return gridarea * totgrid;
}
/* grid hidden layer is present, so have to check each grid for
* visibility */
@ -407,22 +410,17 @@ int BKE_pbvh_count_grid_quads(const Span<const BLI_bitmap *> grid_hidden,
int skip = depth2 < depth1 ? 1 << (depth1 - depth2 - 1) : 1;
int totquad = 0;
for (int i = 0; i < totgrid; i++) {
const BLI_bitmap *gh = grid_hidden[grid_indices[i]];
if (gh) {
/* grid hidden are present, have to check each element */
for (int y = 0; y < gridsize - skip; y += skip) {
for (int x = 0; x < gridsize - skip; x += skip) {
if (!paint_is_grid_face_hidden(gh, gridsize, x, y)) {
totquad++;
}
const blender::BoundedBitSpan gh = grid_hidden[grid_indices[i]];
/* grid hidden are present, have to check each element */
for (int y = 0; y < gridsize - skip; y += skip) {
for (int x = 0; x < gridsize - skip; x += skip) {
if (!paint_is_grid_face_hidden(gh, gridsize, x, y)) {
totquad++;
}
}
}
else {
totquad += gridarea;
}
}
return totquad;
@ -1466,7 +1464,6 @@ static blender::draw::pbvh::PBVH_GPU_Args pbvh_draw_args_init(const Mesh &mesh,
args.grid_indices = node.prim_indices;
args.subdiv_ccg = pbvh.subdiv_ccg;
args.grids = pbvh.subdiv_ccg->grids;
args.grid_hidden = pbvh.subdiv_ccg->grid_hidden;
args.grid_flag_mats = pbvh.subdiv_ccg->grid_flag_mats;
args.vert_normals = pbvh.vert_normals;
break;
@ -1662,20 +1659,24 @@ static void pbvh_faces_node_visibility_update(const Mesh &mesh, const Span<PBVHN
static void pbvh_grids_node_visibility_update(PBVH *pbvh, const Span<PBVHNode *> nodes)
{
using namespace blender;
const BitGroupVector<> &grid_hidden = pbvh->subdiv_ccg->grid_hidden;
if (grid_hidden.is_empty()) {
for (PBVHNode *node : nodes) {
BKE_pbvh_node_fully_hidden_set(node, false);
node->flag &= ~PBVH_UpdateVisibility;
}
return;
}
CCGKey key = *BKE_pbvh_get_grid_key(pbvh);
const Span<const BLI_bitmap *> grid_hidden = pbvh->subdiv_ccg->grid_hidden;
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (PBVHNode *node : nodes.slice(range)) {
for (const int grid_index : BKE_pbvh_node_get_grid_indices(*node)) {
const BLI_bitmap *gh = grid_hidden[grid_index];
if (!gh) {
BKE_pbvh_node_fully_hidden_set(node, false);
return;
}
const blender::BoundedBitSpan gh = grid_hidden[grid_index];
for (int y = 0; y < key.grid_size; y++) {
for (int x = 0; x < key.grid_size; x++) {
if (!BLI_BITMAP_TEST(gh, y * key.grid_size + x)) {
if (!gh[y * key.grid_size + x]) {
BKE_pbvh_node_fully_hidden_set(node, false);
return;
}
@ -2335,7 +2336,7 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh,
bool hit = false;
float nearest_vertex_co[3] = {0.0};
const CCGKey *gridkey = &pbvh->gridkey;
const Span<BLI_bitmap *> grid_hidden = pbvh->subdiv_ccg->grid_hidden;
const BitGroupVector<> &grid_hidden = pbvh->subdiv_ccg->grid_hidden;
const Span<CCGElem *> grids = pbvh->subdiv_ccg->grids;
for (int i = 0; i < totgrid; i++) {
@ -2345,13 +2346,11 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh,
continue;
}
const BLI_bitmap *gh = grid_hidden[grid_index];
for (int y = 0; y < gridsize - 1; y++) {
for (int x = 0; x < gridsize - 1; x++) {
/* check if grid face is hidden */
if (gh) {
if (paint_is_grid_face_hidden(gh, gridsize, x, y)) {
if (!grid_hidden.is_empty()) {
if (paint_is_grid_face_hidden(grid_hidden[grid_index], gridsize, x, y)) {
continue;
}
}
@ -2665,7 +2664,7 @@ static bool pbvh_grids_node_nearest_to_ray(PBVH *pbvh,
const int totgrid = node->prim_indices.size();
const int gridsize = pbvh->gridkey.grid_size;
bool hit = false;
const Span<BLI_bitmap *> grid_hidden = pbvh->subdiv_ccg->grid_hidden;
const BitGroupVector<> &grid_hidden = pbvh->subdiv_ccg->grid_hidden;
const Span<CCGElem *> grids = pbvh->subdiv_ccg->grids;
for (int i = 0; i < totgrid; i++) {
@ -2674,13 +2673,11 @@ static bool pbvh_grids_node_nearest_to_ray(PBVH *pbvh,
continue;
}
const BLI_bitmap *gh = grid_hidden[node->prim_indices[i]];
for (int y = 0; y < gridsize - 1; y++) {
for (int x = 0; x < gridsize - 1; x++) {
/* check if grid face is hidden */
if (gh) {
if (paint_is_grid_face_hidden(gh, gridsize, x, y)) {
if (!grid_hidden.is_empty()) {
if (paint_is_grid_face_hidden(grid_hidden[node->prim_indices[i]], gridsize, x, y)) {
continue;
}
}
@ -3078,9 +3075,10 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
vi->bm_vdata, CD_PROP_FLOAT, ".sculpt_mask");
}
vi->gh = nullptr;
vi->gh.reset();
if (vi->grids && mode == PBVH_ITER_UNIQUE) {
vi->grid_hidden = pbvh->subdiv_ccg->grid_hidden.data();
vi->grid_hidden = pbvh->subdiv_ccg->grid_hidden.is_empty() ? nullptr :
&pbvh->subdiv_ccg->grid_hidden;
}
vi->mask = 0.0f;
@ -3294,20 +3292,23 @@ void BKE_pbvh_sync_visibility_from_verts(PBVH *pbvh, Mesh *mesh)
}
case PBVH_GRIDS: {
const OffsetIndices faces = mesh->faces();
const Span<BLI_bitmap *> grid_hidden = pbvh->subdiv_ccg->grid_hidden;
const BitGroupVector<> &grid_hidden = pbvh->subdiv_ccg->grid_hidden;
CCGKey key = pbvh->gridkey;
IndexMaskMemory memory;
const IndexMask hidden_faces = IndexMask::from_predicate(
faces.index_range(), GrainSize(1024), memory, [&](const int i) {
const IndexRange face = faces[i];
return std::any_of(face.begin(), face.end(), [&](const int corner) {
if (!grid_hidden[corner]) {
return false;
}
return BLI_BITMAP_TEST_BOOL(grid_hidden[corner], key.grid_area - 1);
});
});
const IndexMask hidden_faces =
grid_hidden.is_empty() ?
IndexMask::from_predicate(faces.index_range(),
GrainSize(1024),
memory,
[&](const int i) {
const IndexRange face = faces[i];
return std::any_of(
face.begin(), face.end(), [&](const int corner) {
return grid_hidden[corner][key.grid_area - 1];
});
}) :
IndexMask();
MutableAttributeAccessor attributes = mesh->attributes_for_write();
if (hidden_faces.is_empty()) {

View File

@ -144,10 +144,6 @@ static void subdiv_ccg_alloc_elements(SubdivCCG &subdiv_ccg, Subdiv &subdiv)
subdiv_ccg.grids[grid_index] = (CCGElem *)&subdiv_ccg.grids_storage[grid_offset];
}
subdiv_ccg.grid_flag_mats.reinitialize(num_grids);
subdiv_ccg.grid_hidden.reinitialize(num_grids);
for (int grid_index = 0; grid_index < num_grids; grid_index++) {
subdiv_ccg.grid_hidden[grid_index] = BLI_BITMAP_NEW(grid_area, "ccg grid hidden");
}
/* TODO(sergey): Allocate memory for loose elements. */
subdiv_ccg.faces.reinitialize(num_faces);
subdiv_ccg.grid_to_face_map.reinitialize(num_grids);
@ -563,10 +559,6 @@ Mesh *BKE_subdiv_to_ccg_mesh(Subdiv &subdiv,
void BKE_subdiv_ccg_destroy(SubdivCCG *subdiv_ccg)
{
for (const int grid_index : subdiv_ccg->grid_hidden.index_range()) {
MEM_SAFE_FREE(subdiv_ccg->grid_hidden[grid_index]);
}
if (subdiv_ccg->subdiv != nullptr) {
BKE_subdiv_free(subdiv_ccg->subdiv);
}
@ -1684,20 +1676,18 @@ SubdivCCGAdjacencyType BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(
return SUBDIV_CCG_ADJACENT_NONE;
}
void BKE_subdiv_ccg_grid_hidden_ensure(SubdivCCG &subdiv_ccg, int grid_index)
blender::BitGroupVector<> &BKE_subdiv_ccg_grid_hidden_ensure(SubdivCCG &subdiv_ccg)
{
if (subdiv_ccg.grid_hidden[grid_index] != nullptr) {
return;
if (subdiv_ccg.grid_hidden.is_empty()) {
const int grid_area = subdiv_ccg.grid_size * subdiv_ccg.grid_size;
subdiv_ccg.grid_hidden = blender::BitGroupVector<>(subdiv_ccg.grids.size(), grid_area, false);
}
CCGKey key;
BKE_subdiv_ccg_key_top_level(key, subdiv_ccg);
subdiv_ccg.grid_hidden[grid_index] = BLI_BITMAP_NEW(key.grid_area, __func__);
return subdiv_ccg.grid_hidden;
}
void BKE_subdiv_ccg_grid_hidden_free(SubdivCCG &subdiv_ccg, int grid_index)
void BKE_subdiv_ccg_grid_hidden_free(SubdivCCG &subdiv_ccg)
{
MEM_SAFE_FREE(subdiv_ccg.grid_hidden[grid_index]);
subdiv_ccg.grid_hidden = {};
}
static void subdiv_ccg_coord_to_ptex_coord(const SubdivCCG &subdiv_ccg,

View File

@ -76,6 +76,11 @@ class BitGroupVector {
return aligned_group_size_ == 0 ? 0 : data_.size() / aligned_group_size_;
}
bool is_empty() const
{
return this->size() == 0;
}
/** Number of bits per group. */
int64_t group_size() const
{

View File

@ -363,6 +363,11 @@ class MutableBoundedBitSpan : public MutableBitSpan {
return {data_, bit_range_.take_front(n)};
}
BoundedBitSpan as_span() const
{
return BoundedBitSpan(data_, bit_range_);
}
void copy_from(const BitSpan other);
void copy_from(const BoundedBitSpan other);
};

View File

@ -255,6 +255,16 @@ template<typename BitSpanT> inline bool any_bit_set(const BitSpanT &arg)
return has_common_set_bits(arg);
}
template<typename... BitSpanT> inline bool has_common_unset_bits(const BitSpanT &...args)
{
return any_set_expr([](const auto... x) { return ~(x | ...); }, args...);
}
template<typename BitSpanT> inline bool any_bit_unset(const BitSpanT &arg)
{
return has_common_unset_bits(arg);
}
template<typename BitSpanT, typename Fn> inline void foreach_1_index(const BitSpanT &data, Fn &&fn)
{
foreach_1_index_expr([](const BitInt x) { return x; }, fn, data);

View File

@ -95,10 +95,10 @@ class BitVector {
BitVector(NoExceptConstructor, Allocator allocator = {}) noexcept : BitVector(allocator) {}
BitVector(const BitVector &other) : BitVector(NoExceptConstructor(), other.allocator_)
BitVector(const BoundedBitSpan span) : BitVector(NoExceptConstructor())
{
const int64_t ints_to_copy = other.used_ints_amount();
if (other.size_in_bits_ <= BitsInInlineBuffer) {
const int64_t ints_to_copy = required_ints_for_bits(span.size());
if (span.size() <= BitsInInlineBuffer) {
/* The data is copied into the owned inline buffer. */
data_ = inline_buffer_;
capacity_in_bits_ = BitsInInlineBuffer;
@ -109,8 +109,13 @@ class BitVector {
allocator_.allocate(ints_to_copy * sizeof(BitInt), AllocationAlignment, __func__));
capacity_in_bits_ = ints_to_copy * BitsPerInt;
}
size_in_bits_ = other.size_in_bits_;
uninitialized_copy_n(other.data_, ints_to_copy, data_);
size_in_bits_ = span.size();
uninitialized_copy_n(span.data(), ints_to_copy, data_);
}
BitVector(const BitVector &other) : BitVector(BoundedBitSpan(other))
{
allocator_ = other.allocator_;
}
BitVector(BitVector &&other) noexcept : BitVector(NoExceptConstructor(), other.allocator_)

View File

@ -10,7 +10,6 @@
/* Needed for BKE_ccg.h. */
#include "BLI_assert.h"
#include "BLI_bitmap.h"
#include "BLI_math_vector_types.hh"
#include "BLI_offset_indices.hh"
#include "BLI_set.hh"
@ -80,7 +79,6 @@ struct PBVH_GPU_Args {
Span<int> grid_indices;
CCGKey ccg_key;
Span<CCGElem *> grids;
Span<const BLI_bitmap *> grid_hidden;
Span<int> prim_indices;

View File

@ -351,7 +351,7 @@ struct PBVHBatches {
break;
}
case PBVH_GRIDS: {
count = BKE_pbvh_count_grid_quads(args.grid_hidden,
count = BKE_pbvh_count_grid_quads(args.subdiv_ccg->grid_hidden,
args.grid_indices.data(),
args.grid_indices.size(),
args.ccg_key.grid_size,
@ -1111,6 +1111,7 @@ struct PBVHBatches {
void create_index_grids(const PBVH_GPU_Args &args, bool do_coarse)
{
const BitGroupVector<> &grid_hidden = args.subdiv_ccg->grid_hidden;
const int *mat_index = static_cast<const int *>(
CustomData_get_layer_named(args.face_data, CD_PROP_INT32, "material_index"));
@ -1134,14 +1135,15 @@ struct PBVHBatches {
for (const int grid_index : args.grid_indices) {
bool smooth = !args.grid_flag_mats[grid_index].sharp;
const BLI_bitmap *gh = args.grid_hidden[grid_index];
for (int y = 0; y < gridsize - 1; y += skip) {
for (int x = 0; x < gridsize - 1; x += skip) {
if (gh && paint_is_grid_face_hidden(gh, gridsize, x, y)) {
/* Skip hidden faces by just setting smooth to true. */
smooth = true;
goto outer_loop_break;
if (!grid_hidden.is_empty()) {
const BoundedBitSpan gh = grid_hidden[grid_index];
for (int y = 0; y < gridsize - 1; y += skip) {
for (int x = 0; x < gridsize - 1; x += skip) {
if (paint_is_grid_face_hidden(gh, gridsize, x, y)) {
/* Skip hidden faces by just setting smooth to true. */
smooth = true;
goto outer_loop_break;
}
}
}
}
@ -1159,7 +1161,7 @@ struct PBVHBatches {
const CCGKey *key = &args.ccg_key;
uint visible_quad_len = BKE_pbvh_count_grid_quads(
args.grid_hidden, args.grid_indices.data(), totgrid, key->grid_size, display_gridsize);
grid_hidden, args.grid_indices.data(), totgrid, key->grid_size, display_gridsize);
GPU_indexbuf_init(&elb, GPU_PRIM_TRIS, 2 * visible_quad_len, INT_MAX);
GPU_indexbuf_init(&elb_lines,
@ -1174,12 +1176,13 @@ struct PBVHBatches {
uint v0, v1, v2, v3;
bool grid_visible = false;
const BLI_bitmap *gh = args.grid_hidden[args.grid_indices[i]];
const BoundedBitSpan gh = grid_hidden.is_empty() ? BoundedBitSpan() :
grid_hidden[args.grid_indices[i]];
for (int j = 0; j < gridsize - skip; j += skip) {
for (int k = 0; k < gridsize - skip; k += skip) {
/* Skip hidden grid face */
if (gh && paint_is_grid_face_hidden(gh, gridsize, k, j)) {
if (!gh.is_empty() && paint_is_grid_face_hidden(gh, gridsize, k, j)) {
continue;
}
/* Indices in a Clockwise QUAD disposition. */
@ -1212,13 +1215,14 @@ struct PBVHBatches {
for (int i = 0; i < totgrid; i++, offset += grid_vert_len) {
bool grid_visible = false;
const BLI_bitmap *gh = args.grid_hidden[args.grid_indices[i]];
const BoundedBitSpan gh = grid_hidden.is_empty() ? BoundedBitSpan() :
grid_hidden[args.grid_indices[i]];
uint v0, v1, v2, v3;
for (int j = 0; j < gridsize - skip; j += skip) {
for (int k = 0; k < gridsize - skip; k += skip) {
/* Skip hidden grid face */
if (gh && paint_is_grid_face_hidden(gh, gridsize, k, j)) {
if (!gh.is_empty() && paint_is_grid_face_hidden(gh, gridsize, k, j)) {
continue;
}

View File

@ -10,7 +10,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_array_utils.hh"
#include "BLI_bitmap.h"
#include "BLI_bit_span_ops.hh"
#include "BLI_enumerable_thread_specific.hh"
#include "BLI_math_geom.h"
#include "BLI_math_matrix.h"
@ -156,12 +156,12 @@ static void vert_hide_update(Object &object,
}
static void partialvis_update_mesh(Object &object,
PBVH &pbvh,
const VisAction action,
const VisArea area,
const float planes[4][4],
const Span<PBVHNode *> nodes)
{
PBVH &pbvh = *object.sculpt->pbvh;
Mesh &mesh = *static_cast<Mesh *>(object.data);
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
if (action == VisAction::Show && !attributes.contains(".hide_vert")) {
@ -195,7 +195,7 @@ static void partialvis_update_mesh(Object &object,
break;
}
break;
case VisArea::Masked:
case VisArea::Masked: {
const VArraySpan<float> mask = *attributes.lookup<float>(".sculpt_mask", ATTR_DOMAIN_POINT);
if (action == VisAction::Show && mask.is_empty()) {
vert_show_all(object, nodes);
@ -210,96 +210,154 @@ static void partialvis_update_mesh(Object &object,
});
}
break;
}
}
BKE_mesh_flush_hidden_from_verts(&mesh);
}
/* Hide or show elements in multires grids with a special GridFlags
* customdata layer. */
static void partialvis_update_grids(Depsgraph *depsgraph,
Object *ob,
PBVH *pbvh,
static void grids_show_all(Depsgraph &depsgraph, Object &object, const Span<PBVHNode *> nodes)
{
Mesh &mesh = *static_cast<Mesh *>(object.data);
PBVH &pbvh = *object.sculpt->pbvh;
SubdivCCG &subdiv_ccg = *object.sculpt->subdiv_ccg;
const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
if (!grid_hidden.is_empty()) {
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (PBVHNode *node : nodes.slice(range)) {
const Span<int> grids = BKE_pbvh_node_get_grid_indices(*node);
if (std::any_of(grids.begin(), grids.end(), [&](const int i) {
return bits::any_bit_set(grid_hidden[i]);
}))
{
SCULPT_undo_push_node(&object, node, SCULPT_UNDO_HIDDEN);
BKE_pbvh_node_mark_rebuild_draw(node);
}
}
});
}
for (PBVHNode *node : nodes) {
BKE_pbvh_node_fully_hidden_set(node, false);
}
BKE_subdiv_ccg_grid_hidden_free(subdiv_ccg);
BKE_pbvh_sync_visibility_from_verts(&pbvh, &mesh);
multires_mark_as_modified(&depsgraph, &object, MULTIRES_HIDDEN_MODIFIED);
}
static void grid_hide_update(Depsgraph &depsgraph,
Object &object,
const Span<PBVHNode *> nodes,
const FunctionRef<void(const int, MutableBoundedBitSpan)> calc_hide)
{
Mesh &mesh = *static_cast<Mesh *>(object.data);
PBVH &pbvh = *object.sculpt->pbvh;
SubdivCCG &subdiv_ccg = *object.sculpt->subdiv_ccg;
BitGroupVector<> &grid_hidden = BKE_subdiv_ccg_grid_hidden_ensure(subdiv_ccg);
bool any_changed = false;
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (PBVHNode *node : nodes.slice(range)) {
bool changed = false;
bool any_visible = false;
for (const int grid : BKE_pbvh_node_get_grid_indices(*node)) {
MutableBoundedBitSpan gh = grid_hidden[grid];
const BitVector<1024> old_hide(gh);
calc_hide(grid, gh);
changed |= !bits::spans_equal(old_hide, gh);
any_visible |= bits::any_bit_unset(gh);
}
if (!changed) {
continue;
}
any_changed = true;
SCULPT_undo_push_node(&object, node, SCULPT_UNDO_HIDDEN);
/* Don't tag a visibility update, we handle updating the fully hidden status here. */
BKE_pbvh_node_mark_rebuild_draw(node);
BKE_pbvh_node_fully_hidden_set(node, !any_visible);
}
});
if (any_changed) {
multires_mark_as_modified(&depsgraph, &object, MULTIRES_HIDDEN_MODIFIED);
BKE_pbvh_sync_visibility_from_verts(&pbvh, &mesh);
}
}
static void partialvis_update_grids(Depsgraph &depsgraph,
Object &object,
const VisAction action,
const VisArea area,
const float planes[4][4],
const Span<PBVHNode *> nodes)
{
SubdivCCG &subdiv_ccg = *ob->sculpt->subdiv_ccg;
const Span<CCGElem *> grids = subdiv_ccg.grids;
const CCGKey key = *BKE_pbvh_get_grid_key(pbvh);
MutableSpan<BLI_bitmap *> grid_hidden = subdiv_ccg.grid_hidden;
for (PBVHNode *node : nodes) {
bool any_changed = false;
bool any_visible = false;
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN);
for (const int grid_index : BKE_pbvh_node_get_grid_indices(*node)) {
int any_hidden = 0;
BLI_bitmap *gh = grid_hidden[grid_index];
if (!gh) {
switch (action) {
case VisAction::Hide:
/* Create grid flags data. */
gh = grid_hidden[grid_index] = BLI_BITMAP_NEW(key.grid_area,
"partialvis_update_grids");
break;
case VisAction::Show:
/* Entire grid is visible, nothing to show. */
continue;
}
}
else if (action == VisAction::Show && area == VisArea::All) {
/* Special case if we're showing all, just free the grid. */
MEM_freeN(gh);
grid_hidden[grid_index] = nullptr;
any_changed = true;
any_visible = true;
continue;
}
for (int y = 0; y < key.grid_size; y++) {
for (int x = 0; x < key.grid_size; x++) {
CCGElem *elem = CCG_grid_elem(&key, grids[grid_index], x, y);
const float *co = CCG_elem_co(&key, elem);
float mask = key.has_mask ? *CCG_elem_mask(&key, elem) : 0.0f;
/* Skip grid element if not in the effected area. */
if (is_effected(area, planes, co, mask)) {
/* Set or clear the hide flag. */
BLI_BITMAP_SET(gh, y * key.grid_size + x, action == VisAction::Hide);
any_changed = true;
}
/* Keep track of whether any elements are still hidden. */
if (BLI_BITMAP_TEST(gh, y * key.grid_size + x)) {
any_hidden = true;
}
else {
any_visible = true;
}
}
}
/* If everything in the grid is now visible, free the grid flags. */
if (!any_hidden) {
MEM_freeN(gh);
grid_hidden[grid_index] = nullptr;
}
}
/* Mark updates if anything was hidden/shown. */
if (any_changed) {
BKE_pbvh_node_mark_rebuild_draw(node);
BKE_pbvh_node_fully_hidden_set(node, !any_visible);
multires_mark_as_modified(depsgraph, ob, MULTIRES_HIDDEN_MODIFIED);
}
PBVH &pbvh = *object.sculpt->pbvh;
SubdivCCG &subdiv_ccg = *object.sculpt->subdiv_ccg;
if (action == VisAction::Show && area == VisArea::All) {
grids_show_all(depsgraph, object, nodes);
return;
}
BKE_pbvh_sync_visibility_from_verts(pbvh, static_cast<Mesh *>(ob->data));
const bool value = action_to_hide(action);
switch (area) {
case VisArea::Inside:
case VisArea::Outside: {
const CCGKey key = *BKE_pbvh_get_grid_key(&pbvh);
const Span<CCGElem *> grids = subdiv_ccg.grids;
grid_hide_update(
depsgraph, object, nodes, [&](const int grid_index, MutableBoundedBitSpan hide) {
CCGElem *grid = grids[grid_index];
for (const int y : IndexRange(key.grid_size)) {
for (const int x : IndexRange(key.grid_size)) {
CCGElem *elem = CCG_grid_elem(&key, grid, x, y);
if (isect_point_planes_v3(planes, 4, CCG_elem_co(&key, elem))) {
hide[y * key.grid_size + x].set(value);
}
}
}
});
break;
}
case VisArea::All:
switch (action) {
case VisAction::Hide:
grid_hide_update(
depsgraph, object, nodes, [&](const int /*verts*/, MutableBoundedBitSpan hide) {
hide.fill(true);
});
break;
case VisAction::Show:
grids_show_all(depsgraph, object, nodes);
break;
}
break;
case VisArea::Masked: {
const CCGKey key = *BKE_pbvh_get_grid_key(&pbvh);
const Span<CCGElem *> grids = subdiv_ccg.grids;
if (!key.has_mask) {
grid_hide_update(
depsgraph, object, nodes, [&](const int /*verts*/, MutableBoundedBitSpan hide) {
hide.fill(value);
});
}
else {
grid_hide_update(
depsgraph, object, nodes, [&](const int grid_index, MutableBoundedBitSpan hide) {
CCGElem *grid = grids[grid_index];
for (const int y : IndexRange(key.grid_size)) {
for (const int x : IndexRange(key.grid_size)) {
CCGElem *elem = CCG_grid_elem(&key, grid, x, y);
if (*CCG_elem_mask(&key, elem) > 0.5f) {
hide[y * key.grid_size + x].set(value);
}
}
}
});
}
break;
}
}
}
static void partialvis_update_bmesh_verts(BMesh *bm,
@ -464,10 +522,10 @@ static int hide_show_exec(bContext *C, wmOperator *op)
switch (pbvh_type) {
case PBVH_FACES:
partialvis_update_mesh(*ob, *pbvh, action, area, clip_planes, nodes);
partialvis_update_mesh(*ob, action, area, clip_planes, nodes);
break;
case PBVH_GRIDS:
partialvis_update_grids(depsgraph, ob, pbvh, action, area, clip_planes, nodes);
partialvis_update_grids(*depsgraph, *ob, action, area, clip_planes, nodes);
break;
case PBVH_BMESH:
partialvis_update_bmesh(ob, pbvh, action, area, clip_planes, nodes);

View File

@ -417,9 +417,8 @@ bool SCULPT_vertex_visible_get(const SculptSession *ss, PBVHVertRef vertex)
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
const int grid_index = vertex.i / key->grid_area;
const int vertex_index = vertex.i - grid_index * key->grid_area;
const blender::Span<const BLI_bitmap *> grid_hidden = ss->subdiv_ccg->grid_hidden;
if (grid_hidden[grid_index]) {
return !BLI_BITMAP_TEST(grid_hidden[grid_index], vertex_index);
if (!ss->subdiv_ccg->grid_hidden.is_empty()) {
return !ss->subdiv_ccg->grid_hidden[grid_index][vertex_index];
}
}
}

View File

@ -200,7 +200,7 @@ struct SculptUndoNode {
int maxgrid; /* same for grid */
int gridsize; /* same for grid */
blender::Array<int> grids; /* to restore into right location */
BLI_bitmap **grid_hidden;
blender::BitGroupVector<> grid_hidden;
/* bmesh */
BMLogEntry *bm_entry;

View File

@ -510,10 +510,19 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode, bool
hide_vert.finish();
}
else if (!unode->grids.is_empty() && subdiv_ccg != nullptr) {
if (unode->grid_hidden.is_empty()) {
BKE_subdiv_ccg_grid_hidden_free(*subdiv_ccg);
return true;
}
blender::BitGroupVector<> &grid_hidden = BKE_subdiv_ccg_grid_hidden_ensure(*subdiv_ccg);
const Span<int> grids = unode->grids;
blender::MutableSpan<BLI_bitmap *> grid_hidden = subdiv_ccg->grid_hidden;
for (const int i : grids.index_range()) {
SWAP(BLI_bitmap *, unode->grid_hidden[i], grid_hidden[grids[i]]);
const int grid_index = grids[i];
/* Swap the two bit spans. */
blender::BitVector<512> tmp(grid_hidden[grid_index]);
grid_hidden[grid_index].copy_from(unode->grid_hidden[i].as_span());
unode->grid_hidden[i].copy_from(tmp);
}
}
@ -1087,13 +1096,6 @@ static void sculpt_undo_free_list(ListBase *lb)
while (unode != nullptr) {
SculptUndoNode *unode_next = unode->next;
if (unode->grid_hidden) {
for (const int i : unode->grids.index_range()) {
MEM_SAFE_FREE(unode->grid_hidden[i]);
}
MEM_freeN(unode->grid_hidden);
}
if (unode->bm_entry) {
BM_log_entry_drop(unode->bm_entry);
}
@ -1162,25 +1164,20 @@ SculptUndoNode *SCULPT_undo_get_first_node()
static size_t sculpt_undo_alloc_and_store_hidden(SculptSession *ss, SculptUndoNode *unode)
{
PBVHNode *node = static_cast<PBVHNode *>(unode->node);
const blender::Span<const BLI_bitmap *> grid_hidden = ss->subdiv_ccg->grid_hidden;
const Span<int> grid_indices = BKE_pbvh_node_get_grid_indices(*node);
size_t alloc_size = sizeof(*unode->grid_hidden) * grid_indices.size();
unode->grid_hidden = static_cast<BLI_bitmap **>(MEM_callocN(alloc_size, "unode->grid_hidden"));
for (const int i : grid_indices.index_range()) {
const int grid_index = grid_indices[i];
if (grid_hidden[grid_index]) {
unode->grid_hidden[i] = static_cast<BLI_bitmap *>(MEM_dupallocN(grid_hidden[grid_index]));
alloc_size += MEM_allocN_len(unode->grid_hidden[i]);
}
else {
unode->grid_hidden[i] = nullptr;
}
if (!ss->subdiv_ccg) {
return 0;
}
const blender::BitGroupVector<> grid_hidden = ss->subdiv_ccg->grid_hidden;
if (grid_hidden.is_empty()) {
return 0;
}
return alloc_size;
const Span<int> grid_indices = BKE_pbvh_node_get_grid_indices(*node);
for (const int i : grid_indices.index_range()) {
unode->grid_hidden[i].copy_from(grid_hidden[grid_indices[i]]);
}
return unode->grid_hidden.all_bits().full_ints_num() / blender::bits::BitsPerInt;
}
/* Allocate node and initialize its default fields specific for the given undo type.