Blender V5.0
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 override
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>::from_container(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_voxel_size_valid(float3(scale_fac))) {
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().postScale(openvdb::math::Vec3<double>(scale_fac.x, scale_fac.y, scale_fac.z));
166 grid->transform().postTranslate(
167 openvdb::math::Vec3<float>(bounds_min.x, bounds_min.y, bounds_min.z));
168
169 Volume *volume = BKE_id_new_nomain<Volume>(nullptr);
170 BKE_volume_grid_add_vdb(*volume, "density", std::move(grid));
171
172 GeometrySet r_geometry_set;
173 r_geometry_set.replace_volume(volume);
174 params.set_output("Volume", r_geometry_set);
175#else
177#endif
178}
179
180static void node_register()
181{
182 static blender::bke::bNodeType ntype;
183
184 geo_node_type_base(&ntype, "GeometryNodeVolumeCube", GEO_NODE_VOLUME_CUBE);
185 ntype.ui_name = "Volume Cube";
186 ntype.ui_description =
187 "Generate a dense volume with a field that controls the density at each grid voxel based on "
188 "its position";
189 ntype.enum_name_legacy = "VOLUME_CUBE";
191 ntype.declare = node_declare;
194}
195NOD_REGISTER_NODE(node_register)
196
197} // namespace blender::nodes::node_geo_volume_cube_cc
void * BKE_id_new_nomain(short type, const char *name)
Definition lib_id.cc:1519
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#define GEO_NODE_VOLUME_CUBE
Volume data-block.
bool BKE_volume_voxel_size_valid(const blender::float3 &voxel_size)
#define BLT_I18NCONTEXT_ID_ID
#define TIP_(msgid)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
long long int int64_t
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
const T * data() const
Definition BLI_array.hh:312
static VArray from_container(ContainerT container)
int add_with_destination(GField field, GVMutableArray dst)
Definition field.cc:738
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 override
VecBase< int, 3 > int3
VecBase< float, 3 > float3
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
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:93
VecBase< int32_t, 3 > int3
VecBase< double, 3 > double3
VecBase< float, 3 > float3
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void replace_volume(Volume *volume, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
const char * enum_name_legacy
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:362