Blender V4.3
node_geo_points_to_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/LevelSetUtil.h>
8# include <openvdb/tools/ParticlesToLevelSet.h>
9#endif
10
11#include "BLI_bounds.hh"
12
13#include "node_geometry_util.hh"
14
16
17#include "BKE_lib_id.hh"
18#include "BKE_volume.hh"
19
20#include "NOD_rna_define.hh"
21
22#include "UI_interface.hh"
23#include "UI_resources.hh"
24
26
27#ifdef WITH_OPENVDB
28
29static void gather_point_data_from_component(Field<float> radius_field,
30 const GeometryComponent &component,
31 Vector<float3> &r_positions,
32 Vector<float> &r_radii)
33{
34 if (component.is_empty()) {
35 return;
36 }
37 const VArray<float3> positions = *component.attributes()->lookup<float3>("position");
38
39 const bke::GeometryFieldContext field_context{component, AttrDomain::Point};
40 const int domain_num = component.attribute_domain_size(AttrDomain::Point);
41
42 r_positions.resize(r_positions.size() + domain_num);
43 positions.materialize(r_positions.as_mutable_span().take_back(domain_num));
44
45 r_radii.resize(r_radii.size() + domain_num);
46 fn::FieldEvaluator evaluator{field_context, domain_num};
47 evaluator.add_with_destination(radius_field, r_radii.as_mutable_span().take_back(domain_num));
48 evaluator.evaluate();
49}
50
51static float compute_voxel_size_from_amount(const float voxel_amount,
52 Span<float3> positions,
53 const float radius)
54{
55 if (positions.is_empty()) {
56 return 0.0f;
57 }
58
59 if (voxel_amount <= 1) {
60 return 0.0f;
61 }
62
63 const Bounds<float3> bounds = *bounds::min_max(positions);
64
65 /* The voxel size adapts to the final size of the volume. */
66 const float diagonal = math::distance(bounds.min, bounds.max);
67 const float extended_diagonal = diagonal + 2.0f * radius;
68 const float voxel_size = extended_diagonal / voxel_amount;
69 return voxel_size;
70}
71
76static void initialize_volume_component_from_points(GeoNodeExecParams &params,
77 const NodeGeometryPointsToVolume &storage,
78 GeometrySet &r_geometry_set)
79{
80 Vector<float3> positions;
81 Vector<float> radii;
82 Field<float> radius_field = params.get_input<Field<float>>("Radius");
83
87 {
88 if (r_geometry_set.has(type)) {
89 gather_point_data_from_component(
90 radius_field, *r_geometry_set.get_component(type), positions, radii);
91 }
92 }
93
94 if (positions.is_empty()) {
95 return;
96 }
97
98 float voxel_size = 0.0f;
100 voxel_size = params.get_input<float>("Voxel Size");
101 }
103 const float voxel_amount = params.get_input<float>("Voxel Amount");
104 const float max_radius = *std::max_element(radii.begin(), radii.end());
105 voxel_size = compute_voxel_size_from_amount(voxel_amount, positions, max_radius);
106 }
107 else {
108 BLI_assert_msg(0, "Unknown volume resolution mode");
109 }
110
111 const double determinant = std::pow(double(voxel_size), 3.0);
113 return;
114 }
115
116 Volume *volume = reinterpret_cast<Volume *>(BKE_id_new_nomain(ID_VO, nullptr));
117
118 const float density = params.get_input<float>("Density");
119 blender::geometry::fog_volume_grid_add_from_points(
120 volume, "density", positions, radii, voxel_size, density);
121
123 r_geometry_set.replace_volume(volume);
124}
125
126#endif /* WITH_OPENVDB */
127
129
131{
132 b.add_input<decl::Geometry>("Points");
133 b.add_input<decl::Float>("Density").default_value(1.0f).min(0.0f);
134 auto &voxel_size = b.add_input<decl::Float>("Voxel Size")
135 .default_value(0.3f)
136 .min(0.01f)
138 .make_available([](bNode &node) {
139 node_storage(node).resolution_mode =
141 });
142 auto &voxel_amount = b.add_input<decl::Float>("Voxel Amount")
143 .default_value(64.0f)
144 .min(0.0f)
145 .make_available([](bNode &node) {
146 node_storage(node).resolution_mode =
148 });
149 b.add_input<decl::Float>("Radius")
150 .default_value(0.5f)
151 .min(0.0f)
153 .field_on_all();
154 b.add_output<decl::Geometry>("Volume").translation_context(BLT_I18NCONTEXT_ID_ID);
155
156 const bNode *node = b.node_or_null();
157 if (node != nullptr) {
158 const NodeGeometryPointsToVolume &data = node_storage(*node);
159 voxel_size.available(data.resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE);
160 voxel_amount.available(data.resolution_mode ==
162 }
163}
164
165static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
166{
167 uiLayoutSetPropSep(layout, true);
168 uiLayoutSetPropDecorate(layout, false);
169 uiItemR(layout, ptr, "resolution_mode", UI_ITEM_NONE, IFACE_("Resolution"), ICON_NONE);
170}
171
172static void node_init(bNodeTree * /*tree*/, bNode *node)
173{
174 NodeGeometryPointsToVolume *data = MEM_cnew<NodeGeometryPointsToVolume>(__func__);
176 node->storage = data;
177}
178
180{
181#ifdef WITH_OPENVDB
182 GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
183 const NodeGeometryPointsToVolume &storage = node_storage(params.node());
184 geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
185 initialize_volume_component_from_points(params, storage, geometry_set);
186 });
187 params.set_output("Volume", std::move(geometry_set));
188#else
190#endif
191}
192
193static void node_rna(StructRNA *srna)
194{
195 static EnumPropertyItem resolution_mode_items[] = {
197 "VOXEL_AMOUNT",
198 0,
199 "Amount",
200 "Specify the approximate number of voxels along the diagonal"},
202 "VOXEL_SIZE",
203 0,
204 "Size",
205 "Specify the voxel side length"},
206 {0, nullptr, 0, nullptr, nullptr},
207 };
208
210 "resolution_mode",
211 "Resolution Mode",
212 "How the voxel size is specified",
213 resolution_mode_items,
214 NOD_storage_enum_accessors(resolution_mode),
216}
217
218static void node_register()
219{
220 static blender::bke::bNodeType ntype;
221
222 geo_node_type_base(&ntype, GEO_NODE_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY);
224 "NodeGeometryPointsToVolume",
227 bke::node_type_size(&ntype, 170, 120, 700);
228 ntype.initfunc = node_init;
229 ntype.declare = node_declare;
233
234 node_rna(ntype.rna_ext.srna);
235}
237
238} // namespace blender::nodes::node_geo_points_to_volume_cc
void * BKE_id_new_nomain(short type, const char *name)
Definition lib_id.cc:1487
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
Volume data-block.
bool BKE_volume_grid_determinant_valid(double determinant)
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
#define BLT_I18NCONTEXT_ID_ID
#define IFACE_(msgid)
@ ID_VO
@ GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT
@ GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_storage_enum_accessors(member)
@ PROP_DISTANCE
Definition RNA_types.hh:159
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
btScalar determinant() const
Return the determinant of the matrix.
int64_t size() const
void resize(const int64_t new_size)
MutableSpan< T > as_mutable_span()
int attribute_domain_size(AttrDomain domain) const
virtual std::optional< AttributeAccessor > attributes() const
virtual bool is_empty() const
int add_with_destination(GField field, GVMutableArray dst)
Definition field.cc:743
void make_available(bNode &node) const
local_group_size(16, 16) .push_constant(Type b
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
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
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:46
T distance(const T &a, const T &b)
static void node_geo_exec(GeoNodeExecParams params)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_declare(NodeDeclarationBuilder &b)
static void node_init(bNodeTree *, bNode *node)
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
StructRNA * srna
Definition RNA_types.hh:780
void replace_volume(Volume *volume, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
bool has(const GeometryComponent::Type component_type) const
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
PointerRNA * ptr
Definition wm_files.cc:4126