Blender V5.0
node_geo_field_to_grid.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
7
12
14#include "UI_resources.hh"
15
16#include "RNA_enum_types.hh"
17#include "RNA_prototypes.hh"
18
19#include "BLO_read_write.hh"
20
21#ifdef WITH_OPENVDB
24#endif
25
27
30
31namespace grid = bke::volume_grid;
32
34{
35 b.use_custom_socket_order();
36 b.allow_any_socket_order();
37 b.add_default_layout();
38
39 const bNodeTree *tree = b.tree_or_null();
40 const bNode *node = b.node_or_null();
41 if (!node || !tree) {
42 return;
43 }
44 const GeometryNodeFieldToGrid &storage = node_storage(*node);
45 const eNodeSocketDatatype data_type = eNodeSocketDatatype(storage.data_type);
46
47 b.add_input(data_type, "Topology").structure_type(StructureType::Grid);
48
49 const Span<GeometryNodeFieldToGridItem> items(storage.items, storage.items_num);
50 for (const int i : items.index_range()) {
51 const GeometryNodeFieldToGridItem &item = items[i];
53 const std::string input_identifier = ItemsAccessor::input_socket_identifier_for_item(item);
54 const std::string output_identifier = ItemsAccessor::output_socket_identifier_for_item(item);
55
56 b.add_input(data_type, item.name, input_identifier)
57 .supports_field()
58 .socket_name_ptr(&tree->id, FieldToGridItemsAccessor::item_srna, &item, "name");
59 b.add_output(data_type, item.name, output_identifier)
60 .structure_type(StructureType::Grid)
61 .align_with_previous()
62 .description("Output grid with evaluated field values");
63 }
64
65 b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Field);
66 b.add_output<decl::Extend>("", "__extend__")
67 .structure_type(StructureType::Grid)
68 .align_with_previous();
69}
70
71static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
72{
73 layout->prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
74}
75
77{
78 bNodeTree &tree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
79 bNode &node = *static_cast<bNode *>(ptr->data);
80 if (uiLayout *panel = layout->panel(C, "field_to_grid_items", false, IFACE_("Fields"))) {
83 panel->use_property_split_set(true);
84 panel->use_property_decorate_set(false);
85 panel->prop(item_ptr, "data_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
86 });
87 }
88}
89
90static std::optional<eNodeSocketDatatype> node_type_for_socket_type(const bNodeSocket &socket)
91{
92 switch (socket.type) {
93 case SOCK_FLOAT:
94 return SOCK_FLOAT;
95 case SOCK_BOOLEAN:
96 return SOCK_BOOLEAN;
97 case SOCK_INT:
98 return SOCK_INT;
99 case SOCK_VECTOR:
100 case SOCK_RGBA:
101 return SOCK_VECTOR;
102 default:
103 return std::nullopt;
104 }
105}
106
108{
109 const std::optional<eNodeSocketDatatype> data_type = node_type_for_socket_type(
110 params.other_socket());
111 if (!data_type) {
112 return;
113 }
114 if (params.in_out() == SOCK_IN) {
115 params.add_item(IFACE_("Topology"), [data_type](LinkSearchOpParams &params) {
116 bNode &node = params.add_node("GeometryNodeFieldToGrid");
117 node_storage(node).data_type = *data_type;
118 params.update_and_connect_available_socket(node, "Topology");
119 });
120 params.add_item(IFACE_("Field"), [data_type](LinkSearchOpParams &params) {
121 bNode &node = params.add_node("GeometryNodeFieldToGrid");
123 params.node_tree, node, *data_type, params.socket.name);
124 params.update_and_connect_available_socket(node, params.socket.name);
125 });
126 }
127 else {
128 params.add_item(IFACE_("Grid"), [data_type](LinkSearchOpParams &params) {
129 bNode &node = params.add_node("GeometryNodeFieldToGrid");
131 params.node_tree, node, *data_type, params.socket.name);
132 params.update_and_connect_available_socket(node, params.socket.name);
133 });
134 }
135}
136
137#ifdef WITH_OPENVDB
138BLI_NOINLINE static void process_leaf_node(const Span<fn::GField> fields,
139 const openvdb::math::Transform &transform,
140 const grid::LeafNodeMask &leaf_node_mask,
141 const openvdb::CoordBBox &leaf_bbox,
142 const grid::GetVoxelsFn get_voxels_fn,
143 const Span<openvdb::GridBase::Ptr> output_grids)
144{
145 AlignedBuffer<8192, 8> allocation_buffer;
146 ResourceScope scope;
147 scope.allocator().provide_buffer(allocation_buffer);
148
149 IndexMaskMemory memory;
151 IndexRange(grid::LeafNodeMask::SIZE),
152 GrainSize(grid::LeafNodeMask::SIZE),
153 memory,
154 [&](const int64_t i) { return leaf_node_mask.isOn(i); });
155
156 const openvdb::Coord any_voxel_in_leaf = leaf_bbox.min();
157 MutableSpan<openvdb::Coord> voxels = scope.allocator().allocate_array<openvdb::Coord>(
158 index_mask.min_array_size());
159 get_voxels_fn(voxels);
160
161 bke::VoxelFieldContext field_context{transform, voxels};
162 fn::FieldEvaluator evaluator{field_context, &index_mask};
163
164 Array<MutableSpan<bool>> boolean_outputs(fields.size());
165 for (const int i : fields.index_range()) {
166 const CPPType &type = fields[i].cpp_type();
167 grid::to_typed_grid(*output_grids[i], [&](auto &grid) {
168 using GridT = typename std::decay_t<decltype(grid)>;
169 using ValueT = typename GridT::ValueType;
170
171 auto &tree = grid.tree();
172 auto *leaf_node = tree.probeLeaf(any_voxel_in_leaf);
173 /* Should have been added before. */
174 BLI_assert(leaf_node);
175
176 /* Boolean grids are special because they encode the values as bitmask. */
177 if constexpr (std::is_same_v<ValueT, bool>) {
178 boolean_outputs[i] = scope.allocator().allocate_array<bool>(index_mask.min_array_size());
179 evaluator.add_with_destination(fields[i], boolean_outputs[i]);
180 }
181 else {
182 /* Write directly into the buffer of the output leaf node. */
183 ValueT *buffer = leaf_node->buffer().data();
184 evaluator.add_with_destination(fields[i],
185 GMutableSpan(type, buffer, grid::LeafNodeMask::SIZE));
186 }
187 });
188 }
189
190 evaluator.evaluate();
191
192 for (const int i : fields.index_range()) {
193 if (!boolean_outputs[i].is_empty()) {
194 grid::set_mask_leaf_buffer_from_bools(static_cast<openvdb::BoolGrid &>(*output_grids[i]),
195 boolean_outputs[i],
196 index_mask,
197 voxels);
198 }
199 }
200}
201
202BLI_NOINLINE static void process_voxels(const Span<fn::GField> fields,
203 const openvdb::math::Transform &transform,
204 const Span<openvdb::Coord> voxels,
205 const Span<openvdb::GridBase::Ptr> output_grids)
206{
207 const int64_t voxels_num = voxels.size();
208 AlignedBuffer<8192, 8> allocation_buffer;
209 ResourceScope scope;
210 scope.allocator().provide_buffer(allocation_buffer);
211
212 bke::VoxelFieldContext field_context{transform, voxels};
213 fn::FieldEvaluator evaluator{field_context, voxels_num};
214
215 Array<GMutableSpan> output_values(output_grids.size());
216 for (const int i : fields.index_range()) {
217 const CPPType &type = fields[i].cpp_type();
218 output_values[i] = {type, scope.allocator().allocate_array(type, voxels_num), voxels_num};
219 evaluator.add_with_destination(fields[i], output_values[i]);
220 }
221 evaluator.evaluate();
222
223 for (const int i : fields.index_range()) {
224 grid::set_grid_values(*output_grids[i], output_values[i], voxels);
225 }
226}
227
228BLI_NOINLINE static void process_tiles(const Span<fn::GField> fields,
229 const openvdb::math::Transform &transform,
230 const Span<openvdb::CoordBBox> tiles,
231 const Span<openvdb::GridBase::Ptr> output_grids)
232{
233 const int64_t tiles_num = tiles.size();
234 AlignedBuffer<8192, 8> allocation_buffer;
235 ResourceScope scope;
236 scope.allocator().provide_buffer(allocation_buffer);
237
238 bke::TilesFieldContext field_context{transform, tiles};
239 fn::FieldEvaluator evaluator{field_context, tiles_num};
240
241 Array<GMutableSpan> output_values(output_grids.size());
242 for (const int i : fields.index_range()) {
243 const CPPType &type = fields[i].cpp_type();
244 output_values[i] = {type, scope.allocator().allocate_array(type, tiles_num), tiles_num};
245 evaluator.add_with_destination(fields[i], output_values[i]);
246 }
247 evaluator.evaluate();
248
249 for (const int i : fields.index_range()) {
250 grid::set_tile_values(*output_grids[i], output_values[i], tiles);
251 }
252}
253
254BLI_NOINLINE static void process_background(const Span<fn::GField> fields,
255 const openvdb::math::Transform &transform,
256 const Span<openvdb::GridBase::Ptr> output_grids)
257{
258 AlignedBuffer<256, 8> allocation_buffer;
259 ResourceScope scope;
260 scope.allocator().provide_buffer(allocation_buffer);
261
262 static const openvdb::CoordBBox background_space = openvdb::CoordBBox::inf();
263 bke::TilesFieldContext field_context(transform, Span<openvdb::CoordBBox>(&background_space, 1));
264 fn::FieldEvaluator evaluator(field_context, 1);
265
266 Array<GMutablePointer> output_values(output_grids.size());
267 for (const int i : fields.index_range()) {
268 const CPPType &type = fields[i].cpp_type();
269 output_values[i] = {type, scope.allocator().allocate(type)};
270 evaluator.add_with_destination(fields[i], GMutableSpan{type, output_values[i].get(), 1});
271 }
272 evaluator.evaluate();
273
274 for (const int i : fields.index_range()) {
275 grid::set_grid_background(*output_grids[i], output_values[i]);
276 }
277}
278#endif
279
281{
282#ifdef WITH_OPENVDB
283 const GeometryNodeFieldToGrid &storage = node_storage(params.node());
284 const Span<GeometryNodeFieldToGridItem> items(storage.items, storage.items_num);
285 bke::GVolumeGrid topology_grid = params.extract_input<bke::GVolumeGrid>("Topology");
286 if (!topology_grid) {
287 params.error_message_add(NodeWarningType::Error, "The topology grid input is required");
288 params.set_default_remaining_outputs();
289 return;
290 }
291
292 bke::VolumeTreeAccessToken tree_token;
293 const openvdb::GridBase &topology_base = topology_grid->grid(tree_token);
294 const openvdb::math::Transform &transform = topology_base.transform();
295
296 Vector<int> required_items;
297 for (const int i : items.index_range()) {
298 if (params.output_is_required(ItemsAccessor::output_socket_identifier_for_item(items[i]))) {
299 required_items.append(i);
300 }
301 }
302
303 Vector<fn::GField> fields(required_items.size());
304 for (const int i : required_items.index_range()) {
305 const int item_i = required_items[i];
306 const std::string identifier = ItemsAccessor::input_socket_identifier_for_item(items[item_i]);
307 fields[i] = params.extract_input<fn::GField>(identifier);
308 }
309
310 openvdb::MaskTree mask_tree;
311 grid::to_typed_grid(topology_base,
312 [&](const auto &grid) { mask_tree.topologyUnion(grid.tree()); });
313
314 Vector<openvdb::GridBase::Ptr> output_grids(required_items.size());
315 for (const int i : required_items.index_range()) {
316 const int item_i = required_items[i];
317 const eNodeSocketDatatype socket_type = eNodeSocketDatatype(items[item_i].data_type);
318 const VolumeGridType grid_type = *bke::socket_type_to_grid_type(socket_type);
319 output_grids[i] = grid::create_grid_with_topology(mask_tree, transform, grid_type);
320 }
321
322 grid::parallel_grid_topology_tasks(
323 mask_tree,
324 [&](const grid::LeafNodeMask &leaf_node_mask,
325 const openvdb::CoordBBox &leaf_bbox,
326 const grid::GetVoxelsFn get_voxels_fn) {
327 process_leaf_node(
328 fields, transform, leaf_node_mask, leaf_bbox, get_voxels_fn, output_grids);
329 },
330 [&](const Span<openvdb::Coord> voxels) {
331 process_voxels(fields, transform, voxels, output_grids);
332 },
334 process_tiles(fields, transform, tiles, output_grids);
335 });
336
337 process_background(fields, transform, output_grids);
338
339 for (const int i : required_items.index_range()) {
340 const int item_i = required_items[i];
341 const std::string identifier = ItemsAccessor::output_socket_identifier_for_item(items[item_i]);
342 params.set_output(identifier, bke::GVolumeGrid(std::move(output_grids[i])));
343 }
344
345#else
347#endif
348}
349
350static void node_init(bNodeTree * /*tree*/, bNode *node)
351{
353 data->data_type = SOCK_FLOAT;
354 node->storage = data;
355}
356
357static void node_free_storage(bNode *node)
358{
360 MEM_freeN(node->storage);
361}
362
363static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node)
364{
365 const GeometryNodeFieldToGrid &src_storage = node_storage(*src_node);
366 auto *dst_storage = MEM_dupallocN<GeometryNodeFieldToGrid>(__func__, src_storage);
367 dst_node->storage = dst_storage;
368
369 socket_items::copy_array<ItemsAccessor>(*src_node, *dst_node);
370}
371
376
382
383static void node_blend_write(const bNodeTree & /*tree*/, const bNode &node, BlendWriter &writer)
384{
386}
387
388static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader &reader)
389{
391}
392
394 const bNode &node,
395 const bNodeSocket &output_socket)
396{
397 return node.input_by_identifier(output_socket.identifier);
398}
399
400static void node_register()
401{
402 static blender::bke::bNodeType ntype;
403
404 geo_node_type_base(&ntype, "GeometryNodeFieldToGrid");
405 ntype.ui_name = "Field to Grid";
406 ntype.ui_description =
407 "Create new grids by evaluating new values on an existing volume grid topology";
409 ntype.declare = node_declare;
410 ntype.initfunc = node_init;
412 ntype, "GeometryNodeFieldToGrid", node_free_storage, node_copy_storage);
424}
426
427} // namespace blender::nodes::node_geo_field_to_grid_cc
428
429namespace blender::nodes {
430
431StructRNA *FieldToGridItemsAccessor::item_srna = &RNA_GeometryNodeFieldToGridItem;
432
434{
435 BLO_write_string(writer, item.name);
436}
437
442
443} // namespace blender::nodes
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1240
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
VolumeGridType
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_NOINLINE
void BLO_read_string(BlendDataReader *reader, char **ptr_p)
Definition readfile.cc:5828
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
#define IFACE_(msgid)
@ SOCK_IN
eNodeSocketDatatype
@ SOCK_INT
@ SOCK_VECTOR
@ SOCK_BOOLEAN
@ SOCK_FLOAT
@ SOCK_RGBA
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define C
Definition RandGen.cpp:29
#define UI_ITEM_NONE
BMesh const char void * data
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
long long int int64_t
static const CPPType & get()
LinearAllocator & allocator()
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
void provide_buffer(void *buffer, const int64_t size)
MutableSpan< T > allocate_array(int64_t size)
LinearAllocator & allocator()
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
int64_t size() const
void append(const T &value)
IndexRange index_range() const
KDTree_3d * tree
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_gpu_kernel_postfix ccl_global KernelWorkTile * tiles
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
std::optional< VolumeGridType > socket_type_to_grid_type(eNodeSocketDatatype type)
Definition node.cc:5298
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:5414
static std::optional< eNodeSocketDatatype > node_type_for_socket_type(const bNodeSocket &socket)
static void node_declare(NodeDeclarationBuilder &b)
static void node_gather_link_search_ops(GatherLinkSearchOpParams &params)
static bool node_insert_link(bke::NodeInsertLinkParams &params)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_copy_storage(bNodeTree *, bNode *dst_node, const bNode *src_node)
static void node_blend_read(bNodeTree &, bNode &node, BlendDataReader &reader)
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
static void node_geo_exec(GeoNodeExecParams params)
static void node_blend_write(const bNodeTree &, const bNode &node, BlendWriter &writer)
static void node_init(bNodeTree *, bNode *node)
static const bNodeSocket * node_internally_linked_input(const bNodeTree &, const bNode &node, const bNodeSocket &output_socket)
static void draw_items_list_with_operators(const bContext *C, uiLayout *layout, const bNodeTree &tree, const bNode &node)
static void draw_active_item_props(const bNodeTree &tree, const bNode &node, const FunctionRef< void(PointerRNA *item_ptr)> draw_item)
void blend_write(BlendWriter *writer, const bNode &node)
void blend_read_data(BlendDataReader *reader, bNode &node)
void copy_array(const bNode &src_node, bNode &dst_node)
Accessor::ItemT * add_item_with_socket_type_and_name(bNodeTree &ntree, bNode &node, const eNodeSocketDatatype socket_type, const char *name, std::optional< int > dimensions=std::nullopt)
bool try_add_item_via_any_extend_socket(bNodeTree &ntree, bNode &extend_node, bNode &storage_node, bNodeLink &link, const std::optional< StringRef > socket_identifier=std::nullopt, typename Accessor::ItemT **r_new_item=nullptr)
void node_geo_exec_with_missing_openvdb(GeoNodeExecParams &params)
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
GeometryNodeFieldToGridItem * items
char identifier[64]
void * storage
Defines a node type.
Definition BKE_node.hh:238
NodeInternallyLinkedInputFunction internally_linked_input
Definition BKE_node.hh:384
NodeBlendWriteFunction blend_write_storage_content
Definition BKE_node.hh:390
std::string ui_description
Definition BKE_node.hh:244
NodeBlendDataReadFunction blend_data_read_storage_content
Definition BKE_node.hh:391
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
void(* draw_buttons_ex)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:261
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:259
bool(* insert_link)(NodeInsertLinkParams &params)
Definition BKE_node.hh:333
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:378
NodeDeclareFunction declare
Definition BKE_node.hh:362
void(* register_operators)()
Definition BKE_node.hh:417
bool ignore_inferred_input_socket_visibility
Definition BKE_node.hh:422
static std::string input_socket_identifier_for_item(const GeometryNodeFieldToGridItem &item)
static void blend_write_item(BlendWriter *writer, const ItemT &item)
static void blend_read_data_item(BlendDataReader *reader, ItemT &item)
static std::string output_socket_identifier_for_item(const GeometryNodeFieldToGridItem &item)
PanelLayout panel(const bContext *C, blender::StringRef idname, bool default_closed)
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)
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4238