Blender V4.3
node_geo_volume_cube.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#ifdef WITH_OPENVDB
6# include <openvdb/openvdb.h>
7# include <openvdb/tools/Dense.h>
8# include <openvdb/tools/LevelSetUtil.h>
9# include <openvdb/tools/ParticlesToLevelSet.h>
10#endif
11
12#include "node_geometry_util.hh"
13
14#include "BLI_task.hh"
15
16#include "BKE_geometry_set.hh"
17#include "BKE_lib_id.hh"
18#include "BKE_volume.hh"
19#include "BKE_volume_openvdb.hh"
20
22
24{
25 b.add_input<decl::Float>("Density")
26 .default_value(1.0f)
27 .description("Volume density per voxel")
28 .supports_field();
29 b.add_input<decl::Float>("Background").description("Value for voxels outside of the cube");
30
31 b.add_input<decl::Vector>("Min")
32 .default_value(float3(-1.0f))
33 .description("Minimum boundary of volume");
34 b.add_input<decl::Vector>("Max")
35 .default_value(float3(1.0f))
36 .description("Maximum boundary of volume");
37
38 b.add_input<decl::Int>("Resolution X")
39 .default_value(32)
40 .min(2)
41 .description("Number of voxels in the X axis");
42 b.add_input<decl::Int>("Resolution Y")
43 .default_value(32)
44 .min(2)
45 .description("Number of voxels in the Y axis");
46 b.add_input<decl::Int>("Resolution Z")
47 .default_value(32)
48 .min(2)
49 .description("Number of voxels in the Z axis");
50
51 b.add_output<decl::Geometry>("Volume").translation_context(BLT_I18NCONTEXT_ID_ID);
52}
53
54static float map(const float x,
55 const float in_min,
56 const float in_max,
57 const float out_min,
58 const float out_max)
59{
60 return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
61}
62
64 private:
65 int3 resolution_;
66 float3 bounds_min_;
67 float3 bounds_max_;
68
69 public:
70 Grid3DFieldContext(const int3 resolution, const float3 bounds_min, const float3 bounds_max)
71 : resolution_(resolution), bounds_min_(bounds_min), bounds_max_(bounds_max)
72 {
73 }
74
76 {
77 return int64_t(resolution_.x) * int64_t(resolution_.y) * int64_t(resolution_.z);
78 }
79
81 const IndexMask & /*mask*/,
82 ResourceScope & /*scope*/) const
83 {
84 const bke::AttributeFieldInput *attribute_field_input =
85 dynamic_cast<const bke::AttributeFieldInput *>(&field_input);
86 if (attribute_field_input == nullptr) {
87 return {};
88 }
89 if (attribute_field_input->attribute_name() != "position") {
90 return {};
91 }
92
93 Array<float3> positions(this->points_num());
94
95 threading::parallel_for(IndexRange(resolution_.x), 1, [&](const IndexRange x_range) {
96 /* Start indexing at current X slice. */
97 int64_t index = x_range.start() * resolution_.y * resolution_.z;
98 for (const int64_t x_i : x_range) {
99 const float x = map(x_i, 0.0f, resolution_.x - 1, bounds_min_.x, bounds_max_.x);
100 for (const int64_t y_i : IndexRange(resolution_.y)) {
101 const float y = map(y_i, 0.0f, resolution_.y - 1, bounds_min_.y, bounds_max_.y);
102 for (const int64_t z_i : IndexRange(resolution_.z)) {
103 const float z = map(z_i, 0.0f, resolution_.z - 1, bounds_min_.z, bounds_max_.z);
104 positions[index] = float3(x, y, z);
105 index++;
106 }
107 }
108 }
109 });
110 return VArray<float3>::ForContainer(std::move(positions));
111 }
112};
113
115{
116#ifdef WITH_OPENVDB
117 const float3 bounds_min = params.extract_input<float3>("Min");
118 const float3 bounds_max = params.extract_input<float3>("Max");
119
120 const int3 resolution = int3(params.extract_input<int>("Resolution X"),
121 params.extract_input<int>("Resolution Y"),
122 params.extract_input<int>("Resolution Z"));
123
124 if (resolution.x < 2 || resolution.y < 2 || resolution.z < 2) {
125 params.error_message_add(NodeWarningType::Error, TIP_("Resolution must be greater than 1"));
126 params.set_default_remaining_outputs();
127 return;
128 }
129
130 if (bounds_min.x == bounds_max.x || bounds_min.y == bounds_max.y || bounds_min.z == bounds_max.z)
131 {
132 params.error_message_add(NodeWarningType::Error,
133 TIP_("Bounding box volume must be greater than 0"));
134 params.set_default_remaining_outputs();
135 return;
136 }
137
138 const double3 scale_fac = double3(bounds_max - bounds_min) / double3(resolution - 1);
139 if (!BKE_volume_grid_determinant_valid(scale_fac.x * scale_fac.y * scale_fac.z)) {
140 params.error_message_add(NodeWarningType::Warning,
141 TIP_("Volume scale is lower than permitted by OpenVDB"));
142 params.set_default_remaining_outputs();
143 return;
144 }
145
146 Field<float> input_field = params.extract_input<Field<float>>("Density");
147
148 /* Evaluate input field on a 3D grid. */
149 Grid3DFieldContext context(resolution, bounds_min, bounds_max);
150 FieldEvaluator evaluator(context, context.points_num());
151 Array<float> densities(context.points_num());
152 evaluator.add_with_destination(std::move(input_field), densities.as_mutable_span());
153 evaluator.evaluate();
154
155 /* Store resulting values in openvdb grid. */
156 const float background = params.extract_input<float>("Background");
157 openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(background);
158 grid->setGridClass(openvdb::GRID_FOG_VOLUME);
159
160 openvdb::tools::Dense<float, openvdb::tools::LayoutZYX> dense_grid{
161 openvdb::math::CoordBBox({0, 0, 0}, {resolution.x - 1, resolution.y - 1, resolution.z - 1}),
162 densities.data()};
163 openvdb::tools::copyFromDense(dense_grid, *grid, 0.0f);
164
165 grid->transform().preTranslate(openvdb::math::Vec3<float>(-0.5f));
166 grid->transform().postScale(openvdb::math::Vec3<double>(scale_fac.x, scale_fac.y, scale_fac.z));
167 grid->transform().postTranslate(
168 openvdb::math::Vec3<float>(bounds_min.x, bounds_min.y, bounds_min.z));
169
170 Volume *volume = reinterpret_cast<Volume *>(BKE_id_new_nomain(ID_VO, nullptr));
171 BKE_volume_grid_add_vdb(*volume, "density", std::move(grid));
172
173 GeometrySet r_geometry_set;
174 r_geometry_set.replace_volume(volume);
175 params.set_output("Volume", r_geometry_set);
176#else
178#endif
179}
180
181static void node_register()
182{
183 static blender::bke::bNodeType ntype;
184
185 geo_node_type_base(&ntype, GEO_NODE_VOLUME_CUBE, "Volume Cube", NODE_CLASS_GEOMETRY);
186
187 ntype.declare = node_declare;
190}
191NOD_REGISTER_NODE(node_register)
192
193} // namespace blender::nodes::node_geo_volume_cube_cc
void * BKE_id_new_nomain(short type, const char *name)
Definition lib_id.cc:1487
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
Volume data-block.
bool BKE_volume_grid_determinant_valid(double determinant)
#define BLT_I18NCONTEXT_ID_ID
#define TIP_(msgid)
@ ID_VO
#define NOD_REGISTER_NODE(REGISTER_FUNC)
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
const T * data() const
Definition BLI_array.hh:301
static VArray ForContainer(ContainerT container)
int add_with_destination(GField field, GVMutableArray dst)
Definition field.cc:743
Grid3DFieldContext(const int3 resolution, const float3 bounds_min, const float3 bounds_max)
GVArray get_varray_for_input(const FieldInput &field_input, const IndexMask &, ResourceScope &) const
local_group_size(16, 16) .push_constant(Type b
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
static void node_declare(NodeDeclarationBuilder &b)
static void node_geo_exec(GeoNodeExecParams params)
static float map(const float x, const float in_min, const float in_max, const float out_min, const float out_max)
void node_geo_exec_with_missing_openvdb(GeoNodeExecParams &params)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
VecBase< float, 3 > float3
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
__int64 int64_t
Definition stdint.h:89
void replace_volume(Volume *volume, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Defines a node type.
Definition BKE_node.hh:218
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
NodeDeclareFunction declare
Definition BKE_node.hh:347