Files
test/source/blender/blenkernel/intern/pbvh.cc
Sean Kim 8a812e334d Fix #125375: Sculpt undo with duplicate object causes crash
Introduced with d527e3a6bd.

Cached values are tagged as dirty during the update step, this can cause
conflicts where we attempt to then flush then changes into the PBVH but
have not yet updated the mesh pointers and reinitialized them.

This commit forcibly initializes the underlying data to prevent such
cases from happening when flushing to the PBVH.

Pull Request: https://projects.blender.org/blender/blender/pulls/125396
2024-07-26 18:01:11 +02:00

3022 lines
97 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include "MEM_guardedalloc.h"
#include <climits>
#include "BLI_array_utils.hh"
#include "BLI_bit_span_ops.hh"
#include "BLI_bitmap.h"
#include "BLI_bounds.hh"
#include "BLI_enumerable_thread_specific.hh"
#include "BLI_math_geom.h"
#include "BLI_math_matrix.h"
#include "BLI_math_vector.h"
#include "BLI_math_vector.hh"
#include "BLI_rand.h"
#include "BLI_task.h"
#include "BLI_task.hh"
#include "BLI_time.h"
#include "BLI_timeit.hh"
#include "BLI_utildefines.h"
#include "BLI_vector.hh"
#include "BLI_vector_set.hh"
#include "BKE_attribute.hh"
#include "BKE_ccg.hh"
#include "BKE_mesh.hh"
#include "BKE_mesh_mapping.hh"
#include "BKE_paint.hh"
#include "BKE_pbvh_api.hh"
#include "BKE_subdiv_ccg.hh"
#include "DRW_pbvh.hh"
#include "bmesh.hh"
#include "atomic_ops.h"
#include "pbvh_intern.hh"
namespace blender::bke::pbvh {
#define LEAF_LIMIT 10000
#define STACK_FIXED_DEPTH 100
struct PBVHStack {
blender::bke::pbvh::Node *node;
bool revisiting;
};
struct PBVHIter {
blender::bke::pbvh::Tree *pbvh;
blender::FunctionRef<bool(blender::bke::pbvh::Node &)> scb;
PBVHStack *stack;
int stacksize;
PBVHStack stackfixed[STACK_FIXED_DEPTH];
int stackspace;
};
/** Create invalid bounds for use with #math::min_max. */
static Bounds<float3> negative_bounds()
{
return {float3(std::numeric_limits<float>::max()), float3(std::numeric_limits<float>::lowest())};
}
static bool face_materials_match(const Span<int> material_indices,
const Span<bool> sharp_faces,
const int a,
const int b)
{
if (!material_indices.is_empty()) {
if (material_indices[a] != material_indices[b]) {
return false;
}
}
if (!sharp_faces.is_empty()) {
if (sharp_faces[a] != sharp_faces[b]) {
return false;
}
}
return true;
}
/* Adapted from BLI_kdopbvh.c */
/* Returns the index of the first element on the right of the partition */
static int partition_prim_indices(MutableSpan<int> prim_indices,
MutableSpan<int> prim_scratch,
int lo,
int hi,
int axis,
float mid,
const Span<Bounds<float3>> prim_bounds,
const Span<int> prim_to_face_map)
{
for (int i = lo; i < hi; i++) {
prim_scratch[i - lo] = prim_indices[i];
}
int lo2 = lo, hi2 = hi - 1;
int i1 = lo, i2 = 0;
while (i1 < hi) {
const int face_i = prim_to_face_map[prim_scratch[i2]];
const Bounds<float3> &bounds = prim_bounds[prim_scratch[i2]];
const bool side = math::midpoint(bounds.min[axis], bounds.max[axis]) >= mid;
while (i1 < hi && prim_to_face_map[prim_scratch[i2]] == face_i) {
prim_indices[side ? hi2-- : lo2++] = prim_scratch[i2];
i1++;
i2++;
}
}
return lo2;
}
/* Returns the index of the first element on the right of the partition */
static int partition_indices_material_faces(MutableSpan<int> indices,
const Span<int> prim_to_face_map,
const Span<int> material_indices,
const Span<bool> sharp_faces,
const int lo,
const int hi)
{
int i = lo, j = hi;
for (;;) {
const int first = prim_to_face_map[indices[lo]];
for (;
face_materials_match(material_indices, sharp_faces, first, prim_to_face_map[indices[i]]);
i++)
{
/* pass */
}
for (;
!face_materials_match(material_indices, sharp_faces, first, prim_to_face_map[indices[j]]);
j--)
{
/* pass */
}
if (!(i < j)) {
return i;
}
std::swap(indices[i], indices[j]);
i++;
}
}
/* Add a vertex to the map, with a positive value for unique vertices and
* a negative value for additional vertices */
static int map_insert_vert(Map<int, int> &map,
MutableSpan<bool> vert_bitmap,
int *face_verts,
int *uniq_verts,
int vertex)
{
return map.lookup_or_add_cb(vertex, [&]() {
int value;
if (!vert_bitmap[vertex]) {
vert_bitmap[vertex] = true;
value = *uniq_verts;
(*uniq_verts)++;
}
else {
value = ~(*face_verts);
(*face_verts)++;
}
return value;
});
}
/* Find vertices used by the faces in this node and update the draw buffers */
static void build_mesh_leaf_node(const Span<int> corner_verts,
const Span<int3> corner_tris,
MutableSpan<bool> vert_bitmap,
Node &node)
{
node.unique_verts_num_ = 0;
int shared_verts = 0;
const Span<int> prim_indices = node.prim_indices_;
/* reserve size is rough guess */
Map<int, int> map;
map.reserve(prim_indices.size());
node.face_vert_indices_.reinitialize(prim_indices.size());
for (const int i : prim_indices.index_range()) {
const int3 &tri = corner_tris[prim_indices[i]];
for (int j = 0; j < 3; j++) {
node.face_vert_indices_[i][j] = map_insert_vert(
map, vert_bitmap, &shared_verts, &node.unique_verts_num_, corner_verts[tri[j]]);
}
}
node.vert_indices_.reinitialize(node.unique_verts_num_ + shared_verts);
/* Build the vertex list, unique verts first */
for (const MapItem<int, int> item : map.items()) {
int value = item.value;
if (value < 0) {
value = -value + node.unique_verts_num_ - 1;
}
node.vert_indices_[value] = item.key;
}
for (const int i : prim_indices.index_range()) {
for (int j = 0; j < 3; j++) {
if (node.face_vert_indices_[i][j] < 0) {
node.face_vert_indices_[i][j] = -node.face_vert_indices_[i][j] + node.unique_verts_num_ -
1;
}
}
}
BKE_pbvh_node_mark_positions_update(&node);
BKE_pbvh_node_mark_rebuild_draw(&node);
}
/* Return zero if all primitives in the node can be drawn with the
* same material (including flat/smooth shading), non-zero otherwise */
static bool leaf_needs_material_split(Tree &pbvh,
const Span<int> prim_to_face_map,
const Span<int> material_indices,
const Span<bool> sharp_faces,
int offset,
int count)
{
if (count <= 1) {
return false;
}
const int first = prim_to_face_map[pbvh.prim_indices_[offset]];
for (int i = offset + count - 1; i > offset; i--) {
int prim = pbvh.prim_indices_[i];
if (!face_materials_match(material_indices, sharp_faces, first, prim_to_face_map[prim])) {
return true;
}
}
return false;
}
static void build_nodes_recursive_mesh(Tree &pbvh,
const Span<int> corner_verts,
const Span<int3> corner_tris,
const Span<int> tri_faces,
const Span<int> material_indices,
const Span<bool> sharp_faces,
const int leaf_limit,
MutableSpan<bool> vert_bitmap,
const int node_index,
const Bounds<float3> *cb,
const Span<Bounds<float3>> prim_bounds,
const int prim_offset,
const int prims_num,
MutableSpan<int> prim_scratch,
const int depth)
{
int end;
/* Decide whether this is a leaf or not */
const bool below_leaf_limit = prims_num <= leaf_limit || depth >= STACK_FIXED_DEPTH - 1;
if (below_leaf_limit) {
if (!leaf_needs_material_split(
pbvh, tri_faces, material_indices, sharp_faces, prim_offset, prims_num))
{
Node &node = pbvh.nodes_[node_index];
node.flag_ |= PBVH_Leaf;
node.prim_indices_ = pbvh.prim_indices_.as_span().slice(prim_offset, prims_num);
build_mesh_leaf_node(corner_verts, corner_tris, vert_bitmap, node);
return;
}
}
/* Add two child nodes */
pbvh.nodes_[node_index].children_offset_ = pbvh.nodes_.size();
pbvh.nodes_.resize(pbvh.nodes_.size() + 2);
Bounds<float3> cb_backing;
if (!below_leaf_limit) {
/* Find axis with widest range of primitive centroids */
if (!cb) {
cb_backing = negative_bounds();
for (int i = prim_offset + prims_num - 1; i >= prim_offset; i--) {
const int prim = pbvh.prim_indices_[i];
const float3 center = math::midpoint(prim_bounds[prim].min, prim_bounds[prim].max);
math::min_max(center, cb_backing.min, cb_backing.max);
}
cb = &cb_backing;
}
const int axis = math::dominant_axis(cb->max - cb->min);
/* Partition primitives along that axis */
end = partition_prim_indices(pbvh.prim_indices_,
prim_scratch,
prim_offset,
prim_offset + prims_num,
axis,
math::midpoint(cb->min[axis], cb->max[axis]),
prim_bounds,
tri_faces);
}
else {
/* Partition primitives by material */
end = partition_indices_material_faces(pbvh.prim_indices_,
tri_faces,
material_indices,
sharp_faces,
prim_offset,
prim_offset + prims_num - 1);
}
/* Build children */
build_nodes_recursive_mesh(pbvh,
corner_verts,
corner_tris,
tri_faces,
material_indices,
sharp_faces,
leaf_limit,
vert_bitmap,
pbvh.nodes_[node_index].children_offset_,
nullptr,
prim_bounds,
prim_offset,
end - prim_offset,
prim_scratch,
depth + 1);
build_nodes_recursive_mesh(pbvh,
corner_verts,
corner_tris,
tri_faces,
material_indices,
sharp_faces,
leaf_limit,
vert_bitmap,
pbvh.nodes_[node_index].children_offset_ + 1,
nullptr,
prim_bounds,
end,
prim_offset + prims_num - end,
prim_scratch,
depth + 1);
}
void update_mesh_pointers(Tree &pbvh, Mesh *mesh)
{
BLI_assert(pbvh.type() == Type::Mesh);
if (!pbvh.deformed_) {
/* Deformed data not matching the original mesh are owned directly by the
* Tree, and are set separately by #BKE_pbvh_vert_coords_apply. */
pbvh.vert_positions_ = mesh->vert_positions_for_write();
pbvh.vert_normals_ = mesh->vert_normals();
pbvh.face_normals_ = mesh->face_normals();
}
}
std::unique_ptr<Tree> build_mesh(Mesh *mesh)
{
std::unique_ptr<Tree> pbvh = std::make_unique<Tree>(Type::Mesh);
MutableSpan<float3> vert_positions = mesh->vert_positions_for_write();
const Span<int> corner_verts = mesh->corner_verts();
const Span<int3> corner_tris = mesh->corner_tris();
pbvh->mesh_ = mesh;
update_mesh_pointers(*pbvh, mesh);
const Span<int> tri_faces = mesh->corner_tri_faces();
Array<bool> vert_bitmap(mesh->verts_num, false);
const int leaf_limit = LEAF_LIMIT;
/* For each face, store the AABB and the AABB centroid */
Array<Bounds<float3>> prim_bounds(corner_tris.size());
const Bounds<float3> cb = threading::parallel_reduce(
corner_tris.index_range(),
1024,
negative_bounds(),
[&](const IndexRange range, const Bounds<float3> &init) {
Bounds<float3> current = init;
for (const int i : range) {
const int3 &tri = corner_tris[i];
Bounds<float3> &bounds = prim_bounds[i];
bounds = {vert_positions[corner_verts[tri[0]]]};
math::min_max(vert_positions[corner_verts[tri[1]]], bounds.min, bounds.max);
math::min_max(vert_positions[corner_verts[tri[2]]], bounds.min, bounds.max);
const float3 center = math::midpoint(prim_bounds[i].min, prim_bounds[i].max);
math::min_max(center, current.min, current.max);
}
return current;
},
[](const Bounds<float3> &a, const Bounds<float3> &b) { return bounds::merge(a, b); });
if (!corner_tris.is_empty()) {
const AttributeAccessor attributes = mesh->attributes();
const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", AttrDomain::Point);
const VArraySpan material_index = *attributes.lookup<int>("material_index", AttrDomain::Face);
const VArraySpan sharp_face = *attributes.lookup<bool>("sharp_face", AttrDomain::Face);
pbvh->prim_indices_.reinitialize(corner_tris.size());
array_utils::fill_index_range<int>(pbvh->prim_indices_);
pbvh->nodes_.resize(1);
build_nodes_recursive_mesh(*pbvh,
corner_verts,
corner_tris,
tri_faces,
material_index,
sharp_face,
leaf_limit,
vert_bitmap,
0,
&cb,
prim_bounds,
0,
corner_tris.size(),
Array<int>(pbvh->prim_indices_.size()),
0);
update_bounds(*pbvh);
store_bounds_orig(*pbvh);
if (!hide_vert.is_empty()) {
MutableSpan<Node> nodes = pbvh->nodes_;
threading::parallel_for(nodes.index_range(), 8, [&](const IndexRange range) {
for (const int i : range) {
const Span<int> verts = node_verts(nodes[i]);
if (std::all_of(verts.begin(), verts.end(), [&](const int i) { return hide_vert[i]; })) {
nodes[i].flag_ |= PBVH_FullyHidden;
}
}
});
}
}
return pbvh;
}
static void build_nodes_recursive_grids(Tree &pbvh,
const Span<int> material_indices,
const Span<bool> sharp_faces,
const int leaf_limit,
const int node_index,
const Bounds<float3> *cb,
const Span<Bounds<float3>> prim_bounds,
const int prim_offset,
const int prims_num,
MutableSpan<int> prim_scratch,
const int depth)
{
const Span<int> prim_to_face_map = pbvh.subdiv_ccg_->grid_to_face_map;
int end;
/* Decide whether this is a leaf or not */
const bool below_leaf_limit = prims_num <= leaf_limit || depth >= STACK_FIXED_DEPTH - 1;
if (below_leaf_limit) {
if (!leaf_needs_material_split(
pbvh, prim_to_face_map, material_indices, sharp_faces, prim_offset, prims_num))
{
Node &node = pbvh.nodes_[node_index];
node.flag_ |= PBVH_Leaf;
node.prim_indices_ = pbvh.prim_indices_.as_span().slice(prim_offset, prims_num);
BKE_pbvh_node_mark_positions_update(&node);
BKE_pbvh_node_mark_rebuild_draw(&node);
return;
}
}
/* Add two child nodes */
pbvh.nodes_[node_index].children_offset_ = pbvh.nodes_.size();
pbvh.nodes_.resize(pbvh.nodes_.size() + 2);
Bounds<float3> cb_backing;
if (!below_leaf_limit) {
/* Find axis with widest range of primitive centroids */
if (!cb) {
cb_backing = negative_bounds();
for (int i = prim_offset + prims_num - 1; i >= prim_offset; i--) {
const int prim = pbvh.prim_indices_[i];
const float3 center = math::midpoint(prim_bounds[prim].min, prim_bounds[prim].max);
math::min_max(center, cb_backing.min, cb_backing.max);
}
cb = &cb_backing;
}
const int axis = math::dominant_axis(cb->max - cb->min);
/* Partition primitives along that axis */
end = partition_prim_indices(pbvh.prim_indices_,
prim_scratch,
prim_offset,
prim_offset + prims_num,
axis,
math::midpoint(cb->min[axis], cb->max[axis]),
prim_bounds,
prim_to_face_map);
}
else {
/* Partition primitives by material */
end = partition_indices_material_faces(pbvh.prim_indices_,
prim_to_face_map,
material_indices,
sharp_faces,
prim_offset,
prim_offset + prims_num - 1);
}
/* Build children */
build_nodes_recursive_grids(pbvh,
material_indices,
sharp_faces,
leaf_limit,
pbvh.nodes_[node_index].children_offset_,
nullptr,
prim_bounds,
prim_offset,
end - prim_offset,
prim_scratch,
depth + 1);
build_nodes_recursive_grids(pbvh,
material_indices,
sharp_faces,
leaf_limit,
pbvh.nodes_[node_index].children_offset_ + 1,
nullptr,
prim_bounds,
end,
prim_offset + prims_num - end,
prim_scratch,
depth + 1);
}
std::unique_ptr<Tree> build_grids(Mesh *mesh, SubdivCCG *subdiv_ccg)
{
std::unique_ptr<Tree> pbvh = std::make_unique<Tree>(Type::Grids);
pbvh->subdiv_ccg_ = subdiv_ccg;
/* Find maximum number of grids per face. */
int max_grids = 1;
const OffsetIndices faces = mesh->faces();
for (const int i : faces.index_range()) {
max_grids = max_ii(max_grids, faces[i].size());
}
const CCGKey key = BKE_subdiv_ccg_key_top_level(*subdiv_ccg);
const Span<CCGElem *> grids = subdiv_ccg->grids;
/* Ensure leaf limit is at least 4 so there's room
* to split at original face boundaries.
* Fixes #102209.
*/
const int leaf_limit = max_ii(LEAF_LIMIT / (key.grid_area), max_grids);
/* We also need the base mesh for Tree draw. */
pbvh->mesh_ = mesh;
/* For each grid, store the AABB and the AABB centroid */
Array<Bounds<float3>> prim_bounds(grids.size());
const Bounds<float3> cb = threading::parallel_reduce(
grids.index_range(),
1024,
negative_bounds(),
[&](const IndexRange range, const Bounds<float3> &init) {
Bounds<float3> current = init;
for (const int i : range) {
CCGElem *grid = grids[i];
prim_bounds[i] = negative_bounds();
for (const int j : IndexRange(key.grid_area)) {
const float3 &position = CCG_elem_offset_co(key, grid, j);
math::min_max(position, prim_bounds[i].min, prim_bounds[i].max);
}
const float3 center = math::midpoint(prim_bounds[i].min, prim_bounds[i].max);
math::min_max(center, current.min, current.max);
}
return current;
},
[](const Bounds<float3> &a, const Bounds<float3> &b) { return bounds::merge(a, b); });
if (!grids.is_empty()) {
const AttributeAccessor attributes = mesh->attributes();
const VArraySpan material_index = *attributes.lookup<int>("material_index", AttrDomain::Face);
const VArraySpan sharp_face = *attributes.lookup<bool>("sharp_face", AttrDomain::Face);
pbvh->prim_indices_.reinitialize(grids.size());
array_utils::fill_index_range<int>(pbvh->prim_indices_);
pbvh->nodes_.resize(1);
build_nodes_recursive_grids(*pbvh,
material_index,
sharp_face,
leaf_limit,
0,
&cb,
prim_bounds,
0,
grids.size(),
Array<int>(pbvh->prim_indices_.size()),
0);
update_bounds(*pbvh);
store_bounds_orig(*pbvh);
const BitGroupVector<> &grid_hidden = subdiv_ccg->grid_hidden;
if (!grid_hidden.is_empty()) {
MutableSpan<Node> nodes = pbvh->nodes_;
threading::parallel_for(nodes.index_range(), 8, [&](const IndexRange range) {
for (const int i : range) {
const Span<int> grids = node_grid_indices(nodes[i]);
if (std::all_of(grids.begin(), grids.end(), [&](const int i) {
return !bits::any_bit_unset(grid_hidden[i]);
}))
{
nodes[i].flag_ |= PBVH_FullyHidden;
}
}
});
}
}
return pbvh;
}
Tree::~Tree()
{
for (Node &node : this->nodes_) {
if (node.flag_ & PBVH_Leaf) {
if (node.draw_batches_) {
blender::draw::pbvh::node_free(node.draw_batches_);
}
}
if (node.flag_ & (PBVH_Leaf | PBVH_TexLeaf)) {
node_pixels_free(&node);
}
}
pixels_free(this);
}
void free(std::unique_ptr<Tree> &pbvh)
{
pbvh.reset();
}
static void pbvh_iter_begin(PBVHIter *iter, Tree &pbvh, FunctionRef<bool(Node &)> scb)
{
iter->pbvh = &pbvh;
iter->scb = scb;
iter->stack = iter->stackfixed;
iter->stackspace = STACK_FIXED_DEPTH;
iter->stack[0].node = &pbvh.nodes_.first();
iter->stack[0].revisiting = false;
iter->stacksize = 1;
}
static void pbvh_iter_end(PBVHIter *iter)
{
if (iter->stackspace > STACK_FIXED_DEPTH) {
MEM_freeN(iter->stack);
}
}
static void pbvh_stack_push(PBVHIter *iter, Node *node, bool revisiting)
{
if (UNLIKELY(iter->stacksize == iter->stackspace)) {
iter->stackspace *= 2;
if (iter->stackspace != (STACK_FIXED_DEPTH * 2)) {
iter->stack = static_cast<PBVHStack *>(
MEM_reallocN(iter->stack, sizeof(PBVHStack) * iter->stackspace));
}
else {
iter->stack = static_cast<PBVHStack *>(
MEM_mallocN(sizeof(PBVHStack) * iter->stackspace, "PBVHStack"));
memcpy(iter->stack, iter->stackfixed, sizeof(PBVHStack) * iter->stacksize);
}
}
iter->stack[iter->stacksize].node = node;
iter->stack[iter->stacksize].revisiting = revisiting;
iter->stacksize++;
}
static Node *pbvh_iter_next(PBVHIter *iter, PBVHNodeFlags leaf_flag)
{
/* purpose here is to traverse tree, visiting child nodes before their
* parents, this order is necessary for e.g. computing bounding boxes */
while (iter->stacksize) {
/* pop node */
iter->stacksize--;
Node *node = iter->stack[iter->stacksize].node;
/* on a mesh with no faces this can happen
* can remove this check if we know meshes have at least 1 face */
if (node == nullptr) {
return nullptr;
}
bool revisiting = iter->stack[iter->stacksize].revisiting;
/* revisiting node already checked */
if (revisiting) {
return node;
}
if (iter->scb && !iter->scb(*node)) {
continue; /* don't traverse, outside of search zone */
}
if (node->flag_ & leaf_flag) {
/* immediately hit leaf node */
return node;
}
/* come back later when children are done */
pbvh_stack_push(iter, node, true);
/* push two child nodes on the stack */
pbvh_stack_push(iter, &iter->pbvh->nodes_[node->children_offset_ + 1], false);
pbvh_stack_push(iter, &iter->pbvh->nodes_[node->children_offset_], false);
}
return nullptr;
}
static Node *pbvh_iter_next_occluded(PBVHIter *iter)
{
while (iter->stacksize) {
/* pop node */
iter->stacksize--;
Node *node = iter->stack[iter->stacksize].node;
/* on a mesh with no faces this can happen
* can remove this check if we know meshes have at least 1 face */
if (node == nullptr) {
return nullptr;
}
if (iter->scb && !iter->scb(*node)) {
continue; /* don't traverse, outside of search zone */
}
if (node->flag_ & PBVH_Leaf) {
/* immediately hit leaf node */
return node;
}
pbvh_stack_push(iter, &iter->pbvh->nodes_[node->children_offset_ + 1], false);
pbvh_stack_push(iter, &iter->pbvh->nodes_[node->children_offset_], false);
}
return nullptr;
}
struct node_tree {
Node *data;
node_tree *left;
node_tree *right;
};
static void node_tree_insert(node_tree *tree, node_tree *new_node)
{
if (new_node->data->tmin_ < tree->data->tmin_) {
if (tree->left) {
node_tree_insert(tree->left, new_node);
}
else {
tree->left = new_node;
}
}
else {
if (tree->right) {
node_tree_insert(tree->right, new_node);
}
else {
tree->right = new_node;
}
}
}
static void traverse_tree(node_tree *tree,
const FunctionRef<void(Node &node, float *tmin)> hit_fn,
float *tmin)
{
if (tree->left) {
traverse_tree(tree->left, hit_fn, tmin);
}
hit_fn(*tree->data, tmin);
if (tree->right) {
traverse_tree(tree->right, hit_fn, tmin);
}
}
static void free_tree(node_tree *tree)
{
if (tree->left) {
free_tree(tree->left);
tree->left = nullptr;
}
if (tree->right) {
free_tree(tree->right);
tree->right = nullptr;
}
::free(tree);
}
} // namespace blender::bke::pbvh
float BKE_pbvh_node_get_tmin(const blender::bke::pbvh::Node *node)
{
return node->tmin_;
}
namespace blender::bke::pbvh {
void search_callback(Tree &pbvh,
FunctionRef<bool(Node &)> filter_fn,
FunctionRef<void(Node &)> hit_fn)
{
if (pbvh.nodes_.is_empty()) {
return;
}
PBVHIter iter;
Node *node;
pbvh_iter_begin(&iter, pbvh, filter_fn);
while ((node = pbvh_iter_next(&iter, PBVH_Leaf))) {
if (node->flag_ & PBVH_Leaf) {
hit_fn(*node);
}
}
pbvh_iter_end(&iter);
}
static void search_callback_occluded(Tree &pbvh,
const FunctionRef<bool(Node &)> scb,
const FunctionRef<void(Node &node, float *tmin)> hit_fn)
{
if (pbvh.nodes_.is_empty()) {
return;
}
PBVHIter iter;
Node *node;
node_tree *tree = nullptr;
pbvh_iter_begin(&iter, pbvh, scb);
while ((node = pbvh_iter_next_occluded(&iter))) {
if (node->flag_ & PBVH_Leaf) {
node_tree *new_node = static_cast<node_tree *>(malloc(sizeof(node_tree)));
new_node->data = node;
new_node->left = nullptr;
new_node->right = nullptr;
if (tree) {
node_tree_insert(tree, new_node);
}
else {
tree = new_node;
}
}
}
pbvh_iter_end(&iter);
if (tree) {
float tmin = FLT_MAX;
traverse_tree(tree, hit_fn, &tmin);
free_tree(tree);
}
}
static bool update_search(Node *node, const int flag)
{
if (node->flag_ & PBVH_Leaf) {
return (node->flag_ & flag) != 0;
}
return true;
}
static void normals_calc_faces(const Span<float3> positions,
const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Span<int> face_indices,
MutableSpan<float3> face_normals)
{
for (const int i : face_indices) {
face_normals[i] = mesh::face_normal_calc(positions, corner_verts.slice(faces[i]));
}
}
static void calc_boundary_face_normals(const Span<float3> positions,
const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Span<int> face_indices,
MutableSpan<float3> face_normals)
{
threading::parallel_for(face_indices.index_range(), 512, [&](const IndexRange range) {
normals_calc_faces(positions, faces, corner_verts, face_indices.slice(range), face_normals);
});
}
static void calc_node_face_normals(const Span<float3> positions,
const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Span<int> corner_tri_faces,
const Span<const Node *> nodes,
MutableSpan<float3> face_normals)
{
threading::EnumerableThreadSpecific<Vector<int>> all_index_data;
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
Vector<int> &node_faces = all_index_data.local();
for (const Node *node : nodes.slice(range)) {
normals_calc_faces(positions,
faces,
corner_verts,
node_face_indices_calc_mesh(corner_tri_faces, *node, node_faces),
face_normals);
}
});
}
static void normals_calc_verts_simple(const GroupedSpan<int> vert_to_face_map,
const Span<float3> face_normals,
const Span<int> verts,
MutableSpan<float3> vert_normals)
{
for (const int vert : verts) {
float3 normal(0.0f);
for (const int face : vert_to_face_map[vert]) {
normal += face_normals[face];
}
vert_normals[vert] = math::normalize(normal);
}
}
static void calc_boundary_vert_normals(const GroupedSpan<int> vert_to_face_map,
const Span<float3> face_normals,
const Span<int> verts,
MutableSpan<float3> vert_normals)
{
threading::parallel_for(verts.index_range(), 1024, [&](const IndexRange range) {
normals_calc_verts_simple(vert_to_face_map, face_normals, verts.slice(range), vert_normals);
});
}
static void calc_node_vert_normals(const GroupedSpan<int> vert_to_face_map,
const Span<float3> face_normals,
const Span<Node *> nodes,
MutableSpan<float3> vert_normals)
{
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const Node *node : nodes.slice(range)) {
normals_calc_verts_simple(
vert_to_face_map, face_normals, node_unique_verts(*node), vert_normals);
}
});
}
static void update_normals_faces(Tree &pbvh, Span<Node *> nodes, Mesh &mesh)
{
/* Position changes are tracked on a per-node level, so all the vertex and face normals for every
* affected node are recalculated. However, the additional complexity comes from the fact that
* changing vertex normals also changes surrounding face normals. Those changed face normals then
* change the normals of all connected vertices, which can be in other nodes. So the set of
* vertices that need recalculated normals can propagate into unchanged/untagged Tree nodes.
*
* Currently we have no good way of finding neighboring Tree nodes, so we use the vertex to
* face topology map to find the neighboring vertices that need normal recalculation.
*
* Those boundary face and vertex indices are deduplicated with #VectorSet in order to avoid
* duplicate work recalculation for the same vertex, and to make parallel storage for vertices
* during recalculation thread-safe. */
const Span<float3> positions = pbvh.vert_positions_;
const OffsetIndices faces = mesh.faces();
const Span<int> corner_verts = mesh.corner_verts();
const Span<int> tri_faces = mesh.corner_tri_faces();
const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
VectorSet<int> boundary_faces;
for (const Node *node : nodes) {
for (const int vert : node->vert_indices_.as_span().drop_front(node->unique_verts_num_)) {
boundary_faces.add_multiple(vert_to_face_map[vert]);
}
}
/* In certain cases when undoing strokes on a duplicate object, the cached data may be marked
* dirty before this code is run, leaving the relevant vectors empty. We force reinitialize the
* vectors to prevent crashes here.
* See #125375 for more detail. */
if (!pbvh.deformed_) {
if (mesh.runtime->face_normals_cache.is_dirty()) {
mesh.face_normals();
}
if (mesh.runtime->vert_normals_cache.is_dirty()) {
mesh.vert_normals();
}
}
VectorSet<int> boundary_verts;
threading::parallel_invoke(
[&]() {
if (pbvh.deformed_) {
calc_node_face_normals(
positions, faces, corner_verts, tri_faces, nodes, pbvh.face_normals_deformed_);
calc_boundary_face_normals(
positions, faces, corner_verts, boundary_faces, pbvh.face_normals_deformed_);
}
else {
mesh.runtime->face_normals_cache.update([&](Vector<float3> &r_data) {
calc_node_face_normals(positions, faces, corner_verts, tri_faces, nodes, r_data);
calc_boundary_face_normals(positions, faces, corner_verts, boundary_faces, r_data);
});
/* #SharedCache::update() reallocates cached vectors if they were shared initially. */
pbvh.face_normals_ = mesh.runtime->face_normals_cache.data();
}
},
[&]() {
/* Update all normals connected to affected faces, even if not explicitly tagged. */
boundary_verts.reserve(boundary_faces.size());
for (const int face : boundary_faces) {
boundary_verts.add_multiple(corner_verts.slice(faces[face]));
}
});
if (pbvh.deformed_) {
calc_node_vert_normals(
vert_to_face_map, pbvh.face_normals_, nodes, pbvh.vert_normals_deformed_);
calc_boundary_vert_normals(
vert_to_face_map, pbvh.face_normals_, boundary_verts, pbvh.vert_normals_deformed_);
}
else {
mesh.runtime->vert_normals_cache.update([&](Vector<float3> &r_data) {
calc_node_vert_normals(vert_to_face_map, pbvh.face_normals_, nodes, r_data);
calc_boundary_vert_normals(vert_to_face_map, pbvh.face_normals_, boundary_verts, r_data);
});
pbvh.vert_normals_ = mesh.runtime->vert_normals_cache.data();
}
for (Node *node : nodes) {
node->flag_ &= ~PBVH_UpdateNormals;
}
}
void update_normals(Tree &pbvh, SubdivCCG *subdiv_ccg)
{
Vector<Node *> nodes = search_gather(
pbvh, [&](Node &node) { return update_search(&node, PBVH_UpdateNormals); });
if (nodes.is_empty()) {
return;
}
if (pbvh.type() == Type::BMesh) {
bmesh_normals_update(nodes);
}
else if (pbvh.type() == Type::Mesh) {
update_normals_faces(pbvh, nodes, *pbvh.mesh_);
}
else if (pbvh.type() == Type::Grids) {
IndexMaskMemory memory;
const IndexMask faces_to_update = nodes_to_face_selection_grids(*subdiv_ccg, nodes, memory);
BKE_subdiv_ccg_update_normals(*subdiv_ccg, faces_to_update);
for (Node *node : nodes) {
node->flag_ &= ~PBVH_UpdateNormals;
}
}
}
void update_node_bounds_mesh(const Span<float3> positions, Node &node)
{
Bounds<float3> bounds = negative_bounds();
for (const int vert : node_verts(node)) {
math::min_max(positions[vert], bounds.min, bounds.max);
}
node.bounds_ = bounds;
}
void update_node_bounds_grids(const CCGKey &key, const Span<CCGElem *> grids, Node &node)
{
Bounds<float3> bounds = negative_bounds();
for (const int grid : node.prim_indices_) {
for (const int i : IndexRange(key.grid_area)) {
math::min_max(CCG_elem_offset_co(key, grids[grid], i), bounds.min, bounds.max);
}
}
node.bounds_ = bounds;
}
void update_node_bounds_bmesh(Node &node)
{
Bounds<float3> bounds = negative_bounds();
for (const BMVert *vert : node.bm_unique_verts_) {
math::min_max(float3(vert->co), bounds.min, bounds.max);
}
for (const BMVert *vert : node.bm_other_verts_) {
math::min_max(float3(vert->co), bounds.min, bounds.max);
}
node.bounds_ = bounds;
}
static bool update_leaf_node_bounds(Tree &pbvh)
{
Vector<Node *> nodes = search_gather(
pbvh, [&](Node &node) { return update_search(&node, PBVH_UpdateBB); });
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (Node *node : nodes.as_span().slice(range)) {
switch (pbvh.type()) {
case Type::Mesh:
update_node_bounds_mesh(pbvh.vert_positions_, *node);
break;
case Type::Grids:
update_node_bounds_grids(
BKE_subdiv_ccg_key_top_level(*pbvh.subdiv_ccg_), pbvh.subdiv_ccg_->grids, *node);
break;
case Type::BMesh:
update_node_bounds_bmesh(*node);
break;
}
}
});
return !nodes.is_empty();
}
struct BoundsMergeInfo {
Bounds<float3> bounds;
bool update;
};
static BoundsMergeInfo merge_child_bounds(MutableSpan<Node> nodes, const int node_index)
{
Node &node = nodes[node_index];
if (node.flag_ & PBVH_Leaf) {
const bool update = node.flag_ & PBVH_UpdateBB;
node.flag_ &= ~PBVH_UpdateBB;
return {node.bounds_, update};
}
const BoundsMergeInfo info_0 = merge_child_bounds(nodes, node.children_offset_ + 0);
const BoundsMergeInfo info_1 = merge_child_bounds(nodes, node.children_offset_ + 1);
const bool update = info_0.update || info_1.update;
if (update) {
node.bounds_ = bounds::merge(info_0.bounds, info_1.bounds);
}
node.flag_ &= ~PBVH_UpdateBB;
return {node.bounds_, update};
}
static void flush_bounds_to_parents(Tree &pbvh)
{
pbvh.nodes_.first().bounds_ = merge_child_bounds(pbvh.nodes_, 0).bounds;
}
void update_bounds(Tree &pbvh)
{
if (update_leaf_node_bounds(pbvh)) {
flush_bounds_to_parents(pbvh);
}
}
void store_bounds_orig(Tree &pbvh)
{
MutableSpan<Node> nodes = pbvh.nodes_;
threading::parallel_for(nodes.index_range(), 256, [&](const IndexRange range) {
for (const int i : range) {
nodes[i].bounds_orig_ = nodes[i].bounds_;
}
});
}
void node_update_mask_mesh(const Span<float> mask, Node &node)
{
const Span<int> verts = node_verts(node);
const bool fully_masked = std::all_of(
verts.begin(), verts.end(), [&](const int vert) { return mask[vert] == 1.0f; });
const bool fully_unmasked = std::all_of(
verts.begin(), verts.end(), [&](const int vert) { return mask[vert] <= 0.0f; });
SET_FLAG_FROM_TEST(node.flag_, fully_masked, PBVH_FullyMasked);
SET_FLAG_FROM_TEST(node.flag_, fully_unmasked, PBVH_FullyUnmasked);
node.flag_ &= ~PBVH_UpdateMask;
}
static void update_mask_mesh(const Mesh &mesh, const Span<Node *> nodes)
{
const AttributeAccessor attributes = mesh.attributes();
const VArraySpan<float> mask = *attributes.lookup<float>(".sculpt_mask", AttrDomain::Point);
if (mask.is_empty()) {
for (Node *node : nodes) {
node->flag_ &= ~PBVH_FullyMasked;
node->flag_ |= PBVH_FullyUnmasked;
node->flag_ &= ~PBVH_UpdateMask;
}
return;
}
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (Node *node : nodes.slice(range)) {
node_update_mask_mesh(mask, *node);
}
});
}
void node_update_mask_grids(const CCGKey &key, const Span<CCGElem *> grids, Node &node)
{
BLI_assert(key.has_mask);
bool fully_masked = true;
bool fully_unmasked = true;
for (const int grid : node.prim_indices_) {
CCGElem *elem = grids[grid];
for (const int i : IndexRange(key.grid_area)) {
const float mask = CCG_elem_offset_mask(key, elem, i);
fully_masked &= mask == 1.0f;
fully_unmasked &= mask <= 0.0f;
}
}
SET_FLAG_FROM_TEST(node.flag_, fully_masked, PBVH_FullyMasked);
SET_FLAG_FROM_TEST(node.flag_, fully_unmasked, PBVH_FullyUnmasked);
node.flag_ &= ~PBVH_UpdateMask;
}
static void update_mask_grids(const SubdivCCG &subdiv_ccg, const Span<Node *> nodes)
{
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
if (!key.has_mask) {
for (Node *node : nodes) {
node->flag_ &= ~PBVH_FullyMasked;
node->flag_ |= PBVH_FullyUnmasked;
node->flag_ &= ~PBVH_UpdateMask;
}
return;
}
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (Node *node : nodes.slice(range)) {
node_update_mask_grids(key, subdiv_ccg.grids, *node);
}
});
}
void node_update_mask_bmesh(const int mask_offset, Node &node)
{
BLI_assert(mask_offset != -1);
bool fully_masked = true;
bool fully_unmasked = true;
for (const BMVert *vert : node.bm_unique_verts_) {
fully_masked &= BM_ELEM_CD_GET_FLOAT(vert, mask_offset) == 1.0f;
fully_unmasked &= BM_ELEM_CD_GET_FLOAT(vert, mask_offset) <= 0.0f;
}
for (const BMVert *vert : node.bm_other_verts_) {
fully_masked &= BM_ELEM_CD_GET_FLOAT(vert, mask_offset) == 1.0f;
fully_unmasked &= BM_ELEM_CD_GET_FLOAT(vert, mask_offset) <= 0.0f;
}
SET_FLAG_FROM_TEST(node.flag_, fully_masked, PBVH_FullyMasked);
SET_FLAG_FROM_TEST(node.flag_, fully_unmasked, PBVH_FullyUnmasked);
node.flag_ &= ~PBVH_UpdateMask;
}
static void update_mask_bmesh(const BMesh &bm, const Span<Node *> nodes)
{
const int offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
if (offset == -1) {
for (Node *node : nodes) {
node->flag_ &= ~PBVH_FullyMasked;
node->flag_ |= PBVH_FullyUnmasked;
node->flag_ &= ~PBVH_UpdateMask;
}
return;
}
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (Node *node : nodes.slice(range)) {
node_update_mask_bmesh(offset, *node);
}
});
}
void update_mask(Tree &pbvh)
{
Vector<Node *> nodes = search_gather(
pbvh, [&](Node &node) { return update_search(&node, PBVH_UpdateMask); });
switch (pbvh.type()) {
case Type::Mesh:
update_mask_mesh(*pbvh.mesh_, nodes);
break;
case Type::Grids:
update_mask_grids(*pbvh.subdiv_ccg_, nodes);
break;
case Type::BMesh:
update_mask_bmesh(*pbvh.bm_, nodes);
break;
}
}
void node_update_visibility_mesh(const Span<bool> hide_vert, Node &node)
{
BLI_assert(!hide_vert.is_empty());
const Span<int> verts = node_verts(node);
const bool fully_hidden = std::all_of(
verts.begin(), verts.end(), [&](const int vert) { return hide_vert[vert]; });
SET_FLAG_FROM_TEST(node.flag_, fully_hidden, PBVH_FullyHidden);
node.flag_ &= ~PBVH_UpdateVisibility;
}
static void update_visibility_faces(const Mesh &mesh, const Span<Node *> nodes)
{
const AttributeAccessor attributes = mesh.attributes();
const VArraySpan<bool> hide_vert = *attributes.lookup<bool>(".hide_vert", AttrDomain::Point);
if (hide_vert.is_empty()) {
for (Node *node : nodes) {
node->flag_ &= ~PBVH_FullyHidden;
node->flag_ &= ~PBVH_UpdateVisibility;
}
return;
}
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (Node *node : nodes.slice(range)) {
node_update_visibility_mesh(hide_vert, *node);
}
});
}
void node_update_visibility_grids(const BitGroupVector<> &grid_hidden, Node &node)
{
BLI_assert(!grid_hidden.is_empty());
const bool fully_hidden = std::none_of(
node.prim_indices_.begin(), node.prim_indices_.end(), [&](const int grid) {
return bits::any_bit_unset(grid_hidden[grid]);
});
SET_FLAG_FROM_TEST(node.flag_, fully_hidden, PBVH_FullyHidden);
node.flag_ &= ~PBVH_UpdateVisibility;
}
static void update_visibility_grids(Tree &pbvh, const Span<Node *> nodes)
{
const BitGroupVector<> &grid_hidden = pbvh.subdiv_ccg_->grid_hidden;
if (grid_hidden.is_empty()) {
for (Node *node : nodes) {
node->flag_ &= ~PBVH_FullyHidden;
node->flag_ &= ~PBVH_UpdateVisibility;
}
return;
}
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (Node *node : nodes.slice(range)) {
node_update_visibility_grids(grid_hidden, *node);
}
});
}
void node_update_visibility_bmesh(Node &node)
{
const bool unique_hidden = std::all_of(
node.bm_unique_verts_.begin(), node.bm_unique_verts_.end(), [&](const BMVert *vert) {
return BM_elem_flag_test(vert, BM_ELEM_HIDDEN);
});
const bool other_hidden = std::all_of(
node.bm_other_verts_.begin(), node.bm_other_verts_.end(), [&](const BMVert *vert) {
return BM_elem_flag_test(vert, BM_ELEM_HIDDEN);
});
SET_FLAG_FROM_TEST(node.flag_, unique_hidden && other_hidden, PBVH_FullyHidden);
node.flag_ &= ~PBVH_UpdateVisibility;
}
static void update_visibility_bmesh(const Span<Node *> nodes)
{
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (Node *node : nodes.slice(range)) {
node_update_visibility_bmesh(*node);
}
});
}
void update_visibility(Tree &pbvh)
{
Vector<Node *> nodes = search_gather(
pbvh, [&](Node &node) { return update_search(&node, PBVH_UpdateVisibility); });
switch (pbvh.type()) {
case Type::Mesh:
update_visibility_faces(*pbvh.mesh_, nodes);
break;
case Type::Grids:
update_visibility_grids(pbvh, nodes);
break;
case Type::BMesh:
update_visibility_bmesh(nodes);
break;
}
}
int count_grid_quads(const BitGroupVector<> &grid_hidden,
const Span<int> grid_indices,
int gridsize,
int display_gridsize)
{
const int gridarea = (gridsize - 1) * (gridsize - 1);
if (grid_hidden.is_empty()) {
return gridarea * grid_indices.size();
}
/* grid hidden layer is present, so have to check each grid for
* visibility */
int depth1 = int(log2(double(gridsize) - 1.0) + DBL_EPSILON);
int depth2 = int(log2(double(display_gridsize) - 1.0) + DBL_EPSILON);
int skip = depth2 < depth1 ? 1 << (depth1 - depth2 - 1) : 1;
int totquad = 0;
for (const int grid : grid_indices) {
const blender::BoundedBitSpan gh = grid_hidden[grid];
/* 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++;
}
}
}
}
return totquad;
}
} // namespace blender::bke::pbvh
blender::Bounds<blender::float3> BKE_pbvh_redraw_BB(blender::bke::pbvh::Tree &pbvh)
{
using namespace blender;
using namespace blender::bke::pbvh;
if (pbvh.nodes_.is_empty()) {
return {};
}
Bounds<float3> bounds = negative_bounds();
PBVHIter iter;
pbvh_iter_begin(&iter, pbvh, {});
Node *node;
while ((node = pbvh_iter_next(&iter, PBVH_Leaf))) {
if (node->flag_ & PBVH_UpdateRedraw) {
bounds = bounds::merge(bounds, node->bounds_);
}
}
pbvh_iter_end(&iter);
return bounds;
}
namespace blender::bke::pbvh {
IndexMask nodes_to_face_selection_grids(const SubdivCCG &subdiv_ccg,
const Span<const Node *> nodes,
IndexMaskMemory &memory)
{
const Span<int> grid_to_face_map = subdiv_ccg.grid_to_face_map;
/* Using a #VectorSet for index deduplication would also work, but the performance gets much
* worse with large selections since the loop would be single-threaded. A boolean array has an
* overhead regardless of selection size, but that is small. */
Array<bool> faces_to_update(subdiv_ccg.faces.size(), false);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const Node *node : nodes.slice(range)) {
for (const int grid : node->prim_indices_) {
faces_to_update[grid_to_face_map[grid]] = true;
}
}
});
return IndexMask::from_bools(faces_to_update, memory);
}
Bounds<float3> bounds_get(const Tree &pbvh)
{
if (pbvh.nodes_.is_empty()) {
return float3(0);
}
return pbvh.nodes_.first().bounds_;
}
} // namespace blender::bke::pbvh
int BKE_pbvh_get_grid_num_verts(const blender::bke::pbvh::Tree &pbvh)
{
BLI_assert(pbvh.type() == blender::bke::pbvh::Type::Grids);
const CCGKey key = BKE_subdiv_ccg_key_top_level(*pbvh.subdiv_ccg_);
return pbvh.subdiv_ccg_->grids.size() * key.grid_area;
}
int BKE_pbvh_get_grid_num_faces(const blender::bke::pbvh::Tree &pbvh)
{
BLI_assert(pbvh.type() == blender::bke::pbvh::Type::Grids);
const CCGKey key = BKE_subdiv_ccg_key_top_level(*pbvh.subdiv_ccg_);
return pbvh.subdiv_ccg_->grids.size() * square_i(key.grid_size - 1);
}
/***************************** Node Access ***********************************/
void BKE_pbvh_node_mark_update(blender::bke::pbvh::Node *node)
{
node->flag_ |= PBVH_UpdateNormals | PBVH_UpdateBB | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw |
PBVH_RebuildPixels;
}
void BKE_pbvh_node_mark_update_mask(blender::bke::pbvh::Node *node)
{
node->flag_ |= PBVH_UpdateMask | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
}
void BKE_pbvh_node_mark_update_color(blender::bke::pbvh::Node *node)
{
node->flag_ |= PBVH_UpdateColor | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
}
void BKE_pbvh_node_mark_update_face_sets(blender::bke::pbvh::Node *node)
{
node->flag_ |= PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
}
void BKE_pbvh_mark_rebuild_pixels(blender::bke::pbvh::Tree &pbvh)
{
for (blender::bke::pbvh::Node &node : pbvh.nodes_) {
if (node.flag_ & PBVH_Leaf) {
node.flag_ |= PBVH_RebuildPixels;
}
}
}
void BKE_pbvh_node_mark_update_visibility(blender::bke::pbvh::Node *node)
{
node->flag_ |= PBVH_UpdateVisibility | PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers |
PBVH_UpdateRedraw;
}
void BKE_pbvh_node_mark_rebuild_draw(blender::bke::pbvh::Node *node)
{
node->flag_ |= PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
}
void BKE_pbvh_node_mark_redraw(blender::bke::pbvh::Node *node)
{
node->flag_ |= PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
}
void BKE_pbvh_node_mark_positions_update(blender::bke::pbvh::Node *node)
{
node->flag_ |= PBVH_UpdateNormals | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw | PBVH_UpdateBB;
}
void BKE_pbvh_node_fully_hidden_set(blender::bke::pbvh::Node *node, int fully_hidden)
{
BLI_assert(node->flag_ & PBVH_Leaf);
if (fully_hidden) {
node->flag_ |= PBVH_FullyHidden;
}
else {
node->flag_ &= ~PBVH_FullyHidden;
}
}
bool BKE_pbvh_node_fully_hidden_get(const blender::bke::pbvh::Node *node)
{
return (node->flag_ & PBVH_Leaf) && (node->flag_ & PBVH_FullyHidden);
}
void BKE_pbvh_node_fully_masked_set(blender::bke::pbvh::Node *node, int fully_masked)
{
BLI_assert(node->flag_ & PBVH_Leaf);
if (fully_masked) {
node->flag_ |= PBVH_FullyMasked;
}
else {
node->flag_ &= ~PBVH_FullyMasked;
}
}
bool BKE_pbvh_node_fully_masked_get(const blender::bke::pbvh::Node *node)
{
return (node->flag_ & PBVH_Leaf) && (node->flag_ & PBVH_FullyMasked);
}
void BKE_pbvh_node_fully_unmasked_set(blender::bke::pbvh::Node *node, int fully_masked)
{
BLI_assert(node->flag_ & PBVH_Leaf);
if (fully_masked) {
node->flag_ |= PBVH_FullyUnmasked;
}
else {
node->flag_ &= ~PBVH_FullyUnmasked;
}
}
bool BKE_pbvh_node_fully_unmasked_get(const blender::bke::pbvh::Node *node)
{
return (node->flag_ & PBVH_Leaf) && (node->flag_ & PBVH_FullyUnmasked);
}
namespace blender::bke::pbvh {
Span<int> node_corners(const Node &node)
{
return node.corner_indices_;
}
Span<int> node_verts(const Node &node)
{
return node.vert_indices_;
}
Span<int> node_unique_verts(const Node &node)
{
return node.vert_indices_.as_span().take_front(node.unique_verts_num_);
}
Span<int> node_face_indices_calc_mesh(const Span<int> corner_tri_faces,
const Node &node,
Vector<int> &faces)
{
faces.clear();
int prev_face = -1;
for (const int tri : node.prim_indices_) {
const int face = corner_tri_faces[tri];
if (face != prev_face) {
faces.append(face);
prev_face = face;
}
}
return faces.as_span();
}
Span<int> node_face_indices_calc_grids(const Tree &pbvh, const Node &node, Vector<int> &faces)
{
faces.clear();
const Span<int> grid_to_face_map = pbvh.subdiv_ccg_->grid_to_face_map;
int prev_face = -1;
for (const int prim : node.prim_indices_) {
const int face = grid_to_face_map[prim];
if (face != prev_face) {
faces.append(face);
prev_face = face;
}
}
return faces.as_span();
}
Span<int> node_grid_indices(const Node &node)
{
return node.prim_indices_;
}
} // namespace blender::bke::pbvh
namespace blender::bke::pbvh {
Bounds<float3> node_bounds(const Node &node)
{
return node.bounds_;
}
} // namespace blender::bke::pbvh
blender::Bounds<blender::float3> BKE_pbvh_node_get_original_BB(
const blender::bke::pbvh::Node *node)
{
return node->bounds_orig_;
}
void BKE_pbvh_node_get_bm_orco_data(blender::bke::pbvh::Node *node,
int (**r_orco_tris)[3],
int *r_orco_tris_num,
float (**r_orco_coords)[3],
BMVert ***r_orco_verts)
{
*r_orco_tris = node->bm_ortri_;
*r_orco_tris_num = node->bm_tot_ortri_;
*r_orco_coords = node->bm_orco_;
if (r_orco_verts) {
*r_orco_verts = node->bm_orvert_;
}
}
/********************************* Ray-cast ***********************************/
namespace blender::bke::pbvh {
struct RaycastData {
IsectRayAABB_Precalc ray;
bool original;
};
static bool ray_aabb_intersect(Node &node, const RaycastData &rcd)
{
if (rcd.original) {
return isect_ray_aabb_v3(&rcd.ray, node.bounds_orig_.min, node.bounds_orig_.max, &node.tmin_);
}
return isect_ray_aabb_v3(&rcd.ray, node.bounds_.min, node.bounds_.max, &node.tmin_);
}
void raycast(Tree &pbvh,
const FunctionRef<void(Node &node, float *tmin)> hit_fn,
const float ray_start[3],
const float ray_normal[3],
bool original)
{
RaycastData rcd;
isect_ray_aabb_v3_precalc(&rcd.ray, ray_start, ray_normal);
rcd.original = original;
search_callback_occluded(
pbvh, [&](Node &node) { return ray_aabb_intersect(node, rcd); }, hit_fn);
}
bool ray_face_intersection_quad(const float ray_start[3],
IsectRayPrecalc *isect_precalc,
const float t0[3],
const float t1[3],
const float t2[3],
const float t3[3],
float *depth)
{
float depth_test;
if ((isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, nullptr) &&
(depth_test < *depth)) ||
(isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t2, t3, &depth_test, nullptr) &&
(depth_test < *depth)))
{
*depth = depth_test;
return true;
}
return false;
}
bool ray_face_intersection_tri(const float ray_start[3],
IsectRayPrecalc *isect_precalc,
const float t0[3],
const float t1[3],
const float t2[3],
float *depth)
{
float depth_test;
if (isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, nullptr) &&
(depth_test < *depth))
{
*depth = depth_test;
return true;
}
return false;
}
/* Take advantage of the fact we know this won't be an intersection.
* Just handle ray-tri edges. */
static float dist_squared_ray_to_tri_v3_fast(const float ray_origin[3],
const float ray_direction[3],
const float v0[3],
const float v1[3],
const float v2[3],
float r_point[3],
float *r_depth)
{
const float *tri[3] = {v0, v1, v2};
float dist_sq_best = FLT_MAX;
for (int i = 0, j = 2; i < 3; j = i++) {
float point_test[3], depth_test = FLT_MAX;
const float dist_sq_test = dist_squared_ray_to_seg_v3(
ray_origin, ray_direction, tri[i], tri[j], point_test, &depth_test);
if (dist_sq_test < dist_sq_best || i == 0) {
copy_v3_v3(r_point, point_test);
*r_depth = depth_test;
dist_sq_best = dist_sq_test;
}
}
return dist_sq_best;
}
bool ray_face_nearest_quad(const float ray_start[3],
const float ray_normal[3],
const float t0[3],
const float t1[3],
const float t2[3],
const float t3[3],
float *depth,
float *dist_sq)
{
float dist_sq_test;
float co[3], depth_test;
if ((dist_sq_test = dist_squared_ray_to_tri_v3_fast(
ray_start, ray_normal, t0, t1, t2, co, &depth_test)) < *dist_sq)
{
*dist_sq = dist_sq_test;
*depth = depth_test;
if ((dist_sq_test = dist_squared_ray_to_tri_v3_fast(
ray_start, ray_normal, t0, t2, t3, co, &depth_test)) < *dist_sq)
{
*dist_sq = dist_sq_test;
*depth = depth_test;
}
return true;
}
return false;
}
bool ray_face_nearest_tri(const float ray_start[3],
const float ray_normal[3],
const float t0[3],
const float t1[3],
const float t2[3],
float *depth,
float *dist_sq)
{
float dist_sq_test;
float co[3], depth_test;
if ((dist_sq_test = dist_squared_ray_to_tri_v3_fast(
ray_start, ray_normal, t0, t1, t2, co, &depth_test)) < *dist_sq)
{
*dist_sq = dist_sq_test;
*depth = depth_test;
return true;
}
return false;
}
static bool pbvh_faces_node_raycast(Tree &pbvh,
const Node &node,
const float (*origco)[3],
const Span<int> corner_verts,
const Span<int3> corner_tris,
const Span<int> corner_tri_faces,
const Span<bool> hide_poly,
const float ray_start[3],
const float ray_normal[3],
IsectRayPrecalc *isect_precalc,
float *depth,
PBVHVertRef *r_active_vertex,
int *r_active_face_index,
float *r_face_normal)
{
using namespace blender;
const Span<float3> positions = pbvh.vert_positions_;
bool hit = false;
float nearest_vertex_co[3] = {0.0f};
for (const int i : node.prim_indices_.index_range()) {
const int tri_i = node.prim_indices_[i];
const int3 &tri = corner_tris[tri_i];
const int3 face_verts = node.face_vert_indices_[i];
if (!hide_poly.is_empty() && hide_poly[corner_tri_faces[tri_i]]) {
continue;
}
const float *co[3];
if (origco) {
/* Intersect with backed up original coordinates. */
co[0] = origco[face_verts[0]];
co[1] = origco[face_verts[1]];
co[2] = origco[face_verts[2]];
}
else {
/* intersect with current coordinates */
co[0] = positions[corner_verts[tri[0]]];
co[1] = positions[corner_verts[tri[1]]];
co[2] = positions[corner_verts[tri[2]]];
}
if (ray_face_intersection_tri(ray_start, isect_precalc, co[0], co[1], co[2], depth)) {
hit = true;
if (r_face_normal) {
normal_tri_v3(r_face_normal, co[0], co[1], co[2]);
}
if (r_active_vertex) {
float location[3] = {0.0f};
madd_v3_v3v3fl(location, ray_start, ray_normal, *depth);
for (int j = 0; j < 3; j++) {
/* Always assign nearest_vertex_co in the first iteration to avoid comparison against
* uninitialized values. This stores the closest vertex in the current intersecting
* triangle. */
if (j == 0 ||
len_squared_v3v3(location, co[j]) < len_squared_v3v3(location, nearest_vertex_co))
{
copy_v3_v3(nearest_vertex_co, co[j]);
r_active_vertex->i = corner_verts[tri[j]];
*r_active_face_index = corner_tri_faces[tri_i];
}
}
}
}
}
return hit;
}
static bool pbvh_grids_node_raycast(Tree &pbvh,
Node &node,
const float (*origco)[3],
const float ray_start[3],
const float ray_normal[3],
IsectRayPrecalc *isect_precalc,
float *depth,
PBVHVertRef *r_active_vertex,
int *r_active_grid_index,
float *r_face_normal)
{
const CCGKey key = BKE_subdiv_ccg_key_top_level(*pbvh.subdiv_ccg_);
const int totgrid = node.prim_indices_.size();
const int gridsize = key.grid_size;
bool hit = false;
float nearest_vertex_co[3] = {0.0};
const BitGroupVector<> &grid_hidden = pbvh.subdiv_ccg_->grid_hidden;
const Span<CCGElem *> grids = pbvh.subdiv_ccg_->grids;
for (int i = 0; i < totgrid; i++) {
const int grid_index = node.prim_indices_[i];
CCGElem *grid = grids[grid_index];
if (!grid) {
continue;
}
for (int y = 0; y < gridsize - 1; y++) {
for (int x = 0; x < gridsize - 1; x++) {
/* check if grid face is hidden */
if (!grid_hidden.is_empty()) {
if (paint_is_grid_face_hidden(grid_hidden[grid_index], gridsize, x, y)) {
continue;
}
}
const float *co[4];
if (origco) {
co[0] = origco[(y + 1) * gridsize + x];
co[1] = origco[(y + 1) * gridsize + x + 1];
co[2] = origco[y * gridsize + x + 1];
co[3] = origco[y * gridsize + x];
}
else {
co[0] = CCG_grid_elem_co(key, grid, x, y + 1);
co[1] = CCG_grid_elem_co(key, grid, x + 1, y + 1);
co[2] = CCG_grid_elem_co(key, grid, x + 1, y);
co[3] = CCG_grid_elem_co(key, grid, x, y);
}
if (ray_face_intersection_quad(
ray_start, isect_precalc, co[0], co[1], co[2], co[3], depth))
{
hit = true;
if (r_face_normal) {
normal_quad_v3(r_face_normal, co[0], co[1], co[2], co[3]);
}
if (r_active_vertex) {
float location[3] = {0.0};
madd_v3_v3v3fl(location, ray_start, ray_normal, *depth);
const int x_it[4] = {0, 1, 1, 0};
const int y_it[4] = {1, 1, 0, 0};
for (int j = 0; j < 4; j++) {
/* Always assign nearest_vertex_co in the first iteration to avoid comparison against
* uninitialized values. This stores the closest vertex in the current intersecting
* quad. */
if (j == 0 || len_squared_v3v3(location, co[j]) <
len_squared_v3v3(location, nearest_vertex_co))
{
copy_v3_v3(nearest_vertex_co, co[j]);
r_active_vertex->i = key.grid_area * grid_index + (y + y_it[j]) * key.grid_size +
(x + x_it[j]);
}
}
}
if (r_active_grid_index) {
*r_active_grid_index = grid_index;
}
}
}
}
if (origco) {
origco += gridsize * gridsize;
}
}
return hit;
}
bool raycast_node(Tree &pbvh,
Node &node,
const float (*origco)[3],
bool use_origco,
const Span<int> corner_verts,
const Span<int3> corner_tris,
const Span<int> corner_tri_faces,
const Span<bool> hide_poly,
const float ray_start[3],
const float ray_normal[3],
IsectRayPrecalc *isect_precalc,
float *depth,
PBVHVertRef *active_vertex,
int *active_face_grid_index,
float *face_normal)
{
bool hit = false;
if (node.flag_ & PBVH_FullyHidden) {
return false;
}
switch (pbvh.type()) {
case Type::Mesh:
hit |= pbvh_faces_node_raycast(pbvh,
node,
origco,
corner_verts,
corner_tris,
corner_tri_faces,
hide_poly,
ray_start,
ray_normal,
isect_precalc,
depth,
active_vertex,
active_face_grid_index,
face_normal);
break;
case Type::Grids:
hit |= pbvh_grids_node_raycast(pbvh,
node,
origco,
ray_start,
ray_normal,
isect_precalc,
depth,
active_vertex,
active_face_grid_index,
face_normal);
break;
case Type::BMesh:
BM_mesh_elem_index_ensure(pbvh.bm_, BM_VERT);
hit = bmesh_node_raycast(node,
ray_start,
ray_normal,
isect_precalc,
depth,
use_origco,
active_vertex,
face_normal);
break;
}
return hit;
}
void clip_ray_ortho(
Tree &pbvh, bool original, float ray_start[3], float ray_end[3], float ray_normal[3])
{
if (pbvh.nodes_.is_empty()) {
return;
}
float rootmin_start, rootmin_end;
Bounds<float3> bb_root;
float bb_center[3], bb_diff[3];
IsectRayAABB_Precalc ray;
float ray_normal_inv[3];
float offset = 1.0f + 1e-3f;
const float offset_vec[3] = {1e-3f, 1e-3f, 1e-3f};
if (original) {
bb_root = BKE_pbvh_node_get_original_BB(&pbvh.nodes_.first());
}
else {
bb_root = node_bounds(pbvh.nodes_.first());
}
/* Calc rough clipping to avoid overflow later. See #109555. */
float mat[3][3];
axis_dominant_v3_to_m3(mat, ray_normal);
float a[3], b[3], min[3] = {FLT_MAX, FLT_MAX, FLT_MAX}, max[3] = {FLT_MIN, FLT_MIN, FLT_MIN};
/* Compute AABB bounds rotated along ray_normal. */
copy_v3_v3(a, bb_root.min);
copy_v3_v3(b, bb_root.max);
mul_m3_v3(mat, a);
mul_m3_v3(mat, b);
minmax_v3v3_v3(min, max, a);
minmax_v3v3_v3(min, max, b);
float cent[3];
/* Find midpoint of aabb on ray. */
mid_v3_v3v3(cent, bb_root.min, bb_root.max);
float t = line_point_factor_v3(cent, ray_start, ray_end);
interp_v3_v3v3(cent, ray_start, ray_end, t);
/* Compute rough interval. */
float dist = max[2] - min[2];
madd_v3_v3v3fl(ray_start, cent, ray_normal, -dist);
madd_v3_v3v3fl(ray_end, cent, ray_normal, dist);
/* Slightly offset min and max in case we have a zero width node
* (due to a plane mesh for instance), or faces very close to the bounding box boundary. */
mid_v3_v3v3(bb_center, bb_root.max, bb_root.min);
/* Diff should be same for both min/max since it's calculated from center. */
sub_v3_v3v3(bb_diff, bb_root.max, bb_center);
/* Handles case of zero width bb. */
add_v3_v3(bb_diff, offset_vec);
madd_v3_v3v3fl(bb_root.max, bb_center, bb_diff, offset);
madd_v3_v3v3fl(bb_root.min, bb_center, bb_diff, -offset);
/* Final projection of start ray. */
isect_ray_aabb_v3_precalc(&ray, ray_start, ray_normal);
if (!isect_ray_aabb_v3(&ray, bb_root.min, bb_root.max, &rootmin_start)) {
return;
}
/* Final projection of end ray. */
mul_v3_v3fl(ray_normal_inv, ray_normal, -1.0);
isect_ray_aabb_v3_precalc(&ray, ray_end, ray_normal_inv);
/* Unlikely to fail exiting if entering succeeded, still keep this here. */
if (!isect_ray_aabb_v3(&ray, bb_root.min, bb_root.max, &rootmin_end)) {
return;
}
/*
* As a last-ditch effort to correct floating point overflow compute
* and add an epsilon if rootmin_start == rootmin_end.
*/
float epsilon = (std::nextafter(rootmin_start, rootmin_start + 1000.0f) - rootmin_start) *
5000.0f;
if (rootmin_start == rootmin_end) {
rootmin_start -= epsilon;
rootmin_end += epsilon;
}
madd_v3_v3v3fl(ray_start, ray_start, ray_normal, rootmin_start);
madd_v3_v3v3fl(ray_end, ray_end, ray_normal_inv, rootmin_end);
}
/* -------------------------------------------------------------------- */
static bool nearest_to_ray_aabb_dist_sq(Node *node,
const DistRayAABB_Precalc &dist_ray_to_aabb_precalc,
const bool original)
{
const float *bb_min, *bb_max;
if (original) {
/* BKE_pbvh_node_get_original_BB */
bb_min = node->bounds_orig_.min;
bb_max = node->bounds_orig_.max;
}
else {
bb_min = node->bounds_.min;
bb_max = node->bounds_.max;
}
float co_dummy[3], depth;
node->tmin_ = dist_squared_ray_to_aabb_v3(
&dist_ray_to_aabb_precalc, bb_min, bb_max, co_dummy, &depth);
/* Ideally we would skip distances outside the range. */
return depth > 0.0f;
}
void find_nearest_to_ray(Tree &pbvh,
const FunctionRef<void(Node &node, float *tmin)> fn,
const float ray_start[3],
const float ray_normal[3],
const bool original)
{
const DistRayAABB_Precalc ray_dist_precalc = dist_squared_ray_to_aabb_v3_precalc(ray_start,
ray_normal);
search_callback_occluded(
pbvh,
[&](Node &node) { return nearest_to_ray_aabb_dist_sq(&node, ray_dist_precalc, original); },
fn);
}
static bool pbvh_faces_node_nearest_to_ray(Tree &pbvh,
const Node &node,
const float (*origco)[3],
const Span<int> corner_verts,
const Span<int3> corner_tris,
const Span<int> corner_tri_faces,
const Span<bool> hide_poly,
const float ray_start[3],
const float ray_normal[3],
float *depth,
float *dist_sq)
{
using namespace blender;
const Span<float3> positions = pbvh.vert_positions_;
bool hit = false;
for (const int i : node.prim_indices_.index_range()) {
const int tri_i = node.prim_indices_[i];
const int3 &corner_tri = corner_tris[tri_i];
const int3 face_verts = node.face_vert_indices_[i];
if (!hide_poly.is_empty() && hide_poly[corner_tri_faces[tri_i]]) {
continue;
}
if (origco) {
/* Intersect with backed-up original coordinates. */
hit |= ray_face_nearest_tri(ray_start,
ray_normal,
origco[face_verts[0]],
origco[face_verts[1]],
origco[face_verts[2]],
depth,
dist_sq);
}
else {
/* intersect with current coordinates */
hit |= ray_face_nearest_tri(ray_start,
ray_normal,
positions[corner_verts[corner_tri[0]]],
positions[corner_verts[corner_tri[1]]],
positions[corner_verts[corner_tri[2]]],
depth,
dist_sq);
}
}
return hit;
}
static bool pbvh_grids_node_nearest_to_ray(Tree &pbvh,
Node &node,
const float (*origco)[3],
const float ray_start[3],
const float ray_normal[3],
float *depth,
float *dist_sq)
{
const CCGKey key = BKE_subdiv_ccg_key_top_level(*pbvh.subdiv_ccg_);
const int totgrid = node.prim_indices_.size();
const int gridsize = key.grid_size;
bool hit = false;
const BitGroupVector<> &grid_hidden = pbvh.subdiv_ccg_->grid_hidden;
const Span<CCGElem *> grids = pbvh.subdiv_ccg_->grids;
for (int i = 0; i < totgrid; i++) {
CCGElem *grid = grids[node.prim_indices_[i]];
if (!grid) {
continue;
}
for (int y = 0; y < gridsize - 1; y++) {
for (int x = 0; x < gridsize - 1; x++) {
/* check if grid face is hidden */
if (!grid_hidden.is_empty()) {
if (paint_is_grid_face_hidden(grid_hidden[node.prim_indices_[i]], gridsize, x, y)) {
continue;
}
}
if (origco) {
hit |= ray_face_nearest_quad(ray_start,
ray_normal,
origco[y * gridsize + x],
origco[y * gridsize + x + 1],
origco[(y + 1) * gridsize + x + 1],
origco[(y + 1) * gridsize + x],
depth,
dist_sq);
}
else {
hit |= ray_face_nearest_quad(ray_start,
ray_normal,
CCG_grid_elem_co(key, grid, x, y),
CCG_grid_elem_co(key, grid, x + 1, y),
CCG_grid_elem_co(key, grid, x + 1, y + 1),
CCG_grid_elem_co(key, grid, x, y + 1),
depth,
dist_sq);
}
}
}
if (origco) {
origco += gridsize * gridsize;
}
}
return hit;
}
bool find_nearest_to_ray_node(Tree &pbvh,
Node &node,
const float (*origco)[3],
bool use_origco,
const Span<int> corner_verts,
const Span<int3> corner_tris,
const Span<int> corner_tri_faces,
const Span<bool> hide_poly,
const float ray_start[3],
const float ray_normal[3],
float *depth,
float *dist_sq)
{
bool hit = false;
if (node.flag_ & PBVH_FullyHidden) {
return false;
}
switch (pbvh.type()) {
case Type::Mesh:
hit |= pbvh_faces_node_nearest_to_ray(pbvh,
node,
origco,
corner_verts,
corner_tris,
corner_tri_faces,
hide_poly,
ray_start,
ray_normal,
depth,
dist_sq);
break;
case Type::Grids:
hit |= pbvh_grids_node_nearest_to_ray(
pbvh, node, origco, ray_start, ray_normal, depth, dist_sq);
break;
case Type::BMesh:
hit = bmesh_node_nearest_to_ray(node, ray_start, ray_normal, depth, dist_sq, use_origco);
break;
}
return hit;
}
enum PlaneAABBIsect {
ISECT_INSIDE,
ISECT_OUTSIDE,
ISECT_INTERSECT,
};
/* Adapted from:
* http://www.gamedev.net/community/forums/topic.asp?topic_id=512123
* Returns true if the AABB is at least partially within the frustum
* (ok, not a real frustum), false otherwise.
*/
static PlaneAABBIsect test_frustum_aabb(const Bounds<float3> &bounds,
const PBVHFrustumPlanes *frustum)
{
PlaneAABBIsect ret = ISECT_INSIDE;
const float(*planes)[4] = frustum->planes;
for (int i = 0; i < frustum->num_planes; i++) {
float vmin[3], vmax[3];
for (int axis = 0; axis < 3; axis++) {
if (planes[i][axis] < 0) {
vmin[axis] = bounds.min[axis];
vmax[axis] = bounds.max[axis];
}
else {
vmin[axis] = bounds.max[axis];
vmax[axis] = bounds.min[axis];
}
}
if (dot_v3v3(planes[i], vmin) + planes[i][3] < 0) {
return ISECT_OUTSIDE;
}
if (dot_v3v3(planes[i], vmax) + planes[i][3] <= 0) {
ret = ISECT_INTERSECT;
}
}
return ret;
}
} // namespace blender::bke::pbvh
bool BKE_pbvh_node_frustum_contain_AABB(const blender::bke::pbvh::Node *node,
const PBVHFrustumPlanes *data)
{
return blender::bke::pbvh::test_frustum_aabb(node->bounds_, data) !=
blender::bke::pbvh::ISECT_OUTSIDE;
}
bool BKE_pbvh_node_frustum_exclude_AABB(const blender::bke::pbvh::Node *node,
const PBVHFrustumPlanes *data)
{
return blender::bke::pbvh::test_frustum_aabb(node->bounds_, data) !=
blender::bke::pbvh::ISECT_INSIDE;
}
static blender::draw::pbvh::PBVH_GPU_Args pbvh_draw_args_init(const Mesh &mesh,
blender::bke::pbvh::Tree &pbvh,
const blender::bke::pbvh::Node &node)
{
/* TODO: Use an explicit argument for the original mesh to avoid relying on
* #Tree::mesh. */
blender::draw::pbvh::PBVH_GPU_Args args{};
args.pbvh_type = pbvh.type();
/* Occasionally, the evaluated and original meshes are out of sync. Prefer using the pbvh mesh in
* these cases. See #115856 and #121008 */
args.face_sets_color_default = pbvh.mesh_ ? pbvh.mesh_->face_sets_color_default :
mesh.face_sets_color_default;
args.face_sets_color_seed = pbvh.mesh_ ? pbvh.mesh_->face_sets_color_seed :
mesh.face_sets_color_seed;
args.active_color = pbvh.mesh_ ? pbvh.mesh_->active_color_attribute :
mesh.active_color_attribute;
args.render_color = pbvh.mesh_ ? pbvh.mesh_->default_color_attribute :
mesh.default_color_attribute;
switch (pbvh.type()) {
case blender::bke::pbvh::Type::Mesh:
args.vert_data = &mesh.vert_data;
args.corner_data = &mesh.corner_data;
args.face_data = &mesh.face_data;
args.mesh = pbvh.mesh_;
args.vert_positions = pbvh.vert_positions_;
args.corner_verts = mesh.corner_verts();
args.corner_edges = mesh.corner_edges();
args.corner_tris = mesh.corner_tris();
args.vert_normals = pbvh.vert_normals_;
args.face_normals = pbvh.face_normals_;
/* Retrieve data from the original mesh. Ideally that would be passed to this function to
* make it clearer when each is used. */
args.hide_poly = *pbvh.mesh_->attributes().lookup<bool>(".hide_poly",
blender::bke::AttrDomain::Face);
args.prim_indices = node.prim_indices_;
args.tri_faces = mesh.corner_tri_faces();
break;
case blender::bke::pbvh::Type::Grids:
args.vert_data = &pbvh.mesh_->vert_data;
args.corner_data = &pbvh.mesh_->corner_data;
args.face_data = &pbvh.mesh_->face_data;
args.ccg_key = BKE_subdiv_ccg_key_top_level(*pbvh.subdiv_ccg_);
args.mesh = pbvh.mesh_;
args.grid_indices = node.prim_indices_;
args.subdiv_ccg = pbvh.subdiv_ccg_;
args.grids = pbvh.subdiv_ccg_->grids;
args.vert_normals = pbvh.vert_normals_;
break;
case blender::bke::pbvh::Type::BMesh:
args.bm = pbvh.bm_;
args.vert_data = &args.bm->vdata;
args.corner_data = &args.bm->ldata;
args.face_data = &args.bm->pdata;
args.bm_faces = &node.bm_faces_;
args.cd_mask_layer = CustomData_get_offset_named(
&pbvh.bm_->vdata, CD_PROP_FLOAT, ".sculpt_mask");
break;
}
return args;
}
namespace blender::bke::pbvh {
static void node_update_draw_buffers(const Mesh &mesh, Tree &pbvh, Node &node)
{
/* Create and update draw buffers. The functions called here must not
* do any OpenGL calls. Flags are not cleared immediately, that happens
* after GPU_pbvh_buffer_flush() which does the final OpenGL calls. */
if (node.flag_ & PBVH_RebuildDrawBuffers) {
const blender::draw::pbvh::PBVH_GPU_Args args = pbvh_draw_args_init(mesh, pbvh, node);
node.draw_batches_ = blender::draw::pbvh::node_create(args);
}
if (node.flag_ & PBVH_UpdateDrawBuffers) {
node.debug_draw_gen_++;
if (node.draw_batches_) {
const blender::draw::pbvh::PBVH_GPU_Args args = pbvh_draw_args_init(mesh, pbvh, node);
blender::draw::pbvh::node_update(node.draw_batches_, args);
}
}
}
void free_draw_buffers(Tree & /*pbvh*/, Node *node)
{
if (node->draw_batches_) {
draw::pbvh::node_free(node->draw_batches_);
node->draw_batches_ = nullptr;
}
}
static void pbvh_update_draw_buffers(const Mesh &mesh,
Tree &pbvh,
Span<Node *> nodes,
int update_flag)
{
if (pbvh.type() == Type::BMesh && !pbvh.bm_) {
/* BMesh hasn't been created yet */
return;
}
if ((update_flag & PBVH_RebuildDrawBuffers) || ELEM(pbvh.type(), Type::Grids, Type::BMesh)) {
/* Free buffers uses OpenGL, so not in parallel. */
for (Node *node : nodes) {
if (node->flag_ & PBVH_RebuildDrawBuffers) {
free_draw_buffers(pbvh, node);
}
else if ((node->flag_ & PBVH_UpdateDrawBuffers) && node->draw_batches_) {
const draw::pbvh::PBVH_GPU_Args args = pbvh_draw_args_init(mesh, pbvh, *node);
draw::pbvh::update_pre(node->draw_batches_, args);
}
}
}
/* Parallel creation and update of draw buffers. */
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (Node *node : nodes.slice(range)) {
node_update_draw_buffers(mesh, pbvh, *node);
}
});
/* Flush buffers uses OpenGL, so not in parallel. */
for (Node *node : nodes) {
if (node->flag_ & PBVH_UpdateDrawBuffers) {
if (node->draw_batches_) {
draw::pbvh::node_gpu_flush(node->draw_batches_);
}
}
node->flag_ &= ~(PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers);
}
}
void draw_cb(const Mesh &mesh,
Tree &pbvh,
bool update_only_visible,
const PBVHFrustumPlanes &update_frustum,
const PBVHFrustumPlanes &draw_frustum,
const FunctionRef<void(draw::pbvh::PBVHBatches *batches,
const draw::pbvh::PBVH_GPU_Args &args)> draw_fn)
{
if (update_only_visible) {
int update_flag = 0;
Vector<Node *> nodes = search_gather(pbvh, [&](Node &node) {
if (!BKE_pbvh_node_frustum_contain_AABB(&node, &update_frustum)) {
return false;
}
update_flag |= node.flag_;
return true;
});
if (update_flag & (PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers)) {
pbvh_update_draw_buffers(mesh, pbvh, nodes, update_flag);
}
}
else {
/* Get all nodes with draw updates, also those outside the view. */
Vector<Node *> nodes = search_gather(pbvh, [&](Node &node) {
return update_search(&node, PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers);
});
pbvh_update_draw_buffers(mesh, pbvh, nodes, PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers);
}
/* Draw visible nodes. */
Vector<Node *> nodes = search_gather(
pbvh, [&](Node &node) { return BKE_pbvh_node_frustum_contain_AABB(&node, &draw_frustum); });
for (Node *node : nodes) {
if (node->flag_ & PBVH_FullyHidden) {
continue;
}
if (!node->draw_batches_) {
continue;
}
const draw::pbvh::PBVH_GPU_Args args = pbvh_draw_args_init(mesh, pbvh, *node);
draw_fn(node->draw_batches_, args);
}
}
} // namespace blender::bke::pbvh
void BKE_pbvh_draw_debug_cb(blender::bke::pbvh::Tree &pbvh,
void (*draw_fn)(blender::bke::pbvh::Node *node,
void *user_data,
const float bmin[3],
const float bmax[3],
PBVHNodeFlags flag),
void *user_data)
{
PBVHNodeFlags flag = PBVH_Leaf;
for (blender::bke::pbvh::Node &node : pbvh.nodes_) {
if (node.flag_ & PBVH_TexLeaf) {
flag = PBVH_TexLeaf;
break;
}
}
for (blender::bke::pbvh::Node &node : pbvh.nodes_) {
if (!(node.flag_ & flag)) {
continue;
}
draw_fn(&node, user_data, node.bounds_.min, node.bounds_.max, node.flag_);
}
}
void BKE_pbvh_vert_coords_apply(blender::bke::pbvh::Tree &pbvh,
const blender::Span<blender::float3> vert_positions)
{
using namespace blender::bke::pbvh;
if (!pbvh.deformed_) {
if (!pbvh.vert_positions_.is_empty()) {
/* When the Tree is deformed, it creates a separate vertex position array
* that it owns directly. Conceptually these copies often aren't and often adds extra
* indirection, but:
* - Sculpting shape keys, the deformations are flushed back to the keys as a separate step.
* - Sculpting on a deformed mesh, deformations are also flushed to original positions
* separately.
* - The Tree currently always assumes we want to change positions, and
* has no way to avoid calculating normals if it's only used for painting, for example. */
pbvh.vert_positions_deformed_ = pbvh.vert_positions_.as_span();
pbvh.vert_positions_ = pbvh.vert_positions_deformed_;
pbvh.vert_normals_deformed_ = pbvh.vert_normals_;
pbvh.vert_normals_ = pbvh.vert_normals_deformed_;
pbvh.face_normals_deformed_ = pbvh.face_normals_;
pbvh.face_normals_ = pbvh.face_normals_deformed_;
pbvh.deformed_ = true;
}
}
if (!pbvh.vert_positions_.is_empty()) {
blender::MutableSpan<blender::float3> positions = pbvh.vert_positions_;
positions.copy_from(vert_positions);
for (Node &node : pbvh.nodes_) {
BKE_pbvh_node_mark_positions_update(&node);
}
update_bounds(pbvh);
store_bounds_orig(pbvh);
}
}
bool BKE_pbvh_is_deformed(const blender::bke::pbvh::Tree &pbvh)
{
return pbvh.deformed_;
}
/* Proxies */
PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(blender::bke::pbvh::Node *node)
{
if (!node->color_buffer_.color) {
node->color_buffer_.color = static_cast<float(*)[4]>(
MEM_callocN(sizeof(float[4]) * node->unique_verts_num_, "Color buffer"));
}
return &node->color_buffer_;
}
void BKE_pbvh_node_color_buffer_free(blender::bke::pbvh::Tree &pbvh)
{
blender::Vector<blender::bke::pbvh::Node *> nodes = search_gather(pbvh, {});
for (blender::bke::pbvh::Node *node : nodes) {
MEM_SAFE_FREE(node->color_buffer_.color);
}
}
void pbvh_vertex_iter_init(blender::bke::pbvh::Tree &pbvh,
blender::bke::pbvh::Node *node,
PBVHVertexIter *vi,
int mode)
{
vi->grid = nullptr;
vi->no = nullptr;
vi->fno = nullptr;
vi->vert_positions = {};
vi->vertex.i = 0LL;
int uniq_verts;
int totvert;
switch (pbvh.type()) {
case blender::bke::pbvh::Type::Grids:
totvert = node->prim_indices_.size() *
BKE_subdiv_ccg_key_top_level(*pbvh.subdiv_ccg_).grid_area;
uniq_verts = totvert;
break;
case blender::bke::pbvh::Type::Mesh:
totvert = node->vert_indices_.size();
uniq_verts = node->unique_verts_num_;
break;
case blender::bke::pbvh::Type::BMesh:
totvert = node->bm_unique_verts_.size() + node->bm_other_verts_.size();
uniq_verts = node->bm_unique_verts_.size();
break;
}
if (pbvh.type() == blender::bke::pbvh::Type::Grids) {
vi->key = BKE_subdiv_ccg_key_top_level(*pbvh.subdiv_ccg_);
vi->grids = pbvh.subdiv_ccg_->grids.data();
vi->grid_indices = node->prim_indices_.data();
vi->totgrid = node->prim_indices_.size();
vi->gridsize = vi->key.grid_size;
}
else {
vi->key = {};
vi->grids = nullptr;
vi->grid_indices = nullptr;
vi->totgrid = 1;
vi->gridsize = 0;
}
if (mode == PBVH_ITER_ALL) {
vi->totvert = totvert;
}
else {
vi->totvert = uniq_verts;
}
vi->vert_indices = node->vert_indices_.data();
vi->vert_positions = pbvh.vert_positions_;
vi->is_mesh = !pbvh.vert_positions_.is_empty();
if (pbvh.type() == blender::bke::pbvh::Type::BMesh) {
vi->bm_unique_verts = node->bm_unique_verts_.begin();
vi->bm_unique_verts_end = node->bm_unique_verts_.end();
vi->bm_other_verts = node->bm_other_verts_.begin();
vi->bm_other_verts_end = node->bm_other_verts_.end();
vi->bm_vdata = &pbvh.bm_->vdata;
vi->cd_vert_mask_offset = CustomData_get_offset_named(
vi->bm_vdata, CD_PROP_FLOAT, ".sculpt_mask");
}
vi->gh.reset();
if (vi->grids && mode == PBVH_ITER_UNIQUE) {
vi->grid_hidden = pbvh.subdiv_ccg_->grid_hidden.is_empty() ? nullptr :
&pbvh.subdiv_ccg_->grid_hidden;
}
vi->mask = 0.0f;
if (pbvh.type() == blender::bke::pbvh::Type::Mesh) {
vi->vert_normals = pbvh.vert_normals_;
vi->hide_vert = static_cast<const bool *>(
CustomData_get_layer_named(&pbvh.mesh_->vert_data, CD_PROP_BOOL, ".hide_vert"));
vi->vmask = static_cast<const float *>(
CustomData_get_layer_named(&pbvh.mesh_->vert_data, CD_PROP_FLOAT, ".sculpt_mask"));
}
}
bool pbvh_has_mask(const blender::bke::pbvh::Tree &pbvh)
{
switch (pbvh.type()) {
case blender::bke::pbvh::Type::Grids:
return BKE_subdiv_ccg_key_top_level(*pbvh.subdiv_ccg_).has_mask;
case blender::bke::pbvh::Type::Mesh:
return pbvh.mesh_->attributes().contains(".sculpt_mask");
case blender::bke::pbvh::Type::BMesh:
return pbvh.bm_ &&
CustomData_has_layer_named(&pbvh.bm_->vdata, CD_PROP_FLOAT, ".sculpt_mask");
}
return false;
}
bool pbvh_has_face_sets(blender::bke::pbvh::Tree &pbvh)
{
switch (pbvh.type()) {
case blender::bke::pbvh::Type::Grids:
case blender::bke::pbvh::Type::Mesh:
return pbvh.mesh_->attributes().contains(".sculpt_face_set");
case blender::bke::pbvh::Type::BMesh:
return CustomData_has_layer_named(&pbvh.bm_->pdata, CD_PROP_FLOAT, ".sculpt_mask");
}
return false;
}
namespace blender::bke::pbvh {
void set_frustum_planes(Tree &pbvh, PBVHFrustumPlanes *planes)
{
pbvh.num_planes_ = planes->num_planes;
for (int i = 0; i < pbvh.num_planes_; i++) {
copy_v4_v4(pbvh.planes_[i], planes->planes[i]);
}
}
void get_frustum_planes(const Tree &pbvh, PBVHFrustumPlanes *planes)
{
planes->num_planes = pbvh.num_planes_;
for (int i = 0; i < planes->num_planes; i++) {
copy_v4_v4(planes->planes[i], pbvh.planes_[i]);
}
}
} // namespace blender::bke::pbvh
Mesh *BKE_pbvh_get_mesh(blender::bke::pbvh::Tree &pbvh)
{
return pbvh.mesh_;
}
blender::Span<blender::float3> BKE_pbvh_get_vert_positions(const blender::bke::pbvh::Tree &pbvh)
{
BLI_assert(pbvh.type() == blender::bke::pbvh::Type::Mesh);
return pbvh.vert_positions_;
}
blender::MutableSpan<blender::float3> BKE_pbvh_get_vert_positions(blender::bke::pbvh::Tree &pbvh)
{
BLI_assert(pbvh.type() == blender::bke::pbvh::Type::Mesh);
return pbvh.vert_positions_;
}
blender::Span<blender::float3> BKE_pbvh_get_vert_normals(const blender::bke::pbvh::Tree &pbvh)
{
BLI_assert(pbvh.type() == blender::bke::pbvh::Type::Mesh);
return pbvh.vert_normals_;
}
void BKE_pbvh_subdiv_cgg_set(blender::bke::pbvh::Tree &pbvh, SubdivCCG *subdiv_ccg)
{
pbvh.subdiv_ccg_ = subdiv_ccg;
}
void BKE_pbvh_ensure_node_loops(blender::bke::pbvh::Tree &pbvh,
const blender::Span<blender::int3> corner_tris)
{
using namespace blender;
BLI_assert(pbvh.type() == blender::bke::pbvh::Type::Mesh);
int totloop = 0;
/* Check if nodes already have loop indices. */
for (blender::bke::pbvh::Node &node : pbvh.nodes_) {
if (!(node.flag_ & PBVH_Leaf)) {
continue;
}
if (!node.corner_indices_.is_empty()) {
return;
}
totloop += node.prim_indices_.size() * 3;
}
BLI_bitmap *visit = BLI_BITMAP_NEW(totloop, __func__);
/* Create loop indices from node loop triangles. */
Vector<int> corner_indices;
for (blender::bke::pbvh::Node &node : pbvh.nodes_) {
if (!(node.flag_ & PBVH_Leaf)) {
continue;
}
corner_indices.clear();
for (const int i : node.prim_indices_) {
const int3 &tri = corner_tris[i];
for (int k = 0; k < 3; k++) {
if (!BLI_BITMAP_TEST(visit, tri[k])) {
corner_indices.append(tri[k]);
BLI_BITMAP_ENABLE(visit, tri[k]);
}
}
}
node.corner_indices_ = corner_indices.as_span();
}
MEM_SAFE_FREE(visit);
}
int BKE_pbvh_debug_draw_gen_get(blender::bke::pbvh::Node &node)
{
return node.debug_draw_gen_;
}
void BKE_pbvh_sync_visibility_from_verts(blender::bke::pbvh::Tree &pbvh, Mesh *mesh)
{
using namespace blender;
using namespace blender::bke;
switch (pbvh.type()) {
case blender::bke::pbvh::Type::Mesh: {
mesh_hide_vert_flush(*mesh);
break;
}
case blender::bke::pbvh::Type::BMesh: {
BMIter iter;
BMVert *v;
BMEdge *e;
BMFace *f;
BM_ITER_MESH (f, &iter, pbvh.bm_, BM_FACES_OF_MESH) {
BM_elem_flag_disable(f, BM_ELEM_HIDDEN);
}
BM_ITER_MESH (e, &iter, pbvh.bm_, BM_EDGES_OF_MESH) {
BM_elem_flag_disable(e, BM_ELEM_HIDDEN);
}
BM_ITER_MESH (v, &iter, pbvh.bm_, BM_VERTS_OF_MESH) {
if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
continue;
}
BMIter iter_l;
BMLoop *l;
BM_ITER_ELEM (l, &iter_l, v, BM_LOOPS_OF_VERT) {
BM_elem_flag_enable(l->e, BM_ELEM_HIDDEN);
BM_elem_flag_enable(l->f, BM_ELEM_HIDDEN);
}
}
break;
}
case blender::bke::pbvh::Type::Grids: {
const OffsetIndices faces = mesh->faces();
const BitGroupVector<> &grid_hidden = pbvh.subdiv_ccg_->grid_hidden;
CCGKey key = BKE_subdiv_ccg_key_top_level(*pbvh.subdiv_ccg_);
IndexMaskMemory memory;
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()) {
attributes.remove(".hide_poly");
}
else {
SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_span<bool>(
".hide_poly", AttrDomain::Face, AttributeInitConstruct());
hide_poly.span.fill(false);
index_mask::masked_fill(hide_poly.span, true, hidden_faces);
hide_poly.finish();
}
mesh_hide_face_flush(*mesh);
break;
}
}
}
namespace blender::bke::pbvh {
Vector<Node *> search_gather(Tree &pbvh,
const FunctionRef<bool(Node &)> scb,
PBVHNodeFlags leaf_flag)
{
if (pbvh.nodes_.is_empty()) {
return {};
}
PBVHIter iter;
Vector<Node *> nodes;
pbvh_iter_begin(&iter, pbvh, scb);
Node *node;
while ((node = pbvh_iter_next(&iter, leaf_flag))) {
if (node->flag_ & leaf_flag) {
nodes.append(node);
}
}
pbvh_iter_end(&iter);
return nodes;
}
} // namespace blender::bke::pbvh