WIP: Volume grid attribute support in geometry nodes #110044
|
@ -168,11 +168,11 @@ class GeometryFieldInput : public fn::FieldInput {
|
|||
ResourceScope &scope) const override;
|
||||
virtual GVArray get_varray_for_context(const GeometryFieldContext &context,
|
||||
const IndexMask &mask) const = 0;
|
||||
virtual fn::VolumeGrid get_volume_grid_for_context(const fn::FieldContext &context,
|
||||
const fn::VolumeMask &mask,
|
||||
ResourceScope &scope) const override;
|
||||
virtual fn::VolumeGrid get_volume_grid_for_context(const GeometryFieldContext & /*context*/,
|
||||
const fn::VolumeMask & /*mask*/) const
|
||||
virtual volume::VolumeGrid get_volume_grid_for_context(const fn::FieldContext &context,
|
||||
const volume::VolumeMask &mask,
|
||||
ResourceScope &scope) const override;
|
||||
virtual volume::VolumeGrid get_volume_grid_for_context(const GeometryFieldContext & /*context*/,
|
||||
const volume::VolumeMask & /*mask*/) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
@ -225,6 +225,9 @@ class InstancesFieldInput : public fn::FieldInput {
|
|||
|
||||
class VolumeFieldInput : public fn::FieldInput {
|
||||
public:
|
||||
using VolumeGrid = volume::VolumeGrid;
|
||||
using VolumeMask = volume::VolumeMask;
|
||||
|
||||
using fn::FieldInput::FieldInput;
|
||||
GVArray get_varray_for_context(const fn::FieldContext & /*context*/,
|
||||
const IndexMask & /*mask*/,
|
||||
|
@ -232,11 +235,11 @@ class VolumeFieldInput : public fn::FieldInput {
|
|||
{
|
||||
return {};
|
||||
}
|
||||
virtual fn::VolumeGrid get_volume_grid_for_context(const fn::FieldContext &context,
|
||||
const fn::VolumeMask &mask,
|
||||
ResourceScope &scope) const override;
|
||||
virtual fn::VolumeGrid get_volume_grid_for_context(const VolumeGridVector &grids,
|
||||
const fn::VolumeMask &mask) const = 0;
|
||||
virtual VolumeGrid get_volume_grid_for_context(const fn::FieldContext &context,
|
||||
const VolumeMask &mask,
|
||||
ResourceScope &scope) const override;
|
||||
virtual VolumeGrid get_volume_grid_for_context(const VolumeGridVector &grids,
|
||||
const VolumeMask &mask) const = 0;
|
||||
};
|
||||
|
||||
class AttributeFieldInput : public GeometryFieldInput {
|
||||
|
|
|
@ -21,71 +21,74 @@ namespace blender::bke {
|
|||
|
||||
#ifdef WITH_OPENVDB
|
||||
|
||||
/**
|
||||
* Result when looking up an attribute from some geometry with read and write access. After writing
|
||||
* to the attribute, the #finish method has to be called. This may invalidate caches based on this
|
||||
* attribute.
|
||||
*/
|
||||
template<typename T> struct AttributeGridWriter {
|
||||
// using GridType =
|
||||
|
||||
/**
|
||||
* Grid pointer giving read and write access to the attribute. This may be empty.
|
||||
*/
|
||||
GridT::Ptr VMutableArray<T> varray;
|
||||
/**
|
||||
* Domain where the attribute is stored on the geometry. Also determines the size of the virtual
|
||||
* array.
|
||||
*/
|
||||
eAttrDomain domain;
|
||||
/**
|
||||
* A function that has to be called after the attribute has been edited. This may be empty.
|
||||
*/
|
||||
std::function<void()> tag_modified_fn;
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->varray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has to be called after the attribute has been modified.
|
||||
*/
|
||||
void finish()
|
||||
{
|
||||
if (this->tag_modified_fn) {
|
||||
this->tag_modified_fn();
|
||||
}
|
||||
}
|
||||
};
|
||||
///**
|
||||
// * Result when looking up an attribute from some geometry with read and write access. After
|
||||
// writing
|
||||
// * to the attribute, the #finish method has to be called. This may invalidate caches based on
|
||||
// this
|
||||
// * attribute.
|
||||
// */
|
||||
// template<typename T> struct AttributeGridWriter {
|
||||
// // using GridType =
|
||||
//
|
||||
// /**
|
||||
// * Grid pointer giving read and write access to the attribute. This may be empty.
|
||||
// */
|
||||
// GridT::Ptr VMutableArray<T> varray;
|
||||
// /**
|
||||
// * Domain where the attribute is stored on the geometry. Also determines the size of the
|
||||
// virtual
|
||||
// * array.
|
||||
// */
|
||||
// eAttrDomain domain;
|
||||
// /**
|
||||
// * A function that has to be called after the attribute has been edited. This may be empty.
|
||||
// */
|
||||
// std::function<void()> tag_modified_fn;
|
||||
//
|
||||
// operator bool() const
|
||||
// {
|
||||
// return this->varray;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Has to be called after the attribute has been modified.
|
||||
// */
|
||||
// void finish()
|
||||
// {
|
||||
// if (this->tag_modified_fn) {
|
||||
// this->tag_modified_fn();
|
||||
// }
|
||||
// }
|
||||
//};
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A generic version of #AttributeWriter.
|
||||
*/
|
||||
struct GAttributeGridWriter {
|
||||
VolumeGrid *grid;
|
||||
eAttrDomain domain;
|
||||
std::function<void()> tag_modified_fn;
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->grid;
|
||||
}
|
||||
|
||||
void finish()
|
||||
{
|
||||
if (this->tag_modified_fn) {
|
||||
this->tag_modified_fn();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> AttributeWriter<T> typed() const
|
||||
{
|
||||
return {grid.typed<T>(), domain, tag_modified_fn};
|
||||
}
|
||||
};
|
||||
///**
|
||||
// * A generic version of #AttributeWriter.
|
||||
// */
|
||||
// struct GAttributeGridWriter {
|
||||
// VolumeGrid *grid;
|
||||
// eAttrDomain domain;
|
||||
// std::function<void()> tag_modified_fn;
|
||||
//
|
||||
// operator bool() const
|
||||
// {
|
||||
// return this->grid;
|
||||
// }
|
||||
//
|
||||
// void finish()
|
||||
// {
|
||||
// if (this->tag_modified_fn) {
|
||||
// this->tag_modified_fn();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// template<typename T> AttributeWriter<T> typed() const
|
||||
// {
|
||||
// return {grid.typed<T>(), domain, tag_modified_fn};
|
||||
// }
|
||||
//};
|
||||
|
||||
/* OLD CODE
|
||||
* OLD CODE
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_parameter_pack_utils.hh"
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
# include <openvdb/openvdb.h>
|
||||
#endif
|
||||
|
@ -17,7 +20,22 @@ namespace blender {
|
|||
class CPPType;
|
||||
class ResourceScope;
|
||||
|
||||
namespace volume_mask {
|
||||
/* XXX OpenVDB expects some math functions on vector types. */
|
||||
template<typename T, int Size> inline VecBase<T, Size> Abs(VecBase<T, Size> v)
|
||||
{
|
||||
VecBase<T, Size> r;
|
||||
for (int i = 0; i < Size; i++) {
|
||||
r[i] = math::abs(v[i]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
/* Specialization: math::abs is not defined for unsigned types. */
|
||||
template<int Size> inline VecBase<uint32_t, Size> Abs(VecBase<uint32_t, Size> v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
namespace volume {
|
||||
|
||||
/* Mask defined by active voxels of the grid. */
|
||||
class VolumeMask {
|
||||
|
@ -37,10 +55,6 @@ class VolumeMask {
|
|||
#endif
|
||||
};
|
||||
|
||||
} // namespace volume_mask
|
||||
|
||||
using volume_mask::VolumeMask;
|
||||
|
||||
struct VolumeGrid {
|
||||
#ifdef WITH_OPENVDB
|
||||
openvdb::GridBase::Ptr grid_ = nullptr;
|
||||
|
@ -64,4 +78,124 @@ struct VolumeGrid {
|
|||
operator bool() const;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Tree and Grid types for Blender CPP types
|
||||
* \{ */
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
|
||||
namespace grid_types {
|
||||
|
||||
template<typename ValueType>
|
||||
using TreeCommon = typename openvdb::tree::Tree4<ValueType, 5, 4, 3>::Type;
|
||||
template<typename ValueType> using GridCommon = typename openvdb::Grid<TreeCommon<ValueType>>;
|
||||
|
||||
/* TODO add more as needed. */
|
||||
/* TODO could use template magic to generate all from 1 list, but not worth it for now. */
|
||||
/* TODO some types disabled because of missing CPPType registration. */
|
||||
|
||||
using BoolTree = TreeCommon<bool>;
|
||||
using MaskTree = TreeCommon<openvdb::ValueMask>;
|
||||
using FloatTree = TreeCommon<float>;
|
||||
using Float2Tree = TreeCommon<float2>;
|
||||
using Float3Tree = TreeCommon<float3>;
|
||||
// using Float4Tree = TreeCommon<float4>;
|
||||
using IntTree = TreeCommon<int32_t>;
|
||||
using Int2Tree = TreeCommon<int2>;
|
||||
// using Int3Tree = TreeCommon<int3>;
|
||||
// using Int4Tree = TreeCommon<int4>;
|
||||
using UIntTree = TreeCommon<uint32_t>;
|
||||
// using UInt2Tree = TreeCommon<uint2>;
|
||||
// using UInt3Tree = TreeCommon<uint3>;
|
||||
// using UInt4Tree = TreeCommon<uint4>;
|
||||
using ScalarTree = FloatTree;
|
||||
using TopologyTree = MaskTree;
|
||||
|
||||
using BoolGrid = openvdb::Grid<BoolTree>;
|
||||
using MaskGrid = openvdb::Grid<MaskTree>;
|
||||
using FloatGrid = openvdb::Grid<FloatTree>;
|
||||
using Float2Grid = openvdb::Grid<Float2Tree>;
|
||||
using Float3Grid = openvdb::Grid<Float3Tree>;
|
||||
// using Float4Grid = openvdb::Grid<Float4Tree>;
|
||||
using IntGrid = openvdb::Grid<IntTree>;
|
||||
using Int2Grid = openvdb::Grid<Int2Tree>;
|
||||
// using Int3Grid = openvdb::Grid<Int3Tree>;
|
||||
// using Int4Grid = openvdb::Grid<Int4Tree>;
|
||||
using UIntGrid = openvdb::Grid<UIntTree>;
|
||||
// using UInt2Grid = openvdb::Grid<UInt2Tree>;
|
||||
// using UInt3Grid = openvdb::Grid<UInt3Tree>;
|
||||
// using UInt4Grid = openvdb::Grid<UInt4Tree>;
|
||||
using ScalarGrid = openvdb::Grid<ScalarTree>;
|
||||
using TopologyGrid = openvdb::Grid<TopologyTree>;
|
||||
|
||||
using SupportedGridValueTypes = std::tuple<bool,
|
||||
float,
|
||||
float2,
|
||||
float3,
|
||||
/*float4,*/
|
||||
int32_t,
|
||||
int2,
|
||||
/*int3,*/ /*int4,*/ uint32_t
|
||||
/*,uint2*/
|
||||
/*,uint3*/
|
||||
/*,uint4*/>;
|
||||
|
||||
using SupportedGridTypes = openvdb::TypeList<BoolGrid,
|
||||
MaskGrid,
|
||||
FloatGrid,
|
||||
Float2Grid,
|
||||
Float3Grid,
|
||||
/*Float4Grid,*/
|
||||
IntGrid,
|
||||
Int2Grid,
|
||||
/*Int3Grid,*/
|
||||
/*Int4Grid,*/
|
||||
UIntGrid,
|
||||
/*UInt2Grid,*/
|
||||
/*UInt3Grid,*/
|
||||
/*UInt4Grid,*/
|
||||
ScalarGrid,
|
||||
TopologyGrid>;
|
||||
|
||||
} // namespace grid_types
|
||||
|
||||
/** \} */
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename Func> struct FilterVoidOp {
|
||||
Func func;
|
||||
template<typename T> void operator()(TypeTag<T> type_tag) const
|
||||
{
|
||||
func(type_tag);
|
||||
}
|
||||
template<> void operator()<void>(TypeTag<void> /*type_tag*/) const {}
|
||||
};
|
||||
|
||||
/* Helper function to turn a tuple into a parameter pack by means of the dummy argument. */
|
||||
template<typename... Types, typename Func>
|
||||
void field_to_static_type_resolve(std::tuple<Types...> /*dummy*/, const CPPType &type, Func func)
|
||||
{
|
||||
FilterVoidOp<Func> wrapper{func};
|
||||
type.to_static_type_tag<Types...>(wrapper);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/* Helper function to evaluate a function with a static field type. */
|
||||
template<typename Func> void field_to_static_type(const CPPType &type, Func func)
|
||||
{
|
||||
detail::field_to_static_type_resolve(grid_types::SupportedGridValueTypes(), type, func);
|
||||
}
|
||||
|
||||
/* Helper function to evaluate a function with a static field type. */
|
||||
template<typename Func> void grid_to_static_type(const openvdb::GridBase::Ptr &grid, Func func)
|
||||
{
|
||||
grid->apply<grid_types::SupportedGridTypes>(func);
|
||||
}
|
||||
|
||||
} // namespace volume
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace blender
|
||||
|
|
|
@ -157,6 +157,7 @@ set(SRC
|
|||
intern/timeit.cc
|
||||
intern/uuid.cc
|
||||
intern/uvproject.c
|
||||
intern/volume.cc
|
||||
intern/voronoi_2d.c
|
||||
intern/voxel.c
|
||||
intern/winstuff.c
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_cpp_type.hh"
|
||||
#include "BLI_math_base.hh"
|
||||
#include "BLI_resource_scope.hh"
|
||||
#include "BLI_volume.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
|
||||
namespace volume {
|
||||
|
||||
bool VolumeMask::is_empty() const
|
||||
{
|
||||
return grid_.empty();
|
||||
}
|
||||
|
||||
int64_t VolumeMask::min_voxel_count() const
|
||||
{
|
||||
return grid_.activeVoxelCount();
|
||||
}
|
||||
|
||||
int64_t VolumeGrid::voxel_count() const
|
||||
{
|
||||
return grid_ ? grid_->activeVoxelCount() : 0;
|
||||
}
|
||||
|
||||
bool VolumeGrid::is_empty() const
|
||||
{
|
||||
return grid_ ? grid_->empty() : true;
|
||||
}
|
||||
|
||||
VolumeGrid::operator bool() const
|
||||
{
|
||||
return grid_ != nullptr;
|
||||
}
|
||||
|
||||
VolumeGrid VolumeGrid::create(ResourceScope &scope,
|
||||
const CPPType &type,
|
||||
const void *background_value)
|
||||
{
|
||||
openvdb::GridBase::Ptr grid;
|
||||
volume::field_to_static_type(type, [&grid, background_value](auto type_tag) {
|
||||
using ValueType = typename decltype(type_tag)::type;
|
||||
const ValueType &value = *static_cast<const ValueType *>(background_value);
|
||||
grid = grid_types::GridCommon<ValueType>::create(value);
|
||||
});
|
||||
|
||||
return VolumeGrid{scope.add_value<openvdb::GridBase::Ptr>(std::move(grid))};
|
||||
}
|
||||
|
||||
VolumeGrid VolumeGrid::create(ResourceScope &scope, const CPPType &type)
|
||||
{
|
||||
openvdb::GridBase::Ptr grid;
|
||||
volume::field_to_static_type(type, [&grid](auto type_tag) {
|
||||
using ValueType = typename decltype(type_tag)::type;
|
||||
const CPPType &type = CPPType::get<ValueType>();
|
||||
const ValueType &value = *static_cast<const ValueType *>(type.default_value());
|
||||
grid = grid_types::GridCommon<ValueType>::create(value);
|
||||
});
|
||||
|
||||
return VolumeGrid{scope.add_value<openvdb::GridBase::Ptr>(std::move(grid))};
|
||||
}
|
||||
|
||||
VolumeGrid VolumeGrid::create(ResourceScope &scope,
|
||||
const CPPType &type,
|
||||
const VolumeMask &mask,
|
||||
const void *inactive_value,
|
||||
const void *active_value)
|
||||
{
|
||||
openvdb::GridBase::Ptr grid;
|
||||
volume::field_to_static_type(type, [&](auto type_tag) {
|
||||
using ValueType = typename decltype(type_tag)::type;
|
||||
using TreeType = grid_types::TreeCommon<ValueType>;
|
||||
using GridType = grid_types::GridCommon<ValueType>;
|
||||
|
||||
const ValueType &typed_inactive_value = *static_cast<const ValueType *>(inactive_value);
|
||||
const ValueType &typed_active_value = *static_cast<const ValueType *>(active_value);
|
||||
const TreeType::Ptr tree = TreeType::Ptr(new TreeType(
|
||||
mask.grid().tree(), typed_inactive_value, typed_active_value, openvdb::TopologyCopy{}));
|
||||
grid = GridType::Ptr(new GridType(tree));
|
||||
});
|
||||
|
||||
return VolumeGrid{scope.add_value<openvdb::GridBase::Ptr>(std::move(grid))};
|
||||
}
|
||||
|
||||
} // namespace volume
|
||||
|
||||
#else
|
||||
|
||||
namespace volume {
|
||||
|
||||
bool VolumeMask::is_empty() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t VolumeMask::min_voxel_count() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
VolumeGrid::operator bool() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t VolumeGrid::voxel_count() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool VolumeGrid::is_empty() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
VolumeGrid::operator bool() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
VolumeGrid VolumeGrid::create(ResourceScope &scope, const CPPType &type, const int64_t voxel_count)
|
||||
{
|
||||
return VolumeGrid{};
|
||||
}
|
||||
|
||||
VolumeGrid VolumeGrid::create(ResourceScope &scope,
|
||||
const CPPType &type,
|
||||
const void *background_value)
|
||||
{
|
||||
return VolumeGrid{};
|
||||
}
|
||||
|
||||
} // namespace volume
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace blender
|
|
@ -259,6 +259,9 @@ class FieldContext;
|
|||
*/
|
||||
class FieldInput : public FieldNode {
|
||||
public:
|
||||
using VolumeGrid = volume::VolumeGrid;
|
||||
using VolumeMask = volume::VolumeMask;
|
||||
|
||||
/* The order is also used for sorting in socket inspection. */
|
||||
enum class Category {
|
||||
NamedAttribute = 0,
|
||||
|
@ -334,6 +337,9 @@ struct FieldInputs {
|
|||
*/
|
||||
class FieldContext {
|
||||
public:
|
||||
using VolumeMask = volume::VolumeMask;
|
||||
using VolumeGrid = volume::VolumeGrid;
|
||||
|
||||
virtual ~FieldContext() = default;
|
||||
|
||||
virtual GVArray get_varray_for_input(const FieldInput &field_input,
|
||||
|
@ -524,11 +530,11 @@ Vector<GVArray> evaluate_fields(ResourceScope &scope,
|
|||
* \return The computed virtual arrays for each provided field. If #dst_varrays is passed,
|
||||
* the provided virtual arrays are returned.
|
||||
*/
|
||||
Vector<VolumeGrid> evaluate_volume_fields(ResourceScope &scope,
|
||||
Span<GFieldRef> fields_to_evaluate,
|
||||
const VolumeMask &mask,
|
||||
const FieldContext &context,
|
||||
Span<VolumeGrid> dst_grids = {});
|
||||
Vector<volume::VolumeGrid> evaluate_volume_fields(ResourceScope &scope,
|
||||
Span<GFieldRef> fields_to_evaluate,
|
||||
const volume::VolumeMask &mask,
|
||||
const FieldContext &context,
|
||||
Span<volume::VolumeGrid> dst_grids = {});
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utility functions for simple field creation and evaluation
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_volume.hh"
|
||||
|
||||
#include "FN_multi_function_procedure.hh"
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
|
@ -11,12 +13,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
class CPPType;
|
||||
class ResourceScope;
|
||||
template<typename T> class VArray;
|
||||
class VolumeGrid;
|
||||
class VolumeMask;
|
||||
|
||||
namespace fn {
|
||||
class GFieldRef;
|
||||
}
|
||||
|
@ -25,19 +21,19 @@ class GFieldRef;
|
|||
namespace blender::fn {
|
||||
|
||||
void evaluate_procedure_on_varying_volume_fields(ResourceScope &scope,
|
||||
const VolumeMask &mask,
|
||||
const volume::VolumeMask &mask,
|
||||
const mf::Procedure &procedure,
|
||||
Span<VolumeGrid> field_context_inputs,
|
||||
Span<volume::VolumeGrid> field_context_inputs,
|
||||
Span<GFieldRef> fields_to_evaluate,
|
||||
Span<int> field_indices,
|
||||
Span<VolumeGrid> dst_grids,
|
||||
MutableSpan<VolumeGrid> r_grids,
|
||||
Span<volume::VolumeGrid> dst_grids,
|
||||
MutableSpan<volume::VolumeGrid> r_grids,
|
||||
MutableSpan<bool> r_is_output_written_to_dst);
|
||||
void evaluate_procedure_on_constant_volume_fields(ResourceScope &scope,
|
||||
const mf::Procedure &procedure,
|
||||
Span<VolumeGrid> field_context_inputs,
|
||||
Span<volume::VolumeGrid> field_context_inputs,
|
||||
Span<GFieldRef> fields_to_evaluate,
|
||||
Span<int> field_indices,
|
||||
MutableSpan<VolumeGrid> r_grids);
|
||||
MutableSpan<volume::VolumeGrid> r_grids);
|
||||
|
||||
} // namespace blender::fn
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
|
||||
namespace blender::fn {
|
||||
|
||||
using VolumeGrid = volume::VolumeGrid;
|
||||
using VolumeMask = volume::VolumeMask;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Field Evaluation
|
||||
* \{ */
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_resource_scope.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
#include "BLI_volume.hh"
|
||||
|
||||
#include "FN_field.hh"
|
||||
#include "FN_multi_function_builder.hh"
|
||||
|
@ -18,222 +19,8 @@
|
|||
|
||||
# include <openvdb/tools/ValueTransformer.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* XXX OpenVDB expects some math functions on vector types. */
|
||||
template<typename T, int Size> inline VecBase<T, Size> Abs(VecBase<T, Size> v)
|
||||
{
|
||||
VecBase<T, Size> r;
|
||||
for (int i = 0; i < Size; i++) {
|
||||
r[i] = math::abs(v[i]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
/* Specialization: math::abs is not defined for unsigned types. */
|
||||
template<int Size> inline VecBase<uint32_t, Size> Abs(VecBase<uint32_t, Size> v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
}; // namespace blender
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Tree and Grid types for Blender CPP types
|
||||
* \{ */
|
||||
|
||||
namespace blender::fn::grid_types {
|
||||
|
||||
template<typename ValueType>
|
||||
using TreeCommon = typename openvdb::tree::Tree4<ValueType, 5, 4, 3>::Type;
|
||||
template<typename ValueType> using GridCommon = typename openvdb::Grid<TreeCommon<ValueType>>;
|
||||
|
||||
/* TODO add more as needed. */
|
||||
/* TODO could use template magic to generate all from 1 list, but not worth it for now. */
|
||||
/* TODO some types disabled because of missing CPPType registration. */
|
||||
|
||||
using BoolTree = TreeCommon<bool>;
|
||||
using MaskTree = TreeCommon<openvdb::ValueMask>;
|
||||
using FloatTree = TreeCommon<float>;
|
||||
using Float2Tree = TreeCommon<float2>;
|
||||
using Float3Tree = TreeCommon<float3>;
|
||||
// using Float4Tree = TreeCommon<float4>;
|
||||
using IntTree = TreeCommon<int32_t>;
|
||||
using Int2Tree = TreeCommon<int2>;
|
||||
// using Int3Tree = TreeCommon<int3>;
|
||||
// using Int4Tree = TreeCommon<int4>;
|
||||
using UIntTree = TreeCommon<uint32_t>;
|
||||
// using UInt2Tree = TreeCommon<uint2>;
|
||||
// using UInt3Tree = TreeCommon<uint3>;
|
||||
// using UInt4Tree = TreeCommon<uint4>;
|
||||
using ScalarTree = FloatTree;
|
||||
using TopologyTree = MaskTree;
|
||||
|
||||
using BoolGrid = openvdb::Grid<BoolTree>;
|
||||
using MaskGrid = openvdb::Grid<MaskTree>;
|
||||
using FloatGrid = openvdb::Grid<FloatTree>;
|
||||
using Float2Grid = openvdb::Grid<Float2Tree>;
|
||||
using Float3Grid = openvdb::Grid<Float3Tree>;
|
||||
// using Float4Grid = openvdb::Grid<Float4Tree>;
|
||||
using IntGrid = openvdb::Grid<IntTree>;
|
||||
using Int2Grid = openvdb::Grid<Int2Tree>;
|
||||
// using Int3Grid = openvdb::Grid<Int3Tree>;
|
||||
// using Int4Grid = openvdb::Grid<Int4Tree>;
|
||||
using UIntGrid = openvdb::Grid<UIntTree>;
|
||||
// using UInt2Grid = openvdb::Grid<UInt2Tree>;
|
||||
// using UInt3Grid = openvdb::Grid<UInt3Tree>;
|
||||
// using UInt4Grid = openvdb::Grid<UInt4Tree>;
|
||||
using ScalarGrid = openvdb::Grid<ScalarTree>;
|
||||
using TopologyGrid = openvdb::Grid<TopologyTree>;
|
||||
|
||||
using SupportedGridValueTypes = std::tuple<bool,
|
||||
float,
|
||||
float2,
|
||||
float3,
|
||||
/*float4,*/
|
||||
int32_t,
|
||||
int2,
|
||||
/*int3,*/ /*int4,*/ uint32_t
|
||||
/*,uint2*/
|
||||
/*,uint3*/
|
||||
/*,uint4*/>;
|
||||
|
||||
using SupportedGridTypes = openvdb::TypeList<BoolGrid,
|
||||
MaskGrid,
|
||||
FloatGrid,
|
||||
Float2Grid,
|
||||
Float3Grid,
|
||||
/*Float4Grid,*/
|
||||
IntGrid,
|
||||
Int2Grid,
|
||||
/*Int3Grid,*/
|
||||
/*Int4Grid,*/
|
||||
UIntGrid,
|
||||
/*UInt2Grid,*/
|
||||
/*UInt3Grid,*/
|
||||
/*UInt4Grid,*/
|
||||
ScalarGrid,
|
||||
TopologyGrid>;
|
||||
|
||||
} // namespace blender::fn::grid_types
|
||||
|
||||
/** \} */
|
||||
|
||||
namespace blender::volume_mask {
|
||||
|
||||
bool VolumeMask::is_empty() const
|
||||
{
|
||||
return grid_.empty();
|
||||
}
|
||||
|
||||
int64_t VolumeMask::min_voxel_count() const
|
||||
{
|
||||
return grid_.activeVoxelCount();
|
||||
}
|
||||
|
||||
} // namespace blender::volume_mask
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename Func> struct FilterVoidOp {
|
||||
Func func;
|
||||
template<typename T> void operator()(TypeTag<T> type_tag) const
|
||||
{
|
||||
func(type_tag);
|
||||
}
|
||||
template<> void operator()<void>(TypeTag<void> /*type_tag*/) const {}
|
||||
};
|
||||
|
||||
/* Helper function to turn a tuple into a parameter pack by means of the dummy argument. */
|
||||
template<typename... Types, typename Func>
|
||||
static void field_to_static_type_resolve(std::tuple<Types...> /*dummy*/,
|
||||
const CPPType &type,
|
||||
Func func)
|
||||
{
|
||||
FilterVoidOp<Func> wrapper{func};
|
||||
type.to_static_type_tag<Types...>(wrapper);
|
||||
}
|
||||
|
||||
/* Helper function to evaluate a function with a static field type. */
|
||||
template<typename Func> static void field_to_static_type(const CPPType &type, Func func)
|
||||
{
|
||||
field_to_static_type_resolve(grid_types::SupportedGridValueTypes(), type, func);
|
||||
}
|
||||
|
||||
/* Helper function to evaluate a function with a static field type. */
|
||||
template<typename Func>
|
||||
static void grid_to_static_type(const openvdb::GridBase::Ptr &grid, Func func)
|
||||
{
|
||||
grid->apply<grid_types::SupportedGridTypes>(func);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
int64_t VolumeGrid::voxel_count() const
|
||||
{
|
||||
return grid_ ? grid_->activeVoxelCount() : 0;
|
||||
}
|
||||
|
||||
bool VolumeGrid::is_empty() const
|
||||
{
|
||||
return grid_ ? grid_->empty() : true;
|
||||
}
|
||||
|
||||
VolumeGrid::operator bool() const
|
||||
{
|
||||
return grid_ != nullptr;
|
||||
}
|
||||
|
||||
VolumeGrid VolumeGrid::create(ResourceScope &scope,
|
||||
const CPPType &type,
|
||||
const void *background_value)
|
||||
{
|
||||
openvdb::GridBase::Ptr grid;
|
||||
detail::field_to_static_type(type, [&grid, background_value](auto type_tag) {
|
||||
using ValueType = typename decltype(type_tag)::type;
|
||||
const ValueType &value = *static_cast<const ValueType *>(background_value);
|
||||
grid = grid_types::GridCommon<ValueType>::create(value);
|
||||
});
|
||||
|
||||
return VolumeGrid{scope.add_value<openvdb::GridBase::Ptr>(std::move(grid))};
|
||||
}
|
||||
|
||||
VolumeGrid VolumeGrid::create(ResourceScope &scope, const CPPType &type)
|
||||
{
|
||||
openvdb::GridBase::Ptr grid;
|
||||
detail::field_to_static_type(type, [&grid](auto type_tag) {
|
||||
using ValueType = typename decltype(type_tag)::type;
|
||||
const CPPType &type = CPPType::get<ValueType>();
|
||||
const ValueType &value = *static_cast<const ValueType *>(type.default_value());
|
||||
grid = grid_types::GridCommon<ValueType>::create(value);
|
||||
});
|
||||
|
||||
return VolumeGrid{scope.add_value<openvdb::GridBase::Ptr>(std::move(grid))};
|
||||
}
|
||||
|
||||
VolumeGrid VolumeGrid::create(ResourceScope &scope,
|
||||
const CPPType &type,
|
||||
const VolumeMask &mask,
|
||||
const void *inactive_value,
|
||||
const void *active_value)
|
||||
{
|
||||
openvdb::GridBase::Ptr grid;
|
||||
detail::field_to_static_type(type, [&](auto type_tag) {
|
||||
using ValueType = typename decltype(type_tag)::type;
|
||||
using TreeType = grid_types::TreeCommon<ValueType>;
|
||||
using GridType = grid_types::GridCommon<ValueType>;
|
||||
|
||||
const ValueType &typed_inactive_value = *static_cast<const ValueType *>(inactive_value);
|
||||
const ValueType &typed_active_value = *static_cast<const ValueType *>(active_value);
|
||||
const TreeType::Ptr tree = TreeType::Ptr(new TreeType(
|
||||
mask.grid().tree(), typed_inactive_value, typed_active_value, openvdb::TopologyCopy{}));
|
||||
grid = GridType::Ptr(new GridType(tree));
|
||||
});
|
||||
|
||||
return VolumeGrid{scope.add_value<openvdb::GridBase::Ptr>(std::move(grid))};
|
||||
}
|
||||
|
||||
/* A VArray implementation using OpenVDB grid accessors.
|
||||
* The index is converted to global voxel coordinate
|
||||
* by interpreting it as a leaf buffer index. */
|
||||
|
@ -390,6 +177,8 @@ struct TypedAccessorWrapper : public AccessorWrapper<LeafNodeType> {
|
|||
};
|
||||
|
||||
template<typename GridType> struct EvalPerLeafOp {
|
||||
using VolumeGrid = volume::VolumeGrid;
|
||||
using VolumeMask = volume::VolumeMask;
|
||||
using TreeType = typename GridType::TreeType;
|
||||
using ValueType = typename GridType::ValueType;
|
||||
|
||||
|
@ -419,7 +208,7 @@ template<typename GridType> struct EvalPerLeafOp {
|
|||
input_accessors_.reinitialize(field_context_inputs.size());
|
||||
|
||||
for (const int i : field_context_inputs.index_range()) {
|
||||
detail::grid_to_static_type(field_context_inputs[i].grid_, [&](auto &input_grid) {
|
||||
volume::grid_to_static_type(field_context_inputs[i].grid_, [&](auto &input_grid) {
|
||||
using InputGridType = typename std::decay<decltype(input_grid)>::type;
|
||||
using AccessorType = typename InputGridType::ConstAccessor;
|
||||
|
||||
|
@ -477,19 +266,22 @@ template<typename GridType> struct EvalPerLeafOp {
|
|||
};
|
||||
|
||||
void evaluate_procedure_on_varying_volume_fields(ResourceScope &scope,
|
||||
const VolumeMask &mask,
|
||||
const volume::VolumeMask &mask,
|
||||
const mf::Procedure &procedure,
|
||||
Span<VolumeGrid> field_context_inputs,
|
||||
Span<volume::VolumeGrid> field_context_inputs,
|
||||
Span<GFieldRef> fields_to_evaluate,
|
||||
Span<int> field_indices,
|
||||
Span<VolumeGrid> dst_grids,
|
||||
MutableSpan<VolumeGrid> r_grids,
|
||||
Span<volume::VolumeGrid> dst_grids,
|
||||
MutableSpan<volume::VolumeGrid> r_grids,
|
||||
MutableSpan<bool> r_is_output_written_to_dst)
|
||||
{
|
||||
/* Execute a multifunction procedure on each leaf buffer of the mask.
|
||||
* Each leaf buffer is a contiguous array that can be used as a span.
|
||||
* The leaf buffers' active voxel masks are used as index masks. */
|
||||
|
||||
using volume::VolumeGrid;
|
||||
using volume::VolumeMask;
|
||||
|
||||
/* Destination arrays are optional. Create a small utility method to access them. */
|
||||
auto get_dst_grid = [&](int index) -> VolumeGrid {
|
||||
if (dst_grids.is_empty()) {
|
||||
|
@ -527,7 +319,7 @@ void evaluate_procedure_on_varying_volume_fields(ResourceScope &scope,
|
|||
* Each leaf buffer is a contiguous array that can be used as a span.
|
||||
* The leaf buffers' active voxel masks are used as index masks. */
|
||||
|
||||
detail::grid_to_static_type(dst_grid.grid_, [&](auto &dst_grid) {
|
||||
volume::grid_to_static_type(dst_grid.grid_, [&](auto &dst_grid) {
|
||||
using GridType = typename std::decay<decltype(dst_grid)>::type;
|
||||
|
||||
EvalPerLeafOp<GridType> func(field_context_inputs, procedure_executor, mask.grid());
|
||||
|
@ -541,11 +333,14 @@ void evaluate_procedure_on_varying_volume_fields(ResourceScope &scope,
|
|||
|
||||
void evaluate_procedure_on_constant_volume_fields(ResourceScope & /*scope*/,
|
||||
const mf::Procedure &procedure,
|
||||
Span<VolumeGrid> field_context_inputs,
|
||||
Span<volume::VolumeGrid> field_context_inputs,
|
||||
Span<GFieldRef> fields_to_evaluate,
|
||||
Span<int> field_indices,
|
||||
MutableSpan<VolumeGrid> r_grids)
|
||||
MutableSpan<volume::VolumeGrid> r_grids)
|
||||
{
|
||||
using volume::VolumeGrid;
|
||||
using volume::VolumeMask;
|
||||
|
||||
mf::ProcedureExecutor procedure_executor{procedure};
|
||||
const IndexMask mask(1);
|
||||
mf::ParamsBuilder mf_params{procedure_executor, &mask};
|
||||
|
@ -553,7 +348,7 @@ void evaluate_procedure_on_constant_volume_fields(ResourceScope & /*scope*/,
|
|||
|
||||
/* Provide inputs to the procedure executor. */
|
||||
for (const int i : field_context_inputs.index_range()) {
|
||||
detail::grid_to_static_type(field_context_inputs[i].grid_, [&](auto &input_grid) {
|
||||
volume::grid_to_static_type(field_context_inputs[i].grid_, [&](auto &input_grid) {
|
||||
using InputGridType = typename std::decay<decltype(input_grid)>::type;
|
||||
using ValueType = typename InputGridType::ValueType;
|
||||
|
||||
|
@ -587,11 +382,11 @@ void evaluate_procedure_on_constant_volume_fields(ResourceScope & /*scope*/,
|
|||
const CPPType &type = field.cpp_type();
|
||||
const int out_index = field_indices[i];
|
||||
|
||||
detail::field_to_static_type(type, [&](auto type_tag) {
|
||||
volume::field_to_static_type(type, [&](auto type_tag) {
|
||||
using ValueType = typename decltype(type_tag)::type;
|
||||
|
||||
const ValueType &value = *static_cast<ValueType *>(output_buffers[i]);
|
||||
r_grids[out_index] = VolumeGrid{grid_types::GridCommon<ValueType>::create(value)};
|
||||
r_grids[out_index] = VolumeGrid{volume::grid_types::GridCommon<ValueType>::create(value)};
|
||||
});
|
||||
|
||||
/* Destruct output value buffers, value is stored in grid backgrounds now. */
|
||||
|
@ -601,56 +396,4 @@ void evaluate_procedure_on_constant_volume_fields(ResourceScope & /*scope*/,
|
|||
|
||||
} // namespace blender::fn
|
||||
|
||||
#else
|
||||
|
||||
namespace blender::volume_mask {
|
||||
|
||||
bool VolumeMask::is_empty() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t VolumeMask::min_voxel_count() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace blender::volume_mask
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
VolumeGrid::operator bool() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t VolumeGrid::voxel_count() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool VolumeGrid::is_empty() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
VolumeGrid::operator bool() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
VolumeGrid VolumeGrid::create(ResourceScope &scope, const CPPType &type, const int64_t voxel_count)
|
||||
{
|
||||
return VolumeGrid{};
|
||||
}
|
||||
|
||||
VolumeGrid VolumeGrid::create(ResourceScope &scope,
|
||||
const CPPType &type,
|
||||
const void *background_value)
|
||||
{
|
||||
return VolumeGrid{};
|
||||
}
|
||||
|
||||
} // namespace blender::fn
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue