Blender V5.0
node_geo_distribute_points_in_grid.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
10# include <algorithm>
11#endif
12
13#include "DNA_node_types.h"
15
16#include "BKE_pointcloud.hh"
17#include "BKE_volume_grid.hh"
18
19#include "NOD_rna_define.hh"
20
22#include "UI_resources.hh"
23
24#include "GEO_randomize.hh"
25
26#include "node_geometry_util.hh"
27
29
30enum class DistributeMode {
31 Random = 0,
32 Grid = 1,
33};
34
36{
37 b.add_input<decl::Float>("Grid").hide_value().structure_type(StructureType::Grid);
38 auto &density = b.add_input<decl::Float>("Density")
39 .default_value(1.0f)
40 .min(0.0f)
41 .max(100000.0f)
43 .description(
44 "When combined with each voxel's value, determines the number of points "
45 "to sample per unit volume");
46 auto &seed = b.add_input<decl::Int>("Seed").min(-10000).max(10000).description(
47 "Seed used by the random number generator to generate random points");
48 auto &spacing = b.add_input<decl::Vector>("Spacing")
49 .default_value({0.3, 0.3, 0.3})
50 .min(0.0001f)
52 .description("Spacing between grid points");
53 auto &threshold = b.add_input<decl::Float>("Threshold")
54 .default_value(0.1f)
55 .min(0.0f)
56 .max(FLT_MAX)
57 .description("Minimum density of a voxel to contain a grid point");
58 b.add_output<decl::Geometry>("Points").propagate_all();
59
60 const bNode *node = b.node_or_null();
61 if (node != nullptr) {
62 const auto mode = DistributeMode(node->custom1);
63
64 density.available(mode == DistributeMode::Random);
65 seed.available(mode == DistributeMode::Random);
66 spacing.available(mode == DistributeMode::Grid);
67 threshold.available(mode == DistributeMode::Grid);
68 }
69}
70
71static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
72{
73 layout->prop(ptr, "mode", UI_ITEM_NONE, "", ICON_NONE);
74}
75
76static void node_init(bNodeTree * /*tree*/, bNode *node)
77{
78 node->custom1 = int16_t(DistributeMode::Random);
79}
80
81#ifdef WITH_OPENVDB
82/* Implements the interface required by #openvdb::tools::NonUniformPointScatter. */
83class PositionsVDBWrapper {
84 private:
85 Vector<float3> &vector_;
86
87 public:
88 PositionsVDBWrapper(Vector<float3> &vector) : vector_(vector) {}
89 PositionsVDBWrapper(const PositionsVDBWrapper &wrapper) = default;
90
91 void add(const openvdb::Vec3R &pos)
92 {
93 vector_.append(float3(float(pos[0]), float(pos[1]), float(pos[2])));
94 }
95};
96
97/* Use #std::mt19937 as a random number generator. It has a very long period and thus there should
98 * be no visible patterns in the generated points. */
99using RNGType = std::mt19937;
100/* Non-uniform scatter allows the amount of points to be scaled with the volume's density. */
101using NonUniformPointScatterVDB =
102 openvdb::tools::NonUniformPointScatter<PositionsVDBWrapper, RNGType>;
103
104static void point_scatter_density_random(const openvdb::FloatGrid &grid,
105 const float density,
106 const int seed,
107 Vector<float3> &r_positions)
108{
109 /* Setup and call into OpenVDB's point scatter API. */
110 PositionsVDBWrapper vdb_position_wrapper(r_positions);
111 RNGType random_generator(seed);
112 NonUniformPointScatterVDB point_scatter(vdb_position_wrapper, density, random_generator);
113 point_scatter(grid);
114}
115
116static void point_scatter_density_grid(const openvdb::FloatGrid &grid,
117 const float3 spacing,
118 const float threshold,
119 Vector<float3> &r_positions)
120{
121 const openvdb::Vec3d half_voxel(0.5, 0.5, 0.5);
122 const openvdb::Vec3d voxel_spacing(double(spacing.x) / grid.voxelSize().x(),
123 double(spacing.y) / grid.voxelSize().y(),
124 double(spacing.z) / grid.voxelSize().z());
125
126 /* Abort if spacing is zero. */
127 const double min_spacing = std::min({voxel_spacing.x(), voxel_spacing.y(), voxel_spacing.z()});
128 if (std::abs(min_spacing) < 0.0001) {
129 return;
130 }
131
132 /* Iterate through tiles and voxels on the grid. */
133 for (openvdb::FloatGrid::ValueOnCIter cell = grid.cbeginValueOn(); cell; ++cell) {
134 /* Check if the cell's value meets the minimum threshold. */
135 if (cell.getValue() < threshold) {
136 continue;
137 }
138 /* Compute the bounding box of each tile/voxel. */
139 const openvdb::CoordBBox bbox = cell.getBoundingBox();
140 const openvdb::Vec3d box_min = bbox.min().asVec3d() - half_voxel;
141 const openvdb::Vec3d box_max = bbox.max().asVec3d() + half_voxel;
142
143 /* Pick a starting point rounded up to the nearest possible point. */
144 double abs_spacing_x = std::abs(voxel_spacing.x());
145 double abs_spacing_y = std::abs(voxel_spacing.y());
146 double abs_spacing_z = std::abs(voxel_spacing.z());
147 const openvdb::Vec3d start(ceil(box_min.x() / abs_spacing_x) * abs_spacing_x,
148 ceil(box_min.y() / abs_spacing_y) * abs_spacing_y,
149 ceil(box_min.z() / abs_spacing_z) * abs_spacing_z);
150
151 /* Iterate through all possible points in box. */
152 for (double x = start.x(); x < box_max.x(); x += abs_spacing_x) {
153 for (double y = start.y(); y < box_max.y(); y += abs_spacing_y) {
154 for (double z = start.z(); z < box_max.z(); z += abs_spacing_z) {
155 /* Transform with grid matrix and add point. */
156 const openvdb::Vec3d idx_pos(x, y, z);
157 const openvdb::Vec3d local_pos = grid.indexToWorld(idx_pos);
158 r_positions.append({float(local_pos.x()), float(local_pos.y()), float(local_pos.z())});
159 }
160 }
161 }
162 }
163}
164
165#endif /* WITH_OPENVDB */
166
168{
169#ifdef WITH_OPENVDB
170 const bke::VolumeGrid<float> volume_grid = params.extract_input<bke::VolumeGrid<float>>("Grid");
171 if (!volume_grid) {
172 params.set_default_remaining_outputs();
173 return;
174 }
175
176 bke::VolumeTreeAccessToken tree_token;
177 const openvdb::GridBase &base_grid = volume_grid.grid(tree_token);
178 if (!base_grid.isType<openvdb::FloatGrid>()) {
179 params.set_default_remaining_outputs();
180 return;
181 }
182 const openvdb::FloatGrid &grid = static_cast<const openvdb::FloatGrid &>(base_grid);
183
184 const DistributeMode mode = DistributeMode(params.node().custom1);
185
186 float density;
187 int seed;
188 float3 spacing{0, 0, 0};
189 float threshold;
190 if (mode == DistributeMode::Random) {
191 density = params.extract_input<float>("Density");
192 seed = params.extract_input<int>("Seed");
193 }
194 else if (mode == DistributeMode::Grid) {
195 spacing = params.extract_input<float3>("Spacing");
196 threshold = params.extract_input<float>("Threshold");
197 }
198
199 Vector<float3> positions;
200 switch (mode) {
202 point_scatter_density_random(grid, density, seed, positions);
203 break;
205 point_scatter_density_grid(grid, spacing, threshold, positions);
206 break;
207 }
208
209 PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size());
210 pointcloud->positions_for_write().copy_from(positions);
211
213
214 params.set_output("Points", GeometrySet::from_pointcloud(pointcloud));
215#else
217#endif
218}
219
220static void node_rna(StructRNA *srna)
221{
222 static const EnumPropertyItem mode_items[] = {
224 "DENSITY_RANDOM",
225 0,
226 "Random",
227 "Distribute points randomly inside of the volume"},
229 "DENSITY_GRID",
230 0,
231 "Grid",
232 "Distribute the points in a grid pattern inside of the volume"},
233 {0, nullptr, 0, nullptr, nullptr},
234 };
235
237 "mode",
238 "Distribution Method",
239 "Method to use for scattering points",
240 mode_items,
243}
244
245static void node_register()
246{
247 static blender::bke::bNodeType ntype;
249 &ntype, "GeometryNodeDistributePointsInGrid", GEO_NODE_DISTRIBUTE_POINTS_IN_GRID);
250 ntype.ui_name = "Distribute Points in Grid";
251 ntype.ui_description = "Generate points inside a volume grid";
252 ntype.enum_name_legacy = "DISTRIBUTE_POINTS_IN_GRID";
254 ntype.initfunc = node_init;
255 blender::bke::node_type_size(ntype, 170, 100, 320);
256 ntype.declare = node_declare;
260
261 node_rna(ntype.rna_ext.srna);
262}
264
265} // namespace blender::nodes::node_geo_distribute_points_in_grid_cc
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#define GEO_NODE_DISTRIBUTE_POINTS_IN_GRID
General operations for point clouds.
PointCloud * BKE_pointcloud_new_nomain(int totpoint)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_inline_enum_accessors(member)
@ PROP_XYZ
Definition RNA_types.hh:269
@ PROP_NONE
Definition RNA_types.hh:233
#define UI_ITEM_NONE
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)
int64_t size() const
void append(const T &value)
nullptr float
uint pos
#define ceil
VecBase< float, 3 > float3
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
static void add(blender::Map< std::string, std::string > &messages, Message &msg)
Definition msgfmt.cc:222
void node_type_size(bNodeType &ntype, int width, int minwidth, int maxwidth)
Definition node.cc:5384
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
void debug_randomize_point_order(PointCloud *pointcloud)
Definition randomize.cc:242
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)
VecBase< float, 3 > float3
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
#define min(a, b)
Definition sort.cc:36
#define FLT_MAX
Definition stdcycles.h:14
StructRNA * srna
int16_t custom1
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
const char * enum_name_legacy
Definition BKE_node.hh:247
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:259
NodeDeclareFunction declare
Definition BKE_node.hh:362
static GeometrySet from_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
float z
Definition sky_math.h:136
float y
Definition sky_math.h:136
float x
Definition sky_math.h:136
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
PointerRNA * ptr
Definition wm_files.cc:4238