Blender V4.3
node_geo_distribute_points_in_volume.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/Interpolation.h>
8# include <openvdb/tools/PointScatter.h>
9#endif
10
11#include "DNA_node_types.h"
13
14#include "BKE_pointcloud.hh"
15#include "BKE_volume.hh"
16#include "BKE_volume_grid.hh"
17
18#include "NOD_rna_define.hh"
19
20#include "UI_interface.hh"
21#include "UI_resources.hh"
22
23#include "GEO_randomize.hh"
24
25#include "node_geometry_util.hh"
26
28
30
32{
33 b.add_input<decl::Geometry>("Volume")
34 .supported_type(GeometryComponent::Type::Volume)
36 auto &density = b.add_input<decl::Float>("Density")
37 .default_value(1.0f)
38 .min(0.0f)
39 .max(100000.0f)
41 .description("Number of points to sample per unit volume");
42 auto &seed = b.add_input<decl::Int>("Seed").min(-10000).max(10000).description(
43 "Seed used by the random number generator to generate random points");
44 auto &spacing = b.add_input<decl::Vector>("Spacing")
45 .default_value({0.3, 0.3, 0.3})
46 .min(0.0001f)
48 .description("Spacing between grid points");
49 auto &threshold = b.add_input<decl::Float>("Threshold")
50 .default_value(0.1f)
51 .min(0.0f)
52 .max(FLT_MAX)
53 .description("Minimum density of a volume cell to contain a grid point");
54 b.add_output<decl::Geometry>("Points").propagate_all();
55
56 const bNode *node = b.node_or_null();
57 if (node != nullptr) {
58 const NodeGeometryDistributePointsInVolume &storage = node_storage(*node);
60 storage.mode);
61
65 threshold.available(mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID);
66 }
67}
68
69static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
70{
71 uiItemR(layout, ptr, "mode", UI_ITEM_NONE, "", ICON_NONE);
72}
73
74static void node_init(bNodeTree * /*tree*/, bNode *node)
75{
76 NodeGeometryDistributePointsInVolume *data = MEM_cnew<NodeGeometryDistributePointsInVolume>(
77 __func__);
79 node->storage = data;
80}
81
82#ifdef WITH_OPENVDB
83/* Implements the interface required by #openvdb::tools::NonUniformPointScatter. */
84class PositionsVDBWrapper {
85 private:
86 float3 offset_fix_;
87 Vector<float3> &vector_;
88
89 public:
90 PositionsVDBWrapper(Vector<float3> &vector, const float3 offset_fix)
91 : offset_fix_(offset_fix), vector_(vector)
92 {
93 }
94 PositionsVDBWrapper(const PositionsVDBWrapper &wrapper) = default;
95
96 void add(const openvdb::Vec3R &pos)
97 {
98 vector_.append(float3(float(pos[0]), float(pos[1]), float(pos[2])) + offset_fix_);
99 }
100};
101
102/* Use #std::mt19937 as a random number generator,
103 * it has a very long period and thus there should be no visible patterns in the generated points.
104 */
105using RNGType = std::mt19937;
106/* Non-uniform scatter allows the amount of points to be scaled with the volume's density. */
107using NonUniformPointScatterVDB =
108 openvdb::tools::NonUniformPointScatter<PositionsVDBWrapper, RNGType>;
109
110static void point_scatter_density_random(const openvdb::FloatGrid &grid,
111 const float density,
112 const int seed,
113 Vector<float3> &r_positions)
114{
115 /* Offset points by half a voxel so that grid points are aligned with world grid points. */
116 const float3 offset_fix = {0.5f * float(grid.voxelSize().x()),
117 0.5f * float(grid.voxelSize().y()),
118 0.5f * float(grid.voxelSize().z())};
119 /* Setup and call into OpenVDB's point scatter API. */
120 PositionsVDBWrapper vdb_position_wrapper = PositionsVDBWrapper(r_positions, offset_fix);
121 RNGType random_generator(seed);
122 NonUniformPointScatterVDB point_scatter(vdb_position_wrapper, density, random_generator);
123 point_scatter(grid);
124}
125
126static void point_scatter_density_grid(const openvdb::FloatGrid &grid,
127 const float3 spacing,
128 const float threshold,
129 Vector<float3> &r_positions)
130{
131 const openvdb::Vec3d half_voxel(0.5, 0.5, 0.5);
132 const openvdb::Vec3d voxel_spacing(double(spacing.x) / grid.voxelSize().x(),
133 double(spacing.y) / grid.voxelSize().y(),
134 double(spacing.z) / grid.voxelSize().z());
135
136 /* Abort if spacing is zero. */
137 const double min_spacing = std::min(voxel_spacing.x(),
138 std::min(voxel_spacing.y(), voxel_spacing.z()));
139 if (std::abs(min_spacing) < 0.0001) {
140 return;
141 }
142
143 /* Iterate through tiles and voxels on the grid. */
144 for (openvdb::FloatGrid::ValueOnCIter cell = grid.cbeginValueOn(); cell; ++cell) {
145 /* Check if the cell's value meets the minimum threshold. */
146 if (cell.getValue() < threshold) {
147 continue;
148 }
149 /* Compute the bounding box of each tile/voxel. */
150 const openvdb::CoordBBox bbox = cell.getBoundingBox();
151 const openvdb::Vec3d box_min = bbox.min().asVec3d() - half_voxel;
152 const openvdb::Vec3d box_max = bbox.max().asVec3d() + half_voxel;
153
154 /* Pick a starting point rounded up to the nearest possible point. */
155 double abs_spacing_x = std::abs(voxel_spacing.x());
156 double abs_spacing_y = std::abs(voxel_spacing.y());
157 double abs_spacing_z = std::abs(voxel_spacing.z());
158 const openvdb::Vec3d start(ceil(box_min.x() / abs_spacing_x) * abs_spacing_x,
159 ceil(box_min.y() / abs_spacing_y) * abs_spacing_y,
160 ceil(box_min.z() / abs_spacing_z) * abs_spacing_z);
161
162 /* Iterate through all possible points in box. */
163 for (double x = start.x(); x < box_max.x(); x += abs_spacing_x) {
164 for (double y = start.y(); y < box_max.y(); y += abs_spacing_y) {
165 for (double z = start.z(); z < box_max.z(); z += abs_spacing_z) {
166 /* Transform with grid matrix and add point. */
167 const openvdb::Vec3d idx_pos(x, y, z);
168 const openvdb::Vec3d local_pos = grid.indexToWorld(idx_pos + half_voxel);
169 r_positions.append({float(local_pos.x()), float(local_pos.y()), float(local_pos.z())});
170 }
171 }
172 }
173 }
174}
175
176#endif /* WITH_OPENVDB */
177
179{
180#ifdef WITH_OPENVDB
181 GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume");
182
183 const NodeGeometryDistributePointsInVolume &storage = node_storage(params.node());
185 storage.mode);
186
187 float density;
188 int seed;
189 float3 spacing{0, 0, 0};
190 float threshold;
192 density = params.extract_input<float>("Density");
193 seed = params.extract_input<int>("Seed");
194 }
196 spacing = params.extract_input<float3>("Spacing");
197 threshold = params.extract_input<float>("Threshold");
198 }
199
200 geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
201 if (!geometry_set.has_volume()) {
203 return;
204 }
205 const VolumeComponent *component = geometry_set.get_component<VolumeComponent>();
206 const Volume *volume = component->get();
207 BKE_volume_load(volume, params.bmain());
208
209 Vector<float3> positions;
210
211 for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
212 const bke::VolumeGridData *volume_grid = BKE_volume_grid_get(volume, i);
213 if (volume_grid == nullptr) {
214 continue;
215 }
216
217 bke::VolumeTreeAccessToken tree_token;
218 const openvdb::GridBase &base_grid = volume_grid->grid(tree_token);
219
220 if (!base_grid.isType<openvdb::FloatGrid>()) {
221 continue;
222 }
223
224 const openvdb::FloatGrid &grid = static_cast<const openvdb::FloatGrid &>(base_grid);
225
227 point_scatter_density_random(grid, density, seed, positions);
228 }
230 point_scatter_density_grid(grid, spacing, threshold, positions);
231 }
232 }
233
234 PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size());
235 bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write();
236 pointcloud->positions_for_write().copy_from(positions);
238 point_attributes.lookup_or_add_for_write_only_span<float>("radius", AttrDomain::Point);
239
240 point_radii.span.fill(0.05f);
241 point_radii.finish();
242
244
245 geometry_set.replace_pointcloud(pointcloud);
247 });
248
249 params.set_output("Points", std::move(geometry_set));
250
251#else
253#endif
254}
255
256static void node_rna(StructRNA *srna)
257{
258 static const EnumPropertyItem mode_items[] = {
260 "DENSITY_RANDOM",
261 0,
262 "Random",
263 "Distribute points randomly inside of the volume"},
265 "DENSITY_GRID",
266 0,
267 "Grid",
268 "Distribute the points in a grid pattern inside of the volume"},
269 {0, nullptr, 0, nullptr, nullptr},
270 };
271
273 "mode",
274 "Distribution Method",
275 "Method to use for scattering points",
276 mode_items,
279}
280
281static void node_register()
282{
283 static blender::bke::bNodeType ntype;
284 geo_node_type_base(&ntype,
285 GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME,
286 "Distribute Points in Volume",
289 "NodeGeometryDistributePointsInVolume",
292 ntype.initfunc = node_init;
293 blender::bke::node_type_size(&ntype, 170, 100, 320);
294 ntype.declare = node_declare;
298
299 node_rna(ntype.rna_ext.srna);
300}
302
303} // namespace blender::nodes::node_geo_distribute_points_in_volume_cc
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
General operations for point clouds.
PointCloud * BKE_pointcloud_new_nomain(int totpoint)
Volume data-block.
int BKE_volume_num_grids(const Volume *volume)
bool BKE_volume_load(const Volume *volume, const Main *bmain)
const blender::bke::VolumeGridData * BKE_volume_grid_get(const Volume *volume, int grid_index)
#define BLT_I18NCONTEXT_ID_ID
GeometryNodeDistributePointsInVolumeMode
@ GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID
@ GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_storage_enum_accessors(member)
@ PROP_XYZ
Definition RNA_types.hh:172
@ PROP_NONE
Definition RNA_types.hh:136
#define UI_ITEM_NONE
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
static unsigned long seed
Definition btSoftBody.h:39
void append(const T &value)
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
local_group_size(16, 16) .push_constant(Type b
draw_view in_light_buf[] float
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float3 ceil(const float3 a)
static void add(blender::Map< std::string, std::string > &messages, Message &msg)
Definition msgfmt.cc:227
void node_type_size(bNodeType *ntype, int width, int minwidth, int maxwidth)
Definition node.cc:4602
void node_type_storage(bNodeType *ntype, const char *storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:4632
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
void debug_randomize_point_order(PointCloud *pointcloud)
Definition randomize.cc:181
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
void node_geo_exec_with_missing_openvdb(GeoNodeExecParams &params)
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:46
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:58
#define min(a, b)
Definition sort.c:32
#define FLT_MAX
Definition stdcycles.h:14
StructRNA * srna
Definition RNA_types.hh:780
void replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
void keep_only_during_modify(Span< GeometryComponent::Type > component_types)
const GeometryComponent * get_component(GeometryComponent::Type component_type) const
void modify_geometry_sets(ForeachSubGeometryCallback callback)
Defines a node type.
Definition BKE_node.hh:218
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeDeclareFunction declare
Definition BKE_node.hh:347
float z
Definition sky_float3.h:27
float y
Definition sky_float3.h:27
float x
Definition sky_float3.h:27
PointerRNA * ptr
Definition wm_files.cc:4126