Files
test2/source/blender/blenkernel/intern/pbvh.cc
Sean Kim fc8a163892 Refactor: Sculpt: Remove usage of PBVHVertRef in raycast methods
Part of #118145.

This commit removes the `PBVHVertRef` abstraction and changes the last
remaining usage of it. The `raycast_node` API has been turned into a
static function and the corresponding typed APIs exposed for direct
usage.

Pull Request: https://projects.blender.org/blender/blender/pulls/127933
2024-09-21 20:17:18 +02:00

2657 lines
88 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_stack.hh"
#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 "DNA_object_types.h"
#include "BKE_attribute.hh"
#include "BKE_ccg.hh"
#include "BKE_mesh.hh"
#include "BKE_mesh_mapping.hh"
#include "BKE_object.hh"
#include "BKE_paint.hh"
#include "BKE_pbvh_api.hh"
#include "BKE_subdiv_ccg.hh"
#include "DEG_depsgraph_query.hh"
#include "bmesh.hh"
#include "atomic_ops.h"
#include "pbvh_intern.hh"
namespace blender::bke::pbvh {
// #define DEBUG_BUILD_TIME
#define STACK_FIXED_DEPTH 100
/** 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 Bounds<float3> merge_bounds(const Bounds<float3> &a, const Bounds<float3> &b)
{
return bounds::merge(a, b);
}
static int partition_along_axis(const Span<float3> face_centers,
MutableSpan<int> faces,
const int axis,
const float middle)
{
const int *split = std::partition(faces.begin(), faces.end(), [&](const int face) {
return face_centers[face][axis] >= middle;
});
return split - faces.begin();
}
static int partition_material_indices(const Span<int> material_indices, MutableSpan<int> faces)
{
const int first = material_indices[faces.first()];
const int *split = std::partition(
faces.begin(), faces.end(), [&](const int face) { return material_indices[face] == first; });
return split - faces.begin();
}
BLI_NOINLINE static void build_mesh_leaf_nodes(const int verts_num,
const OffsetIndices<int> faces,
const Span<int> corner_verts,
MutableSpan<MeshNode> nodes)
{
#ifdef DEBUG_BUILD_TIME
SCOPED_TIMER_AVERAGED(__func__);
#endif
Array<Array<int>> verts_per_node(nodes.size(), NoInitialization());
threading::parallel_for(nodes.index_range(), 8, [&](const IndexRange range) {
Set<int> verts;
for (const int i : range) {
MeshNode &node = nodes[i];
verts.clear();
int corners_count = 0;
for (const int face_index : node.face_indices_) {
const IndexRange face = faces[face_index];
verts.add_multiple(corner_verts.slice(face));
corners_count += face.size();
}
nodes[i].corners_num_ = corners_count;
new (&verts_per_node[i]) Array<int>(verts.size());
std::copy(verts.begin(), verts.end(), verts_per_node[i].begin());
std::sort(verts_per_node[i].begin(), verts_per_node[i].end());
}
});
Vector<int> owned_verts;
Vector<int> shared_verts;
BitVector<> vert_used(verts_num);
for (const int i : nodes.index_range()) {
MeshNode &node = nodes[i];
owned_verts.clear();
shared_verts.clear();
for (const int vert : verts_per_node[i]) {
if (vert_used[vert]) {
shared_verts.append(vert);
}
else {
vert_used[vert].set();
owned_verts.append(vert);
}
}
node.unique_verts_num_ = owned_verts.size();
node.vert_indices_.reserve(owned_verts.size() + shared_verts.size());
node.vert_indices_.add_multiple(owned_verts);
node.vert_indices_.add_multiple(shared_verts);
}
}
static bool leaf_needs_material_split(const Span<int> faces, const Span<int> material_indices)
{
if (material_indices.is_empty()) {
return false;
}
const int first = material_indices[faces.first()];
return std::any_of(
faces.begin(), faces.end(), [&](const int face) { return material_indices[face] != first; });
return false;
}
static void build_nodes_recursive_mesh(const Span<int> material_indices,
const int leaf_limit,
const int node_index,
const std::optional<Bounds<float3>> &bounds_precalc,
const Span<float3> face_centers,
const int depth,
MutableSpan<int> faces,
Vector<MeshNode> &nodes)
{
/* Decide whether this is a leaf or not */
const bool below_leaf_limit = faces.size() <= leaf_limit || depth >= STACK_FIXED_DEPTH - 1;
if (below_leaf_limit) {
if (!leaf_needs_material_split(faces, material_indices)) {
MeshNode &node = nodes[node_index];
node.flag_ |= PBVH_Leaf;
node.face_indices_ = faces;
return;
}
}
/* Add two child nodes */
nodes[node_index].children_offset_ = nodes.size();
nodes.resize(nodes.size() + 2);
int split;
if (!below_leaf_limit) {
Bounds<float3> bounds;
if (bounds_precalc) {
bounds = *bounds_precalc;
}
else {
bounds = threading::parallel_reduce(
faces.index_range(),
1024,
negative_bounds(),
[&](const IndexRange range, Bounds<float3> value) {
for (const int face : faces.slice(range)) {
math::min_max(face_centers[face], value.min, value.max);
}
return value;
},
merge_bounds);
}
const int axis = math::dominant_axis(bounds.max - bounds.min);
/* Partition primitives along that axis */
split = partition_along_axis(
face_centers, faces, axis, math::midpoint(bounds.min[axis], bounds.max[axis]));
}
else {
/* Partition primitives by material */
split = partition_material_indices(material_indices, faces);
}
/* Build children */
build_nodes_recursive_mesh(material_indices,
leaf_limit,
nodes[node_index].children_offset_,
std::nullopt,
face_centers,
depth + 1,
faces.take_front(split),
nodes);
build_nodes_recursive_mesh(material_indices,
leaf_limit,
nodes[node_index].children_offset_ + 1,
std::nullopt,
face_centers,
depth + 1,
faces.drop_front(split),
nodes);
}
inline Bounds<float3> calc_face_bounds(const Span<float3> vert_positions,
const Span<int> face_verts)
{
Bounds<float3> bounds{vert_positions[face_verts.first()]};
for (const int vert : face_verts.slice(1, face_verts.size() - 1)) {
math::min_max(vert_positions[vert], bounds.min, bounds.max);
}
return bounds;
}
Tree Tree::from_mesh(const Mesh &mesh)
{
#ifdef DEBUG_BUILD_TIME
SCOPED_TIMER_AVERAGED(__func__);
#endif
Tree pbvh(Type::Mesh);
const Span<float3> vert_positions = mesh.vert_positions();
const OffsetIndices<int> faces = mesh.faces();
const Span<int> corner_verts = mesh.corner_verts();
if (faces.is_empty()) {
return pbvh;
}
constexpr int leaf_limit = 10000;
static_assert(leaf_limit < std::numeric_limits<MeshNode::LocalVertMapIndexT>::max());
Array<float3> face_centers(faces.size());
const Bounds<float3> bounds = threading::parallel_reduce(
faces.index_range(),
1024,
negative_bounds(),
[&](const IndexRange range, const Bounds<float3> &init) {
Bounds<float3> current = init;
for (const int face : range) {
const Bounds<float3> bounds = calc_face_bounds(vert_positions,
corner_verts.slice(faces[face]));
face_centers[face] = bounds.center();
current = bounds::merge(current, bounds);
}
return current;
},
merge_bounds);
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);
pbvh.prim_indices_.reinitialize(faces.size());
array_utils::fill_index_range<int>(pbvh.prim_indices_);
Vector<MeshNode> &nodes = std::get<Vector<MeshNode>>(pbvh.nodes_);
nodes.resize(1);
{
#ifdef DEBUG_BUILD_TIME
SCOPED_TIMER_AVERAGED("build_nodes_recursive_mesh");
#endif
build_nodes_recursive_mesh(
material_index, leaf_limit, 0, bounds, face_centers, 0, pbvh.prim_indices_, nodes);
}
build_mesh_leaf_nodes(mesh.verts_num, faces, corner_verts, nodes);
pbvh.tag_positions_changed(nodes.index_range());
update_bounds_mesh(vert_positions, pbvh);
store_bounds_orig(pbvh);
if (!hide_vert.is_empty()) {
threading::parallel_for(nodes.index_range(), 8, [&](const IndexRange range) {
for (const int i : range) {
node_update_visibility_mesh(hide_vert, nodes[i]);
}
});
}
update_mask_mesh(mesh, nodes.index_range(), pbvh);
return pbvh;
}
static void build_nodes_recursive_grids(const Span<int> material_indices,
const int leaf_limit,
const int node_index,
const std::optional<Bounds<float3>> &bounds_precalc,
const Span<float3> face_centers,
const int depth,
MutableSpan<int> faces,
Vector<GridsNode> &nodes)
{
/* Decide whether this is a leaf or not */
const bool below_leaf_limit = faces.size() <= leaf_limit || depth >= STACK_FIXED_DEPTH - 1;
if (below_leaf_limit) {
if (!leaf_needs_material_split(faces, material_indices)) {
GridsNode &node = nodes[node_index];
node.flag_ |= PBVH_Leaf;
node.prim_indices_ = faces;
return;
}
}
/* Add two child nodes */
nodes[node_index].children_offset_ = nodes.size();
nodes.resize(nodes.size() + 2);
int split;
if (!below_leaf_limit) {
Bounds<float3> bounds;
if (bounds_precalc) {
bounds = *bounds_precalc;
}
else {
bounds = threading::parallel_reduce(
faces.index_range(),
1024,
negative_bounds(),
[&](const IndexRange range, Bounds<float3> value) {
for (const int face : faces.slice(range)) {
math::min_max(face_centers[face], value.min, value.max);
}
return value;
},
merge_bounds);
}
const int axis = math::dominant_axis(bounds.max - bounds.min);
/* Partition primitives along that axis */
split = partition_along_axis(
face_centers, faces, axis, math::midpoint(bounds.min[axis], bounds.max[axis]));
}
else {
/* Partition primitives by material */
split = partition_material_indices(material_indices, faces);
}
/* Build children */
build_nodes_recursive_grids(material_indices,
leaf_limit,
nodes[node_index].children_offset_,
std::nullopt,
face_centers,
depth + 1,
faces.take_front(split),
nodes);
build_nodes_recursive_grids(material_indices,
leaf_limit,
nodes[node_index].children_offset_ + 1,
std::nullopt,
face_centers,
depth + 1,
faces.drop_front(split),
nodes);
}
static Bounds<float3> calc_face_grid_bounds(const OffsetIndices<int> faces,
const Span<float3> positions,
const CCGKey &key,
const int face)
{
Bounds<float3> bounds = negative_bounds();
for (const float3 &position : positions.slice(ccg::face_range(faces, key, face))) {
math::min_max(position, bounds.min, bounds.max);
}
return bounds;
}
Tree Tree::from_grids(const Mesh &base_mesh, const SubdivCCG &subdiv_ccg)
{
#ifdef DEBUG_BUILD_TIME
SCOPED_TIMER_AVERAGED(__func__);
#endif
Tree pbvh(Type::Grids);
const OffsetIndices faces = base_mesh.faces();
if (faces.is_empty()) {
return pbvh;
}
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
const Span<float3> positions = subdiv_ccg.positions;
if (positions.is_empty()) {
return pbvh;
}
const int leaf_limit = std::max(2500 / key.grid_area, 1);
Array<float3> face_centers(faces.size());
const Bounds<float3> bounds = threading::parallel_reduce(
faces.index_range(),
leaf_limit,
negative_bounds(),
[&](const IndexRange range, const Bounds<float3> &init) {
Bounds<float3> current = init;
for (const int face : range) {
const Bounds<float3> bounds = calc_face_grid_bounds(faces, positions, key, face);
face_centers[face] = bounds.center();
current = bounds::merge(current, bounds);
}
return current;
},
merge_bounds);
const AttributeAccessor attributes = base_mesh.attributes();
const VArraySpan material_index = *attributes.lookup<int>("material_index", AttrDomain::Face);
Array<int> face_indices(faces.size());
array_utils::fill_index_range<int>(face_indices);
Vector<GridsNode> &nodes = std::get<Vector<GridsNode>>(pbvh.nodes_);
nodes.resize(1);
{
#ifdef DEBUG_BUILD_TIME
SCOPED_TIMER_AVERAGED("build_nodes_recursive_grids");
#endif
build_nodes_recursive_grids(
material_index, leaf_limit, 0, bounds, face_centers, 0, face_indices, nodes);
}
/* Convert face indices into grid indices. */
pbvh.prim_indices_.reinitialize(faces.total_size());
{
int offset = 0;
for (const int face : face_indices) {
for (const int corner : faces[face]) {
pbvh.prim_indices_[offset] = corner;
offset++;
}
}
}
/* Change the nodes to reference the BVH prim_indices array instead of the local face indices. */
Array<int> node_grids_num(nodes.size() + 1);
threading::parallel_for(nodes.index_range(), 16, [&](const IndexRange range) {
for (const int i : range) {
node_grids_num[i] = offset_indices::sum_group_sizes(faces, nodes[i].prim_indices_);
}
});
const OffsetIndices<int> node_grid_offsets = offset_indices::accumulate_counts_to_offsets(
node_grids_num);
threading::parallel_for(nodes.index_range(), 512, [&](const IndexRange range) {
for (const int i : range) {
nodes[i].prim_indices_ = pbvh.prim_indices_.as_span().slice(node_grid_offsets[i]);
}
});
pbvh.tag_positions_changed(nodes.index_range());
update_bounds_grids(key, positions, pbvh);
store_bounds_orig(pbvh);
const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
if (!grid_hidden.is_empty()) {
threading::parallel_for(nodes.index_range(), 8, [&](const IndexRange range) {
for (const int i : range) {
node_update_visibility_grids(grid_hidden, nodes[i]);
}
});
}
update_mask_grids(subdiv_ccg, nodes.index_range(), pbvh);
return pbvh;
}
Tree::Tree(const Type type) : type_(type)
{
switch (type) {
case bke::pbvh::Type::Mesh:
nodes_ = Vector<MeshNode>();
break;
case bke::pbvh::Type::Grids:
nodes_ = Vector<GridsNode>();
break;
case bke::pbvh::Type::BMesh:
nodes_ = Vector<BMeshNode>();
break;
}
}
int Tree::nodes_num() const
{
return std::visit([](const auto &nodes) { return nodes.size(); }, this->nodes_);
}
template<> Span<MeshNode> Tree::nodes() const
{
return std::get<Vector<MeshNode>>(this->nodes_);
}
template<> Span<GridsNode> Tree::nodes() const
{
return std::get<Vector<GridsNode>>(this->nodes_);
}
template<> Span<BMeshNode> Tree::nodes() const
{
return std::get<Vector<BMeshNode>>(this->nodes_);
}
template<> MutableSpan<MeshNode> Tree::nodes()
{
return std::get<Vector<MeshNode>>(this->nodes_);
}
template<> MutableSpan<GridsNode> Tree::nodes()
{
return std::get<Vector<GridsNode>>(this->nodes_);
}
template<> MutableSpan<BMeshNode> Tree::nodes()
{
return std::get<Vector<BMeshNode>>(this->nodes_);
}
Tree::~Tree()
{
std::visit(
[](auto &nodes) {
for (Node &node : nodes) {
if (node.flag_ & (PBVH_Leaf | PBVH_TexLeaf)) {
node_pixels_free(&node);
}
}
},
this->nodes_);
pixels_free(this);
}
void Tree::tag_positions_changed(const IndexMask &node_mask)
{
this->bounds_dirty_.resize(std::max(this->bounds_dirty_.size(), node_mask.min_array_size()),
false);
this->normals_dirty_.resize(std::max(this->normals_dirty_.size(), node_mask.min_array_size()),
false);
node_mask.set_bits(this->bounds_dirty_);
node_mask.set_bits(this->normals_dirty_);
if (this->draw_data) {
this->draw_data->tag_positions_changed(node_mask);
}
}
void Tree::tag_visibility_changed(const IndexMask &node_mask)
{
this->visibility_dirty_.resize(std::max(this->bounds_dirty_.size(), node_mask.min_array_size()),
false);
node_mask.set_bits(this->visibility_dirty_);
if (this->draw_data) {
this->draw_data->tag_visibility_changed(node_mask);
}
}
void Tree::tag_topology_changed(const IndexMask &node_mask)
{
if (this->draw_data) {
this->draw_data->tag_topology_changed(node_mask);
}
}
void Tree::tag_face_sets_changed(const IndexMask &node_mask)
{
if (this->draw_data) {
this->draw_data->tag_face_sets_changed(node_mask);
}
}
void Tree::tag_masks_changed(const IndexMask &node_mask)
{
if (this->draw_data) {
this->draw_data->tag_masks_changed(node_mask);
}
}
void Tree::tag_attribute_changed(const IndexMask &node_mask, const StringRef attribute_name)
{
if (this->draw_data) {
this->draw_data->tag_attribute_changed(node_mask, attribute_name);
}
}
static bool tree_is_empty(const Tree &pbvh)
{
return std::visit([](const auto &nodes) { return nodes.is_empty(); }, pbvh.nodes_);
}
static Node &first_node(Tree &pbvh)
{
BLI_assert(!tree_is_empty(pbvh));
return std::visit([](auto &nodes) -> Node & { return nodes.first(); }, pbvh.nodes_);
}
struct StackItem {
Node *node;
bool revisiting;
};
struct PBVHIter {
Tree *pbvh;
blender::FunctionRef<bool(Node &)> scb;
Stack<StackItem, 100> stack;
};
static void pbvh_iter_begin(PBVHIter *iter, Tree &pbvh, FunctionRef<bool(Node &)> scb)
{
iter->pbvh = &pbvh;
iter->scb = scb;
iter->stack.push({&first_node(pbvh), false});
}
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->stack.is_empty()) {
StackItem item = iter->stack.pop();
Node *node = item.node;
bool revisiting = item.revisiting;
/* 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;
}
/* 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 */
iter->stack.push({node, true});
/* push two child nodes on the stack */
std::visit(
[&](auto &nodes) {
iter->stack.push({&nodes[node->children_offset_ + 1], false});
iter->stack.push({&nodes[node->children_offset_], false});
},
iter->pbvh->nodes_);
}
return nullptr;
}
static Node *pbvh_iter_next_occluded(PBVHIter *iter)
{
while (!iter->stack.is_empty()) {
StackItem item = iter->stack.pop();
Node *node = item.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;
}
std::visit(
[&](auto &nodes) {
iter->stack.push({&nodes[node->children_offset_ + 1], false});
iter->stack.push({&nodes[node->children_offset_], false});
},
iter->pbvh->nodes_);
}
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 {
static void search_callback_occluded(Tree &pbvh,
const FunctionRef<bool(Node &)> scb,
const FunctionRef<void(Node &node, float *tmin)> hit_fn)
{
if (tree_is_empty(pbvh)) {
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;
}
}
}
if (tree) {
float tmin = FLT_MAX;
traverse_tree(tree, hit_fn, &tmin);
free_tree(tree);
}
}
/**
* Logic used to test whether to use the evaluated mesh for positions.
* \todo A deeper test of equality of topology array pointers would be better. This is kept for now
* to avoid changing logic during a refactor.
*/
static bool mesh_topology_count_matches(const Mesh &a, const Mesh &b)
{
return a.faces_num == b.faces_num && a.corners_num == b.corners_num &&
a.verts_num == b.verts_num;
}
static const SharedCache<Vector<float3>> &vert_normals_cache_eval(const Object &object_orig,
const Object &object_eval)
{
const SculptSession &ss = *object_orig.sculpt;
const Mesh &mesh_orig = *static_cast<const Mesh *>(object_orig.data);
BLI_assert(bke::object::pbvh_get(object_orig)->type() == Type::Mesh);
if (object_orig.mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
if (const Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(&object_eval)) {
if (mesh_topology_count_matches(*mesh_eval, mesh_orig)) {
return mesh_eval->runtime->vert_normals_cache;
}
}
if (const Mesh *mesh_eval = BKE_object_get_mesh_deform_eval(&object_eval)) {
return mesh_eval->runtime->vert_normals_cache;
}
}
if (!ss.deform_cos.is_empty()) {
BLI_assert(ss.deform_cos.size() == mesh_orig.verts_num);
return ss.vert_normals_deform;
}
return mesh_orig.runtime->vert_normals_cache;
}
static SharedCache<Vector<float3>> &vert_normals_cache_eval_for_write(Object &object_orig,
Object &object_eval)
{
return const_cast<SharedCache<Vector<float3>> &>(
vert_normals_cache_eval(object_orig, object_eval));
}
static const SharedCache<Vector<float3>> &face_normals_cache_eval(const Object &object_orig,
const Object &object_eval)
{
const SculptSession &ss = *object_orig.sculpt;
const Mesh &mesh_orig = *static_cast<const Mesh *>(object_orig.data);
BLI_assert(bke::object::pbvh_get(object_orig)->type() == Type::Mesh);
if (object_orig.mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
if (const Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(&object_eval)) {
if (mesh_topology_count_matches(*mesh_eval, mesh_orig)) {
return mesh_eval->runtime->face_normals_cache;
}
}
if (const Mesh *mesh_eval = BKE_object_get_mesh_deform_eval(&object_eval)) {
return mesh_eval->runtime->face_normals_cache;
}
}
if (!ss.deform_cos.is_empty()) {
BLI_assert(ss.deform_cos.size() == mesh_orig.verts_num);
return ss.face_normals_deform;
}
return mesh_orig.runtime->face_normals_cache;
}
static SharedCache<Vector<float3>> &face_normals_cache_eval_for_write(Object &object_orig,
Object &object_eval)
{
return const_cast<SharedCache<Vector<float3>> &>(
face_normals_cache_eval(object_orig, object_eval));
}
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<MeshNode> nodes,
const IndexMask &nodes_to_update,
MutableSpan<float3> face_normals)
{
nodes_to_update.foreach_index(GrainSize(1), [&](const int i) {
normals_calc_faces(positions, faces, corner_verts, nodes[i].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<MeshNode> nodes,
const IndexMask &nodes_to_update,
MutableSpan<float3> vert_normals)
{
nodes_to_update.foreach_index(GrainSize(1), [&](const int i) {
normals_calc_verts_simple(vert_to_face_map, face_normals, nodes[i].verts(), vert_normals);
});
}
static void update_normals_mesh(Object &object_orig,
Object &object_eval,
const Span<MeshNode> nodes,
const IndexMask &nodes_to_update)
{
/* 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. */
Mesh &mesh = *static_cast<Mesh *>(object_orig.data);
const Span<float3> positions = bke::pbvh::vert_positions_eval_from_eval(object_eval);
const OffsetIndices faces = mesh.faces();
const Span<int> corner_verts = mesh.corner_verts();
const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
SharedCache<Vector<float3>> &vert_normals_cache = vert_normals_cache_eval_for_write(object_orig,
object_eval);
SharedCache<Vector<float3>> &face_normals_cache = face_normals_cache_eval_for_write(object_orig,
object_eval);
VectorSet<int> boundary_faces;
nodes_to_update.foreach_index([&](const int i) {
const MeshNode &node = nodes[i];
for (const int vert : node.vert_indices_.as_span().drop_front(node.unique_verts_num_)) {
boundary_faces.add_multiple(vert_to_face_map[vert]);
}
});
VectorSet<int> boundary_verts;
threading::parallel_invoke(
[&]() {
if (face_normals_cache.is_dirty()) {
face_normals_cache.ensure([&](Vector<float3> &r_data) {
r_data.resize(faces.size());
bke::mesh::normals_calc_faces(positions, faces, corner_verts, r_data);
});
}
else {
face_normals_cache.update([&](Vector<float3> &r_data) {
calc_node_face_normals(positions, faces, corner_verts, nodes, nodes_to_update, r_data);
calc_boundary_face_normals(positions, faces, corner_verts, boundary_faces, r_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]));
}
});
const Span<float3> face_normals = face_normals_cache.data();
if (vert_normals_cache.is_dirty()) {
vert_normals_cache.ensure([&](Vector<float3> &r_data) {
r_data.resize(positions.size());
mesh::normals_calc_verts(
positions, faces, corner_verts, vert_to_face_map, face_normals, r_data);
});
}
else {
vert_normals_cache.update([&](Vector<float3> &r_data) {
calc_node_vert_normals(vert_to_face_map, face_normals, nodes, nodes_to_update, r_data);
calc_boundary_vert_normals(vert_to_face_map, face_normals, boundary_verts, r_data);
});
}
}
static void update_normals(Object &object_orig, Object &object_eval, Tree &pbvh)
{
IndexMaskMemory memory;
const IndexMask nodes_to_update = IndexMask::from_bits(pbvh.normals_dirty_, memory);
switch (pbvh.type()) {
case Type::Mesh: {
update_normals_mesh(object_orig, object_eval, pbvh.nodes<MeshNode>(), nodes_to_update);
break;
}
case Type::Grids: {
SculptSession &ss = *object_orig.sculpt;
SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
MutableSpan<GridsNode> nodes = pbvh.nodes<GridsNode>();
IndexMaskMemory memory;
const IndexMask faces_to_update = nodes_to_face_selection_grids(
subdiv_ccg, nodes, nodes_to_update, memory);
BKE_subdiv_ccg_update_normals(subdiv_ccg, faces_to_update);
break;
}
case Type::BMesh: {
bmesh_normals_update(pbvh, nodes_to_update);
break;
}
}
pbvh.normals_dirty_.clear_and_shrink();
}
void update_normals(const Depsgraph &depsgraph, Object &object_orig, Tree &pbvh)
{
BLI_assert(DEG_is_original_object(&object_orig));
Object &object_eval = *DEG_get_evaluated_object(&depsgraph, &object_orig);
update_normals(object_orig, object_eval, pbvh);
}
void update_normals_from_eval(Object &object_eval, Tree &pbvh)
{
/* Updating the original object's mesh normals caches is necessary because we skip dependency
* graph updates for sculpt deformations in some cases (so the evaluated object doesn't contain
* their result), and also because (currently) sculpt deformations skip tagging the mesh normals
* caches dirty. */
Object &object_orig = *DEG_get_original_object(&object_eval);
update_normals(object_orig, object_eval, pbvh);
}
void update_node_bounds_mesh(const Span<float3> positions, MeshNode &node)
{
Bounds<float3> bounds = negative_bounds();
for (const int vert : node.all_verts()) {
math::min_max(positions[vert], bounds.min, bounds.max);
}
node.bounds_ = bounds;
}
void update_node_bounds_grids(const int grid_area, const Span<float3> positions, GridsNode &node)
{
Bounds<float3> bounds = negative_bounds();
for (const int grid : node.grids()) {
for (const float3 &position : positions.slice(bke::ccg::grid_range(grid_area, grid))) {
math::min_max(position, bounds.min, bounds.max);
}
}
node.bounds_ = bounds;
}
void update_node_bounds_bmesh(BMeshNode &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;
}
struct BoundsMergeInfo {
Bounds<float3> bounds;
bool update;
};
template<typename NodeT>
static BoundsMergeInfo merge_child_bounds(MutableSpan<NodeT> nodes,
const BitSpan dirty,
const int node_index)
{
NodeT &node = nodes[node_index];
if (node.flag_ & PBVH_Leaf) {
const bool update = node_index < dirty.size() && dirty[node_index];
return {node.bounds_, update};
}
const BoundsMergeInfo info_0 = merge_child_bounds(nodes, dirty, node.children_offset_ + 0);
const BoundsMergeInfo info_1 = merge_child_bounds(nodes, dirty, 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);
}
return {node.bounds_, update};
}
void flush_bounds_to_parents(Tree &pbvh)
{
std::visit(
[&](auto &nodes) {
nodes.first().bounds_ =
merge_child_bounds(nodes.as_mutable_span(), pbvh.bounds_dirty_, 0).bounds;
},
pbvh.nodes_);
pbvh.bounds_dirty_.clear_and_shrink();
}
void update_bounds_mesh(const Span<float3> vert_positions, Tree &pbvh)
{
IndexMaskMemory memory;
const IndexMask nodes_to_update = IndexMask::from_bits(pbvh.bounds_dirty_, memory);
if (nodes_to_update.is_empty()) {
return;
}
MutableSpan<MeshNode> nodes = pbvh.nodes<MeshNode>();
nodes_to_update.foreach_index(
GrainSize(1), [&](const int i) { update_node_bounds_mesh(vert_positions, nodes[i]); });
flush_bounds_to_parents(pbvh);
}
void update_bounds_grids(const CCGKey &key, const Span<float3> positions, Tree &pbvh)
{
IndexMaskMemory memory;
const IndexMask nodes_to_update = IndexMask::from_bits(pbvh.bounds_dirty_, memory);
if (nodes_to_update.is_empty()) {
return;
}
MutableSpan<GridsNode> nodes = pbvh.nodes<GridsNode>();
nodes_to_update.foreach_index(GrainSize(1), [&](const int i) {
update_node_bounds_grids(key.grid_area, positions, nodes[i]);
});
flush_bounds_to_parents(pbvh);
}
void update_bounds_bmesh(const BMesh & /*bm*/, Tree &pbvh)
{
IndexMaskMemory memory;
const IndexMask nodes_to_update = IndexMask::from_bits(pbvh.bounds_dirty_, memory);
if (nodes_to_update.is_empty()) {
return;
}
MutableSpan<BMeshNode> nodes = pbvh.nodes<BMeshNode>();
nodes_to_update.foreach_index(GrainSize(1),
[&](const int i) { update_node_bounds_bmesh(nodes[i]); });
flush_bounds_to_parents(pbvh);
}
void update_bounds(const Depsgraph &depsgraph, const Object &object, Tree &pbvh)
{
switch (pbvh.type()) {
case Type::Mesh: {
const Span<float3> positions = bke::pbvh::vert_positions_eval(depsgraph, object);
update_bounds_mesh(positions, pbvh);
break;
}
case Type::Grids: {
const SculptSession &ss = *object.sculpt;
const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
update_bounds_grids(key, subdiv_ccg.positions, pbvh);
break;
}
case Type::BMesh: {
const SculptSession &ss = *object.sculpt;
update_bounds_bmesh(*ss.bm, pbvh);
break;
}
}
}
void store_bounds_orig(Tree &pbvh)
{
std::visit(
[](auto &nodes) {
threading::parallel_for(nodes.index_range(), 256, [&](const IndexRange range) {
for (const int i : range) {
nodes[i].bounds_orig_ = nodes[i].bounds_;
}
});
},
pbvh.nodes_);
}
void node_update_mask_mesh(const Span<float> mask, MeshNode &node)
{
const Span<int> verts = node.all_verts();
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);
}
void update_mask_mesh(const Mesh &mesh, const IndexMask &node_mask, Tree &pbvh)
{
const MutableSpan<MeshNode> nodes = pbvh.nodes<MeshNode>();
const AttributeAccessor attributes = mesh.attributes();
const VArraySpan<float> mask = *attributes.lookup<float>(".sculpt_mask", AttrDomain::Point);
if (mask.is_empty()) {
node_mask.foreach_index([&](const int i) {
nodes[i].flag_ &= ~PBVH_FullyMasked;
nodes[i].flag_ |= PBVH_FullyUnmasked;
});
return;
}
node_mask.foreach_index(GrainSize(1),
[&](const int i) { node_update_mask_mesh(mask, nodes[i]); });
}
void node_update_mask_grids(const CCGKey &key, const Span<float> masks, GridsNode &node)
{
bool fully_masked = true;
bool fully_unmasked = true;
for (const int grid : node.grids()) {
for (const float mask : masks.slice(bke::ccg::grid_range(key, grid))) {
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);
}
void update_mask_grids(const SubdivCCG &subdiv_ccg, const IndexMask &node_mask, Tree &pbvh)
{
const MutableSpan<GridsNode> nodes = pbvh.nodes<GridsNode>();
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
if (subdiv_ccg.masks.is_empty()) {
node_mask.foreach_index([&](const int i) {
nodes[i].flag_ &= ~PBVH_FullyMasked;
nodes[i].flag_ |= PBVH_FullyUnmasked;
});
return;
}
node_mask.foreach_index(
GrainSize(1), [&](const int i) { node_update_mask_grids(key, subdiv_ccg.masks, nodes[i]); });
}
void node_update_mask_bmesh(const int mask_offset, BMeshNode &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);
}
void update_mask_bmesh(const BMesh &bm, const IndexMask &node_mask, Tree &pbvh)
{
const MutableSpan<BMeshNode> nodes = pbvh.nodes<BMeshNode>();
const int offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
if (offset == -1) {
node_mask.foreach_index([&](const int i) {
nodes[i].flag_ &= ~PBVH_FullyMasked;
nodes[i].flag_ |= PBVH_FullyUnmasked;
});
return;
}
node_mask.foreach_index(GrainSize(1),
[&](const int i) { node_update_mask_bmesh(offset, nodes[i]); });
}
void node_update_visibility_mesh(const Span<bool> hide_vert, MeshNode &node)
{
BLI_assert(!hide_vert.is_empty());
const Span<int> verts = node.all_verts();
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);
}
static void update_visibility_faces(const Mesh &mesh,
const MutableSpan<MeshNode> nodes,
const IndexMask &node_mask)
{
const AttributeAccessor attributes = mesh.attributes();
const VArraySpan<bool> hide_vert = *attributes.lookup<bool>(".hide_vert", AttrDomain::Point);
if (hide_vert.is_empty()) {
node_mask.foreach_index([&](const int i) { nodes[i].flag_ &= ~PBVH_FullyHidden; });
return;
}
node_mask.foreach_index(GrainSize(1),
[&](const int i) { node_update_visibility_mesh(hide_vert, nodes[i]); });
}
void node_update_visibility_grids(const BitGroupVector<> &grid_hidden, GridsNode &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);
}
static void update_visibility_grids(const SubdivCCG &subdiv_ccg,
const MutableSpan<GridsNode> nodes,
const IndexMask &node_mask)
{
const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
if (grid_hidden.is_empty()) {
node_mask.foreach_index([&](const int i) { nodes[i].flag_ &= ~PBVH_FullyHidden; });
return;
}
node_mask.foreach_index(
GrainSize(1), [&](const int i) { node_update_visibility_grids(grid_hidden, nodes[i]); });
}
void node_update_visibility_bmesh(BMeshNode &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);
}
static void update_visibility_bmesh(const MutableSpan<BMeshNode> nodes, const IndexMask &node_mask)
{
node_mask.foreach_index(GrainSize(1),
[&](const int i) { node_update_visibility_bmesh(nodes[i]); });
}
void update_visibility(const Object &object, Tree &pbvh)
{
IndexMaskMemory memory;
const IndexMask node_mask = IndexMask::from_bits(pbvh.visibility_dirty_, memory);
if (node_mask.is_empty()) {
return;
}
pbvh.visibility_dirty_.clear_and_shrink();
switch (pbvh.type()) {
case Type::Mesh: {
const Mesh &mesh = *static_cast<const Mesh *>(object.data);
update_visibility_faces(mesh, pbvh.nodes<MeshNode>(), node_mask);
break;
}
case Type::Grids: {
const SculptSession &ss = *object.sculpt;
update_visibility_grids(*ss.subdiv_ccg, pbvh.nodes<GridsNode>(), node_mask);
break;
}
case Type::BMesh: {
update_visibility_bmesh(pbvh.nodes<BMeshNode>(), node_mask);
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(const blender::bke::pbvh::Tree &pbvh)
{
using namespace blender;
using namespace blender::bke::pbvh;
if (tree_is_empty(pbvh)) {
return {};
}
Bounds<float3> bounds = negative_bounds();
PBVHIter iter;
pbvh_iter_begin(&iter, const_cast<blender::bke::pbvh::Tree &>(pbvh), {});
Node *node;
while ((node = pbvh_iter_next(&iter, PBVH_Leaf))) {
if (node->flag_ & PBVH_UpdateRedraw) {
bounds = bounds::merge(bounds, node->bounds_);
}
}
return bounds;
}
namespace blender::bke::pbvh {
IndexMask nodes_to_face_selection_grids(const SubdivCCG &subdiv_ccg,
const Span<GridsNode> nodes,
const IndexMask &nodes_mask,
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);
nodes_mask.foreach_index(GrainSize(1), [&](const int i) {
for (const int grid : nodes[i].grids()) {
faces_to_update[grid_to_face_map[grid]] = true;
}
});
return IndexMask::from_bools(faces_to_update, memory);
}
Bounds<float3> bounds_get(const Tree &pbvh)
{
return std::visit(
[](auto &nodes) -> Bounds<float3> {
if (nodes.is_empty()) {
return float3(0);
}
return nodes.first().bounds_;
},
pbvh.nodes_);
}
} // namespace blender::bke::pbvh
int BKE_pbvh_get_grid_num_verts(const Object &object)
{
const SculptSession &ss = *object.sculpt;
BLI_assert(blender::bke::object::pbvh_get(object)->type() == blender::bke::pbvh::Type::Grids);
const CCGKey key = BKE_subdiv_ccg_key_top_level(*ss.subdiv_ccg);
return ss.subdiv_ccg->grids_num * key.grid_area;
}
int BKE_pbvh_get_grid_num_faces(const Object &object)
{
const SculptSession &ss = *object.sculpt;
BLI_assert(blender::bke::object::pbvh_get(object)->type() == blender::bke::pbvh::Type::Grids);
const CCGKey key = BKE_subdiv_ccg_key_top_level(*ss.subdiv_ccg);
return ss.subdiv_ccg->grids_num * square_i(key.grid_size - 1);
}
/***************************** Node Access ***********************************/
void BKE_pbvh_node_mark_update(blender::bke::pbvh::Node &node)
{
node.flag_ |= PBVH_RebuildPixels;
}
void BKE_pbvh_mark_rebuild_pixels(blender::bke::pbvh::Tree &pbvh)
{
std::visit(
[](auto &nodes) {
for (blender::bke::pbvh::Node &node : nodes) {
if (node.flag_ & PBVH_Leaf) {
node.flag_ |= PBVH_RebuildPixels;
}
}
},
pbvh.nodes_);
}
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_face_indices_calc_grids(const SubdivCCG &subdiv_ccg,
const GridsNode &node,
Vector<int> &faces)
{
faces.clear();
const Span<int> grid_to_face_map = subdiv_ccg.grid_to_face_map;
int prev_face = -1;
for (const int grid : node.grids()) {
const int face = grid_to_face_map[grid];
if (face != prev_face) {
faces.append(face);
prev_face = face;
}
}
return faces.as_span();
}
} // 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(const blender::bke::pbvh::BMeshNode &node,
blender::Span<blender::float3> &r_orig_positions,
blender::Span<blender::int3> &r_orig_tris)
{
r_orig_positions = node.orig_positions_;
r_orig_tris = node.orig_tris_;
}
/********************************* 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 float3 &ray_start,
const float3 &ray_normal,
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 float3 &ray_start,
const IsectRayPrecalc *isect_precalc,
const float3 &t0,
const float3 &t1,
const float3 &t2,
const float3 &t3,
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 float3 &ray_start,
const IsectRayPrecalc *isect_precalc,
const float3 &t0,
const float3 &t1,
const float3 &t2,
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 float3 &ray_origin,
const float3 &ray_direction,
const float3 &v0,
const float3 &v1,
const float3 &v2,
float3 &r_point,
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++) {
float3 point_test;
float 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 float3 &ray_start,
const float3 &ray_normal,
const float3 &t0,
const float3 &t1,
const float3 &t2,
const float3 &t3,
float *r_depth,
float *dist_sq)
{
float dist_sq_test;
float3 co;
float 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;
*r_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;
*r_depth = depth_test;
}
return true;
}
return false;
}
bool ray_face_nearest_tri(const float3 &ray_start,
const float3 &ray_normal,
const float3 &t0,
const float3 &t1,
const float3 &t2,
float *r_depth,
float *dist_sq)
{
float dist_sq_test;
float3 co;
float 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;
*r_depth = depth_test;
return true;
}
return false;
}
static void calc_mesh_intersect_data(const Span<int> corner_verts,
const Span<int3> corner_tris,
const float3 &ray_start,
const float3 &ray_normal,
const int face_index,
const int tri_index,
const std::array<const float *, 3> co,
const float *depth,
int &r_active_vertex,
int &r_active_face_index,
float3 &r_face_normal)
{
float3 nearest_vertex_co(0.0f);
normal_tri_v3(r_face_normal, co[0], co[1], co[2]);
const float3 location = ray_start + ray_normal * *depth;
for (int i = 0; i < co.size(); i++) {
/* 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 (i == 0 ||
len_squared_v3v3(location, co[i]) < len_squared_v3v3(location, nearest_vertex_co))
{
nearest_vertex_co = co[i];
r_active_vertex = corner_verts[corner_tris[tri_index][i]];
r_active_face_index = face_index;
}
}
}
bool node_raycast_mesh(const MeshNode &node,
const Span<float3> node_positions,
const Span<float3> vert_positions,
const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Span<int3> corner_tris,
const Span<bool> hide_poly,
const float3 &ray_start,
const float3 &ray_normal,
IsectRayPrecalc *isect_precalc,
float *depth,
int &r_active_vertex,
int &r_active_face_index,
float3 &r_face_normal)
{
const Span<int> face_indices = node.faces();
bool hit = false;
if (node_positions.is_empty()) {
for (const int i : face_indices.index_range()) {
const int face_i = face_indices[i];
if (!hide_poly.is_empty() && hide_poly[face_i]) {
continue;
}
for (const int tri_i : bke::mesh::face_triangles_range(faces, face_i)) {
const int3 &tri = corner_tris[tri_i];
const std::array<const float *, 3> co{{vert_positions[corner_verts[tri[0]]],
vert_positions[corner_verts[tri[1]]],
vert_positions[corner_verts[tri[2]]]}};
if (ray_face_intersection_tri(ray_start, isect_precalc, co[0], co[1], co[2], depth)) {
hit = true;
calc_mesh_intersect_data(corner_verts,
corner_tris,
ray_start,
ray_normal,
face_i,
tri_i,
co,
depth,
r_active_vertex,
r_active_face_index,
r_face_normal);
}
}
}
}
else {
const MeshNode::LocalVertMap &vert_map = node.vert_indices_;
for (const int i : face_indices.index_range()) {
const int face_i = face_indices[i];
if (!hide_poly.is_empty() && hide_poly[face_i]) {
continue;
}
for (const int tri_i : bke::mesh::face_triangles_range(faces, face_i)) {
const int3 &tri = corner_tris[tri_i];
const std::array<const float *, 3> co{
{node_positions[vert_map.index_of(corner_verts[tri[0]])],
node_positions[vert_map.index_of(corner_verts[tri[1]])],
node_positions[vert_map.index_of(corner_verts[tri[2]])]}};
if (ray_face_intersection_tri(ray_start, isect_precalc, co[0], co[1], co[2], depth)) {
hit = true;
calc_mesh_intersect_data(corner_verts,
corner_tris,
ray_start,
ray_normal,
face_i,
tri_i,
co,
depth,
r_active_vertex,
r_active_face_index,
r_face_normal);
}
}
}
}
return hit;
}
static void calc_grids_intersect_data(const float3 &ray_start,
const float3 &ray_normal,
const int grid,
const short x,
const short y,
const std::array<const float *, 4> co,
float *depth,
SubdivCCGCoord &r_active_vertex,
int &r_active_grid_index,
float3 &r_face_normal)
{
float3 nearest_vertex_co;
normal_quad_v3(r_face_normal, co[0], co[1], co[2], co[3]);
const float3 location = ray_start + ray_normal * *depth;
constexpr short x_it[4] = {0, 1, 1, 0};
constexpr short y_it[4] = {1, 1, 0, 0};
for (int i = 0; i < co.size(); i++) {
/* 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 (i == 0 ||
len_squared_v3v3(location, co[i]) < len_squared_v3v3(location, nearest_vertex_co))
{
copy_v3_v3(nearest_vertex_co, co[i]);
r_active_vertex = SubdivCCGCoord{grid, short(x + x_it[i]), short(y + y_it[i])};
}
}
r_active_grid_index = grid;
}
bool node_raycast_grids(const SubdivCCG &subdiv_ccg,
GridsNode &node,
const Span<float3> node_positions,
const float3 &ray_start,
const float3 &ray_normal,
const IsectRayPrecalc *isect_precalc,
float *depth,
SubdivCCGCoord &r_active_vertex,
int &r_active_grid_index,
float3 &r_face_normal)
{
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
const Span<int> grids = node.grids();
const int grid_size = key.grid_size;
bool hit = false;
const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
const Span<float3> positions = subdiv_ccg.positions;
if (node_positions.is_empty()) {
for (const int grid : grids) {
const Span<float3> grid_positions = positions.slice(bke::ccg::grid_range(key, grid));
for (const short y : IndexRange(grid_size - 1)) {
for (const short x : IndexRange(grid_size - 1)) {
if (!grid_hidden.is_empty()) {
if (paint_is_grid_face_hidden(grid_hidden[grid], grid_size, x, y)) {
continue;
}
}
const std::array<const float *, 4> co{
{grid_positions[CCG_grid_xy_to_index(grid_size, x, y + 1)],
grid_positions[CCG_grid_xy_to_index(grid_size, x + 1, y + 1)],
grid_positions[CCG_grid_xy_to_index(grid_size, x + 1, y)],
grid_positions[CCG_grid_xy_to_index(grid_size, x, y)]}};
if (ray_face_intersection_quad(
ray_start, isect_precalc, co[0], co[1], co[2], co[3], depth))
{
hit = true;
calc_grids_intersect_data(ray_start,
ray_normal,
grid,
x,
y,
co,
depth,
r_active_vertex,
r_active_grid_index,
r_face_normal);
}
}
}
}
}
else {
for (const int i : grids.index_range()) {
const int grid = grids[i];
const Span<float3> grid_positions = node_positions.slice(bke::ccg::grid_range(key, i));
for (const short y : IndexRange(grid_size - 1)) {
for (const short x : IndexRange(grid_size - 1)) {
if (!grid_hidden.is_empty()) {
if (paint_is_grid_face_hidden(grid_hidden[grid], grid_size, x, y)) {
continue;
}
}
const std::array<const float *, 4> co{grid_positions[(y + 1) * grid_size + x],
grid_positions[(y + 1) * grid_size + x + 1],
grid_positions[y * grid_size + x + 1],
grid_positions[y * grid_size + x]};
if (ray_face_intersection_quad(
ray_start, isect_precalc, co[0], co[1], co[2], co[3], depth))
{
hit = true;
calc_grids_intersect_data(ray_start,
ray_normal,
grid,
x,
y,
co,
depth,
r_active_vertex,
r_active_grid_index,
r_face_normal);
}
}
}
}
}
return hit;
}
void clip_ray_ortho(
Tree &pbvh, bool original, float ray_start[3], float ray_end[3], float ray_normal[3])
{
if (tree_is_empty(pbvh)) {
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(&first_node(pbvh));
}
else {
bb_root = node_bounds(first_node(pbvh));
}
/* 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 float3 &ray_start,
const float3 &ray_normal,
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(const MeshNode &node,
const Span<float3> node_positions,
const Span<float3> vert_positions,
const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Span<int3> corner_tris,
const Span<bool> hide_poly,
const float3 &ray_start,
const float3 &ray_normal,
float *r_depth,
float *dist_sq)
{
const Span<int> face_indices = node.faces();
bool hit = false;
if (node_positions.is_empty()) {
for (const int i : face_indices.index_range()) {
const int face_i = face_indices[i];
if (!hide_poly.is_empty() && hide_poly[face_i]) {
continue;
}
for (const int tri_i : bke::mesh::face_triangles_range(faces, face_i)) {
const int3 &corner_tri = corner_tris[tri_i];
hit |= ray_face_nearest_tri(ray_start,
ray_normal,
vert_positions[corner_verts[corner_tri[0]]],
vert_positions[corner_verts[corner_tri[1]]],
vert_positions[corner_verts[corner_tri[2]]],
r_depth,
dist_sq);
}
}
}
else {
const MeshNode::LocalVertMap &vert_map = node.vert_indices_;
for (const int i : face_indices.index_range()) {
const int face_i = face_indices[i];
if (!hide_poly.is_empty() && hide_poly[face_i]) {
continue;
}
for (const int tri_i : bke::mesh::face_triangles_range(faces, face_i)) {
const int3 &corner_tri = corner_tris[tri_i];
hit |= ray_face_nearest_tri(ray_start,
ray_normal,
node_positions[vert_map.index_of(corner_verts[corner_tri[0]])],
node_positions[vert_map.index_of(corner_verts[corner_tri[1]])],
node_positions[vert_map.index_of(corner_verts[corner_tri[2]])],
r_depth,
dist_sq);
}
}
}
return hit;
}
static bool pbvh_grids_node_nearest_to_ray(const SubdivCCG &subdiv_ccg,
GridsNode &node,
const Span<float3> node_positions,
const float ray_start[3],
const float ray_normal[3],
float *r_depth,
float *dist_sq)
{
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
const Span<int> grids = node.grids();
const int grid_size = key.grid_size;
const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
const Span<float3> positions = subdiv_ccg.positions;
bool hit = false;
if (node_positions.is_empty()) {
for (const int grid : grids) {
const Span<float3> grid_positions = positions.slice(bke::ccg::grid_range(key, grid));
for (const short y : IndexRange(grid_size - 1)) {
for (const short x : IndexRange(grid_size - 1)) {
if (!grid_hidden.is_empty()) {
if (paint_is_grid_face_hidden(grid_hidden[grid], grid_size, x, y)) {
continue;
}
}
hit |= ray_face_nearest_quad(
ray_start,
ray_normal,
grid_positions[CCG_grid_xy_to_index(grid_size, x, y)],
grid_positions[CCG_grid_xy_to_index(grid_size, x + 1, y)],
grid_positions[CCG_grid_xy_to_index(grid_size, x + 1, y + 1)],
grid_positions[CCG_grid_xy_to_index(grid_size, x, y + 1)],
r_depth,
dist_sq);
}
}
}
}
else {
for (const int i : grids.index_range()) {
const int grid = grids[i];
const Span<float3> grid_positions = node_positions.slice(bke::ccg::grid_range(key, i));
for (const short y : IndexRange(grid_size - 1)) {
for (const short x : IndexRange(grid_size - 1)) {
if (!grid_hidden.is_empty()) {
if (paint_is_grid_face_hidden(grid_hidden[grid], grid_size, x, y)) {
continue;
}
}
hit |= ray_face_nearest_quad(ray_start,
ray_normal,
grid_positions[y * grid_size + x],
grid_positions[y * grid_size + x + 1],
grid_positions[(y + 1) * grid_size + x + 1],
grid_positions[(y + 1) * grid_size + x],
r_depth,
dist_sq);
}
}
}
}
return hit;
}
bool find_nearest_to_ray_node(Tree &pbvh,
Node &node,
const Span<float3> node_positions,
bool use_origco,
const Span<float3> vert_positions,
const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Span<int3> corner_tris,
const Span<bool> hide_poly,
const SubdivCCG *subdiv_ccg,
const float ray_start[3],
const float ray_normal[3],
float *depth,
float *dist_sq)
{
if (node.flag_ & PBVH_FullyHidden) {
return false;
}
switch (pbvh.type()) {
case Type::Mesh:
return pbvh_faces_node_nearest_to_ray(static_cast<MeshNode &>(node),
node_positions,
vert_positions,
faces,
corner_verts,
corner_tris,
hide_poly,
ray_start,
ray_normal,
depth,
dist_sq);
case Type::Grids:
return pbvh_grids_node_nearest_to_ray(*subdiv_ccg,
static_cast<GridsNode &>(node),
node_positions,
ray_start,
ray_normal,
depth,
dist_sq);
case Type::BMesh:
return bmesh_node_nearest_to_ray(
static_cast<BMeshNode &>(node), ray_start, ray_normal, depth, dist_sq, use_origco);
}
BLI_assert_unreachable();
return false;
}
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;
}
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;
std::visit(
[&](auto &nodes) {
for (blender::bke::pbvh::Node &node : nodes) {
if (node.flag_ & PBVH_TexLeaf) {
flag = PBVH_TexLeaf;
break;
}
}
for (blender::bke::pbvh::Node &node : nodes) {
if (!(node.flag_ & flag)) {
continue;
}
draw_fn(&node, user_data, node.bounds_.min, node.bounds_.max, node.flag_);
}
},
pbvh.nodes_);
}
void BKE_pbvh_vert_coords_apply(blender::bke::pbvh::Tree &pbvh,
const blender::Span<blender::float3> vert_positions)
{
using namespace blender::bke::pbvh;
pbvh.tag_positions_changed(blender::IndexRange(pbvh.nodes_num()));
update_bounds_mesh(vert_positions, pbvh);
store_bounds_orig(pbvh);
}
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]);
}
}
static Span<float3> vert_positions_eval(const Object &object_orig, const Object &object_eval)
{
const SculptSession &ss = *object_orig.sculpt;
const Mesh &mesh_orig = *static_cast<const Mesh *>(object_orig.data);
BLI_assert(bke::object::pbvh_get(object_orig)->type() == Type::Mesh);
if (object_orig.mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
if (const Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(&object_eval)) {
if (mesh_topology_count_matches(*mesh_eval, mesh_orig)) {
return mesh_eval->vert_positions();
}
}
if (const Mesh *mesh_eval = BKE_object_get_mesh_deform_eval(&object_eval)) {
return mesh_eval->vert_positions();
}
}
if (!ss.deform_cos.is_empty()) {
BLI_assert(ss.deform_cos.size() == mesh_orig.verts_num);
return ss.deform_cos;
}
return mesh_orig.vert_positions();
}
static MutableSpan<float3> vert_positions_eval_for_write(Object &object_orig, Object &object_eval)
{
SculptSession &ss = *object_orig.sculpt;
Mesh &mesh_orig = *static_cast<Mesh *>(object_orig.data);
BLI_assert(bke::object::pbvh_get(object_orig)->type() == Type::Mesh);
if (object_orig.mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
if (const Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(&object_eval)) {
if (mesh_topology_count_matches(*mesh_eval, mesh_orig)) {
Mesh *mesh_eval_mut = const_cast<Mesh *>(mesh_eval);
return mesh_eval_mut->vert_positions_for_write();
}
}
if (const Mesh *mesh_eval = BKE_object_get_mesh_deform_eval(&object_eval)) {
Mesh *mesh_eval_mut = const_cast<Mesh *>(mesh_eval);
return mesh_eval_mut->vert_positions_for_write();
}
}
if (!ss.deform_cos.is_empty()) {
BLI_assert(ss.deform_cos.size() == mesh_orig.verts_num);
return ss.deform_cos;
}
return mesh_orig.vert_positions_for_write();
}
Span<float3> vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
{
const Object &object_eval = *DEG_get_evaluated_object(&depsgraph,
&const_cast<Object &>(object_orig));
return vert_positions_eval(object_orig, object_eval);
}
Span<float3> vert_positions_eval_from_eval(const Object &object_eval)
{
BLI_assert(!DEG_is_original_object(&object_eval));
const Object &object_orig = *DEG_get_original_object(&const_cast<Object &>(object_eval));
return vert_positions_eval(object_orig, object_eval);
}
MutableSpan<float3> vert_positions_eval_for_write(const Depsgraph &depsgraph, Object &object_orig)
{
Object &object_eval = *DEG_get_evaluated_object(&depsgraph, &object_orig);
return vert_positions_eval_for_write(object_orig, object_eval);
}
Span<float3> vert_normals_eval(const Depsgraph &depsgraph, const Object &object_orig)
{
const Object &object_eval = *DEG_get_evaluated_object(&depsgraph,
&const_cast<Object &>(object_orig));
return vert_normals_cache_eval(object_orig, object_eval).data();
}
Span<float3> vert_normals_eval_from_eval(const Object &object_eval)
{
BLI_assert(!DEG_is_original_object(&object_eval));
Object &object_orig = *DEG_get_original_object(&const_cast<Object &>(object_eval));
return vert_normals_cache_eval(object_orig, object_eval).data();
}
Span<float3> face_normals_eval_from_eval(const Object &object_eval)
{
BLI_assert(!DEG_is_original_object(&object_eval));
Object &object_orig = *DEG_get_original_object(&const_cast<Object &>(object_eval));
return face_normals_cache_eval(object_orig, object_eval).data();
}
} // namespace blender::bke::pbvh
int BKE_pbvh_debug_draw_gen_get(blender::bke::pbvh::Node &node)
{
return node.debug_draw_gen_;
}
void BKE_pbvh_sync_visibility_from_verts(Object &object)
{
using namespace blender;
using namespace blender::bke;
const SculptSession &ss = *object.sculpt;
switch (object::pbvh_get(object)->type()) {
case blender::bke::pbvh::Type::Mesh: {
Mesh &mesh = *static_cast<Mesh *>(object.data);
mesh_hide_vert_flush(mesh);
break;
}
case blender::bke::pbvh::Type::BMesh: {
BMesh &bm = *ss.bm;
BMIter iter;
BMVert *v;
BMEdge *e;
BMFace *f;
BM_ITER_MESH (f, &iter, &bm, BM_FACES_OF_MESH) {
BM_elem_flag_disable(f, BM_ELEM_HIDDEN);
}
BM_ITER_MESH (e, &iter, &bm, BM_EDGES_OF_MESH) {
BM_elem_flag_disable(e, BM_ELEM_HIDDEN);
}
BM_ITER_MESH (v, &iter, &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: {
Mesh &mesh = *static_cast<Mesh *>(object.data);
const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
const OffsetIndices faces = mesh.faces();
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 {
IndexMask all_leaf_nodes(const Tree &pbvh, IndexMaskMemory &memory)
{
return std::visit(
[&](const auto &nodes) {
return IndexMask::from_predicate(
nodes.index_range(), GrainSize(1024), memory, [&](const int i) {
return (nodes[i].flag_ & PBVH_Leaf) != 0;
});
},
pbvh.nodes_);
}
static Vector<Node *> search_gather(Tree &pbvh,
const FunctionRef<bool(Node &)> scb,
PBVHNodeFlags leaf_flag)
{
if (tree_is_empty(pbvh)) {
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);
}
}
return nodes;
}
IndexMask search_nodes(const Tree &pbvh,
IndexMaskMemory &memory,
FunctionRef<bool(const Node &)> filter_fn)
{
Vector<Node *> nodes = search_gather(
const_cast<Tree &>(pbvh), [&](Node &node) { return filter_fn(node); }, PBVH_Leaf);
Array<int> indices(nodes.size());
std::visit(
[&](const auto &pbvh_nodes) {
using VectorT = std::decay_t<decltype(pbvh_nodes)>;
for (const int i : nodes.index_range()) {
indices[i] = static_cast<typename VectorT::value_type *>(nodes[i]) - pbvh_nodes.data();
}
},
pbvh.nodes_);
std::sort(indices.begin(), indices.end());
return IndexMask::from_indices(indices.as_span(), memory);
}
} // namespace blender::bke::pbvh