17#include <fmt/format.h>
21# include <openvdb/Grid.h>
22# include <openvdb/math/Transform.h>
23# include <openvdb/tools/Merge.h>
31namespace grid = bke::volume_grid;
35static std::optional<VolumeGridType> cpp_type_to_grid_type(
const CPPType &cpp_type)
58BLI_NOINLINE static void process_leaf_node(
const mf::MultiFunction &fn,
59 const Span<bke::SocketValueVariant *> input_values,
60 const Span<const openvdb::GridBase *> input_grids,
61 MutableSpan<openvdb::GridBase::Ptr> output_grids,
62 const openvdb::math::Transform &
transform,
63 const grid::LeafNodeMask &leaf_node_mask,
64 const openvdb::CoordBBox &leaf_bbox,
65 const grid::GetVoxelsFn get_voxels_fn)
68 IndexMaskMemory memory;
70 IndexRange(grid::LeafNodeMask::SIZE),
71 GrainSize(grid::LeafNodeMask::SIZE),
73 [&](
const int64_t i) {
return leaf_node_mask.isOn(
i); });
75 AlignedBuffer<8192, 8> allocation_buffer;
77 scope.
allocator().provide_buffer(allocation_buffer);
78 mf::ParamsBuilder
params{fn, &index_mask};
83 const openvdb::Coord any_voxel_in_leaf = leaf_bbox.min();
85 std::optional<MutableSpan<openvdb::Coord>> voxel_coords_opt;
86 auto ensure_voxel_coords = [&]() {
87 if (!voxel_coords_opt.has_value()) {
88 voxel_coords_opt = scope.
allocator().allocate_array<openvdb::Coord>(
90 get_voxels_fn(voxel_coords_opt.value());
92 return *voxel_coords_opt;
95 for (
const int input_i : input_values.
index_range()) {
96 const bke::SocketValueVariant &value_variant = *input_values[input_i];
97 const mf::ParamType param_type = fn.param_type(
params.next_param_index());
98 const CPPType ¶m_cpp_type = param_type.data_type().single_type();
100 if (
const openvdb::GridBase *grid_base = input_grids[input_i]) {
102 grid::to_typed_grid(*grid_base, [&](
const auto &
grid) {
103 using GridT =
typename std::decay_t<
decltype(
grid)>;
104 using ValueT =
typename GridT::ValueType;
108 if (
const auto *leaf_node =
tree.probeLeaf(any_voxel_in_leaf)) {
111 if constexpr (std::is_same_v<ValueT, bool>) {
112 const Span<openvdb::Coord> voxels = ensure_voxel_coords();
113 MutableSpan<bool> values = scope.
allocator().allocate_array<
bool>(
116 const openvdb::Coord &coord = voxels[
i];
117 values[
i] =
tree.getValue(coord);
119 params.add_readonly_single_input(values);
122 const Span<ValueT> values(leaf_node->buffer().data(), grid::LeafNodeMask::SIZE);
123 const grid::LeafNodeMask &input_leaf_mask = leaf_node->valueMask();
124 const grid::LeafNodeMask missing_mask = leaf_node_mask & !input_leaf_mask;
125 if (missing_mask.isOff()) {
127 params.add_readonly_single_input(
128 GSpan(param_cpp_type, values.
data(), values.
size()));
132 MutableSpan copied_values = scope.
allocator().construct_array_copy(values);
133 const auto &background =
tree.background();
134 for (
auto missing_it = missing_mask.beginOn(); missing_it.test(); ++missing_it) {
135 const int index = missing_it.pos();
136 copied_values[index] = background;
138 params.add_readonly_single_input(
139 GSpan(param_cpp_type, copied_values.
data(), copied_values.
size()));
147 const auto &single_value =
tree.getValue(any_voxel_in_leaf);
148 params.add_readonly_single_input(GPointer(param_cpp_type, &single_value));
152 else if (value_variant.is_context_dependent_field()) {
155 const fn::GField field = value_variant.get<fn::GField>();
156 const CPPType &type = field.cpp_type();
157 const Span<openvdb::Coord> voxels = ensure_voxel_coords();
158 bke::VoxelFieldContext field_context{
transform, voxels};
159 fn::FieldEvaluator evaluator{field_context, &index_mask};
162 evaluator.add_with_destination(field, values);
163 evaluator.evaluate();
164 params.add_readonly_single_input(values);
168 params.add_readonly_single_input(value_variant.get_single_ptr());
172 for (
const int output_i : output_grids.
index_range()) {
173 const mf::ParamType param_type = fn.param_type(
params.next_param_index());
174 const CPPType ¶m_cpp_type = param_type.data_type().single_type();
175 if (!output_grids[output_i]) {
176 params.add_ignored_single_output();
180 openvdb::GridBase &grid_base = *output_grids[output_i];
181 grid::to_typed_grid(grid_base, [&](
auto &
grid) {
182 using GridT =
typename std::decay_t<
decltype(
grid)>;
183 using ValueT =
typename GridT::ValueType;
186 auto *leaf_node =
tree.probeLeaf(any_voxel_in_leaf);
191 if constexpr (std::is_same_v<ValueT, bool>) {
192 MutableSpan<bool> values = scope.
allocator().allocate_array<
bool>(
194 params.add_uninitialized_single_output(values);
198 ValueT *values = leaf_node->buffer().
data();
199 params.add_uninitialized_single_output(
200 GMutableSpan(param_cpp_type, values, grid::LeafNodeMask::SIZE));
207 fn.call_auto(index_mask,
params, context);
209 for (
const int output_i : output_grids.
index_range()) {
210 const int param_index = input_values.
size() + output_i;
211 const mf::ParamType param_type = fn.param_type(param_index);
212 const CPPType ¶m_cpp_type = param_type.data_type().single_type();
213 if (!param_cpp_type.
is<
bool>()) {
216 grid::set_mask_leaf_buffer_from_bools(
217 static_cast<openvdb::BoolGrid &
>(*output_grids[output_i]),
218 params.computed_array(param_index).typed<
bool>(),
220 ensure_voxel_coords());
235BLI_NOINLINE static void process_voxels(
const mf::MultiFunction &fn,
236 const Span<bke::SocketValueVariant *> input_values,
237 const Span<const openvdb::GridBase *> input_grids,
238 MutableSpan<openvdb::GridBase::Ptr> output_grids,
239 const openvdb::math::Transform &
transform,
240 const Span<openvdb::Coord> voxels)
243 const IndexMask index_mask{voxels_num};
244 AlignedBuffer<8192, 8> allocation_buffer;
246 scope.
allocator().provide_buffer(allocation_buffer);
247 mf::ParamsBuilder
params{fn, &index_mask};
250 for (
const int input_i : input_values.
index_range()) {
251 const bke::SocketValueVariant &value_variant = *input_values[input_i];
252 const mf::ParamType param_type = fn.param_type(
params.next_param_index());
253 const CPPType ¶m_cpp_type = param_type.data_type().single_type();
255 if (
const openvdb::GridBase *grid_base = input_grids[input_i]) {
257 grid::to_typed_grid(*grid_base, [&](
const auto &
grid) {
258 using ValueType =
typename std::decay_t<
decltype(
grid)>::ValueType;
263 auto accessor =
grid.getConstUnsafeAccessor();
265 MutableSpan<ValueType> values = scope.
allocator().allocate_array<ValueType>(voxels_num);
266 for (
const int64_t i : IndexRange(voxels_num)) {
267 const openvdb::Coord &coord = voxels[
i];
268 values[
i] =
tree.getValue(coord, accessor);
271 params.add_readonly_single_input(GSpan(param_cpp_type, values.
data(), voxels_num));
274 else if (value_variant.is_context_dependent_field()) {
277 const fn::GField field = value_variant.get<fn::GField>();
278 const CPPType &type = field.cpp_type();
279 bke::VoxelFieldContext field_context{
transform, voxels};
280 fn::FieldEvaluator evaluator{field_context, voxels_num};
281 GMutableSpan values{type, scope.
allocator().allocate_array(type, voxels_num), voxels_num};
282 evaluator.add_with_destination(field, values);
283 evaluator.evaluate();
284 params.add_readonly_single_input(values);
288 params.add_readonly_single_input(value_variant.get_single_ptr());
294 for ([[maybe_unused]]
const int output_i : output_grids.
index_range()) {
295 const int param_index = input_values.
size() + output_i;
296 const mf::ParamType param_type = fn.param_type(param_index);
297 const CPPType &type = param_type.data_type().single_type();
298 void *buffer = scope.
allocator().allocate_array(type, voxels_num);
299 params.add_uninitialized_single_output(GMutableSpan{type, buffer, voxels_num});
303 fn.call_auto(index_mask,
params, context);
306 for (
const int output_i : output_grids.
index_range()) {
307 if (!output_grids[output_i]) {
310 const int param_index = input_values.
size() + output_i;
311 grid::set_grid_values(*output_grids[output_i],
params.computed_array(param_index), voxels);
327BLI_NOINLINE static void process_tiles(
const mf::MultiFunction &fn,
328 const Span<bke::SocketValueVariant *> input_values,
329 const Span<const openvdb::GridBase *> input_grids,
330 MutableSpan<openvdb::GridBase::Ptr> output_grids,
331 const openvdb::math::Transform &
transform,
332 const Span<openvdb::CoordBBox>
tiles)
335 const IndexMask index_mask{tiles_num};
337 AlignedBuffer<8192, 8> allocation_buffer;
339 scope.
allocator().provide_buffer(allocation_buffer);
340 mf::ParamsBuilder
params{fn, &index_mask};
343 for (
const int input_i : input_values.
index_range()) {
344 const bke::SocketValueVariant &value_variant = *input_values[input_i];
345 const mf::ParamType param_type = fn.param_type(
params.next_param_index());
346 const CPPType ¶m_cpp_type = param_type.data_type().single_type();
348 if (
const openvdb::GridBase *grid_base = input_grids[input_i]) {
350 grid::to_typed_grid(*grid_base, [&](
const auto &
grid) {
351 using GridT = std::decay_t<
decltype(
grid)>;
352 using ValueType =
typename GridT::ValueType;
354 auto accessor =
grid.getConstUnsafeAccessor();
356 MutableSpan<ValueType> values = scope.
allocator().allocate_array<ValueType>(tiles_num);
357 for (
const int64_t i : IndexRange(tiles_num)) {
361 const openvdb::Coord any_coord_in_tile =
tile.min();
362 values[
i] =
tree.getValue(any_coord_in_tile, accessor);
365 params.add_readonly_single_input(GSpan(param_cpp_type, values.
data(), tiles_num));
368 else if (value_variant.is_context_dependent_field()) {
371 const fn::GField field = value_variant.get<fn::GField>();
372 const CPPType &type = field.cpp_type();
374 fn::FieldEvaluator evaluator{field_context, tiles_num};
375 GMutableSpan values{type, scope.
allocator().allocate_array(type, tiles_num), tiles_num};
376 evaluator.add_with_destination(field, values);
377 evaluator.evaluate();
378 params.add_readonly_single_input(values);
382 params.add_readonly_single_input(value_variant.get_single_ptr());
388 for ([[maybe_unused]]
const int output_i : output_grids.
index_range()) {
389 if (!output_grids[output_i]) {
390 params.add_ignored_single_output();
393 const int param_index = input_values.
size() + output_i;
394 const mf::ParamType param_type = fn.param_type(param_index);
395 const CPPType &type = param_type.data_type().single_type();
396 void *buffer = scope.
allocator().allocate_array(type, tiles_num);
397 params.add_uninitialized_single_output(GMutableSpan{type, buffer, tiles_num});
401 fn.call_auto(index_mask,
params, context);
404 for (
const int output_i : output_grids.
index_range()) {
405 if (!output_grids[output_i]) {
408 const int param_index = input_values.
size() + output_i;
409 grid::set_tile_values(*output_grids[output_i],
params.computed_array(param_index),
tiles);
413BLI_NOINLINE static void process_background(
const mf::MultiFunction &fn,
414 const Span<bke::SocketValueVariant *> input_values,
415 const Span<const openvdb::GridBase *> input_grids,
416 const openvdb::math::Transform &
transform,
417 MutableSpan<openvdb::GridBase::Ptr> output_grids)
419 AlignedBuffer<160, 8> allocation_buffer;
421 scope.
allocator().provide_buffer(allocation_buffer);
423 const IndexMask
mask(1);
427 for (
const int input_i : input_values.
index_range()) {
428 const bke::SocketValueVariant &value_variant = *input_values[input_i];
429 const mf::ParamType param_type = fn.param_type(
params.next_param_index());
430 const CPPType ¶m_cpp_type = param_type.data_type().single_type();
432 if (
const openvdb::GridBase *grid_base = input_grids[input_i]) {
433 grid::to_typed_grid(*grid_base, [&](
const auto &
grid) {
435 using GridT = std::decay_t<
decltype(
grid)>;
436 using ValueType =
typename GridT::ValueType;
440 params.add_readonly_single_input(GPointer(param_cpp_type, &
tree.background()));
445 if (value_variant.is_context_dependent_field()) {
446 const fn::GField field = value_variant.get<fn::GField>();
447 const CPPType &type = field.cpp_type();
448 static const openvdb::CoordBBox background_space = openvdb::CoordBBox::inf();
449 bke::TilesFieldContext field_context(
transform,
450 Span<openvdb::CoordBBox>(&background_space, 1));
451 fn::FieldEvaluator evaluator(field_context, 1);
452 GMutableSpan value(type, scope.
allocator().allocate(type), 1);
453 evaluator.add_with_destination(field, value);
454 evaluator.evaluate();
455 params.add_readonly_single_input(GPointer(type, value.data()));
459 params.add_readonly_single_input(value_variant.get_single_ptr());
462 for ([[maybe_unused]]
const int output_i : output_grids.
index_range()) {
463 if (!output_grids[output_i]) {
464 params.add_ignored_single_output();
467 const int param_index = input_values.
size() + output_i;
468 const mf::ParamType param_type = fn.param_type(param_index);
469 const CPPType &type = param_type.data_type().single_type();
471 GMutableSpan value_buffer(type, scope.
allocator().allocate(type), 1);
472 params.add_uninitialized_single_output(value_buffer);
477 for ([[maybe_unused]]
const int output_i : output_grids.
index_range()) {
478 if (!output_grids[output_i]) {
481 const int param_index = input_values.
size() + output_i;
482 const GSpan value =
params.computed_array(param_index);
483 grid::set_grid_background(*output_grids[output_i], GPointer(value.type(), value.data()));
488 const mf::MultiFunction &fn,
489 const Span<bke::SocketValueVariant *> input_values,
490 const Span<bke::SocketValueVariant *> output_values,
491 std::string &r_error_message)
493 const int inputs_num = input_values.
size();
494 Array<bke::VolumeTreeAccessToken> input_volume_tokens(inputs_num);
495 Array<const openvdb::GridBase *> input_grids(inputs_num,
nullptr);
497 for (
const int input_i : IndexRange(inputs_num)) {
498 bke::SocketValueVariant &value_variant = *input_values[input_i];
499 if (value_variant.is_volume_grid()) {
500 const bke::GVolumeGrid g_volume_grid = value_variant.get<bke::GVolumeGrid>();
501 input_grids[input_i] = &g_volume_grid->grid(input_volume_tokens[input_i]);
503 else if (value_variant.is_context_dependent_field()) {
507 value_variant.convert_to_single();
511 const openvdb::math::Transform *
transform =
nullptr;
512 for (
const openvdb::GridBase *
grid : input_grids) {
516 const openvdb::math::Transform &other_transform =
grid->transform();
522 r_error_message =
TIP_(
"Input grids have incompatible transforms");
527 r_error_message =
TIP_(
"No input grid found that can determine the topology");
531 openvdb::MaskTree mask_tree;
532 for (
const openvdb::GridBase *
grid : input_grids) {
536 grid::to_typed_grid(*
grid, [&](
const auto &
grid) { mask_tree.topologyUnion(
grid.tree()); });
539 Array<openvdb::GridBase::Ptr> output_grids(output_values.
size());
541 if (!output_values[
i]) {
544 const int param_index = input_values.
size() +
i;
545 const mf::ParamType param_type = fn.param_type(param_index);
546 const CPPType &cpp_type = param_type.data_type().single_type();
547 const std::optional<VolumeGridType> grid_type = cpp_type_to_grid_type(cpp_type);
549 r_error_message =
TIP_(
"Grid type not supported");
553 output_grids[
i] = grid::create_grid_with_topology(mask_tree, *
transform, *grid_type);
556 grid::parallel_grid_topology_tasks(
558 [&](
const grid::LeafNodeMask &leaf_node_mask,
559 const openvdb::CoordBBox &leaf_bbox,
560 const grid::GetVoxelsFn get_voxels_fn) {
561 process_leaf_node(fn,
570 [&](
const Span<openvdb::Coord> voxels) {
571 process_voxels(fn, input_values, input_grids, output_grids, *
transform, voxels);
573 [&](
const Span<openvdb::CoordBBox>
tiles) {
574 process_tiles(fn, input_values, input_grids, output_grids, *
transform,
tiles);
577 process_background(fn, input_values, input_grids, *
transform, output_grids);
580 if (bke::SocketValueVariant *output_value = output_values[
i]) {
581 output_value->set(bke::GVolumeGrid(std::move(output_grids[
i])));
591 const mf::MultiFunction & ,
594 std::string &r_error_message)
596 r_error_message =
TIP_(
"Compiled without OpenVDB");
CustomData interface, see also DNA_customdata_types.h.
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
int64_t min_array_size() const
void foreach_index(Fn &&fn) const
constexpr int64_t size() const
constexpr T * data() const
constexpr IndexRange index_range() const
LinearAllocator & allocator()
constexpr int64_t size() const
constexpr IndexRange index_range() const
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
ccl_gpu_kernel_postfix ccl_global KernelWorkTile * tiles
const ccl_global KernelWorkTile * tile
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
std::optional< VolumeGridType > custom_data_type_to_volume_grid_type(eCustomDataType type)
int context(const bContext *C, const char *member, bContextDataResult *result)
bool execute_multi_function_on_value_variant__volume_grid(const mf::MultiFunction &, const Span< bke::SocketValueVariant * >, const Span< bke::SocketValueVariant * >, std::string &r_error_message)