Blender V4.5
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
84 for (const GeometryComponent::Type type : {GeometryComponent::Type::Mesh,
85 GeometryComponent::Type::PointCloud,
86 GeometryComponent::Type::Curve})
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 if (!BKE_volume_voxel_size_valid(float3(voxel_size))) {
112 return;
113 }
114
115 Volume *volume = BKE_id_new_nomain<Volume>(nullptr);
116
117 const float density = params.get_input<float>("Density");
118 blender::geometry::fog_volume_grid_add_from_points(
119 volume, "density", positions, radii, voxel_size, density);
120
121 r_geometry_set.keep_only_during_modify({GeometryComponent::Type::Volume});
122 r_geometry_set.replace_volume(volume);
123}
124
125#endif /* WITH_OPENVDB */
126
128
130{
131 b.add_input<decl::Geometry>("Points");
132 b.add_input<decl::Float>("Density").default_value(1.0f).min(0.0f);
133 auto &voxel_size = b.add_input<decl::Float>("Voxel Size")
134 .default_value(0.3f)
135 .min(0.01f)
136 .subtype(PROP_DISTANCE)
137 .make_available([](bNode &node) {
138 node_storage(node).resolution_mode =
140 });
141 auto &voxel_amount = b.add_input<decl::Float>("Voxel Amount")
142 .default_value(64.0f)
143 .min(0.0f)
144 .make_available([](bNode &node) {
145 node_storage(node).resolution_mode =
147 });
148 b.add_input<decl::Float>("Radius")
149 .default_value(0.5f)
150 .min(0.0f)
151 .subtype(PROP_DISTANCE)
152 .field_on_all();
154
155 const bNode *node = b.node_or_null();
156 if (node != nullptr) {
157 const NodeGeometryPointsToVolume &data = node_storage(*node);
158 voxel_size.available(data.resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE);
159 voxel_amount.available(data.resolution_mode ==
161 }
162}
163
164static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
165{
166 uiLayoutSetPropSep(layout, true);
167 uiLayoutSetPropDecorate(layout, false);
168 layout->prop(ptr, "resolution_mode", UI_ITEM_NONE, IFACE_("Resolution"), ICON_NONE);
169}
170
171static void node_init(bNodeTree * /*tree*/, bNode *node)
172{
175 node->storage = data;
176}
177
179{
180#ifdef WITH_OPENVDB
181 GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
182 const NodeGeometryPointsToVolume &storage = node_storage(params.node());
183 geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
184 initialize_volume_component_from_points(params, storage, geometry_set);
185 });
186 params.set_output("Volume", std::move(geometry_set));
187#else
189#endif
190}
191
192static void node_rna(StructRNA *srna)
193{
194 static EnumPropertyItem resolution_mode_items[] = {
196 "VOXEL_AMOUNT",
197 0,
198 "Amount",
199 "Specify the approximate number of voxels along the diagonal"},
201 "VOXEL_SIZE",
202 0,
203 "Size",
204 "Specify the voxel side length"},
205 {0, nullptr, 0, nullptr, nullptr},
206 };
207
209 "resolution_mode",
210 "Resolution Mode",
211 "How the voxel size is specified",
212 resolution_mode_items,
213 NOD_storage_enum_accessors(resolution_mode),
215}
216
217static void node_register()
218{
219 static blender::bke::bNodeType ntype;
220
221 geo_node_type_base(&ntype, "GeometryNodePointsToVolume", GEO_NODE_POINTS_TO_VOLUME);
222 ntype.ui_name = "Points to Volume";
223 ntype.ui_description = "Generate a fog volume sphere around every point";
224 ntype.enum_name_legacy = "POINTS_TO_VOLUME";
227 ntype, "NodeGeometryPointsToVolume", node_free_standard_storage, node_copy_standard_storage);
228 bke::node_type_size(ntype, 170, 120, 700);
229 ntype.initfunc = node_init;
230 ntype.declare = node_declare;
234
235 node_rna(ntype.rna_ext.srna);
236}
238
239} // namespace blender::nodes::node_geo_points_to_volume_cc
void * BKE_id_new_nomain(short type, const char *name)
Definition lib_id.cc:1500
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1215
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:447
#define GEO_NODE_POINTS_TO_VOLUME
Volume data-block.
bool BKE_volume_voxel_size_valid(const blender::float3 &voxel_size)
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define BLT_I18NCONTEXT_ID_ID
#define IFACE_(msgid)
@ 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:244
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
BMesh const char void * data
constexpr bool is_empty() const
Definition BLI_span.hh:260
void materialize(MutableSpan< T > r_span) const
int64_t size() const
bool is_empty() 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:738
std::optional< std::string > translation_context
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void node_type_size(bNodeType &ntype, int width, int minwidth, int maxwidth)
Definition node.cc:5573
void node_register_type(bNodeType &ntype)
Definition node.cc:2748
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:5603
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:55
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)
VecBase< float, 3 > float3
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:42
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:54
#define min(a, b)
Definition sort.cc:36
StructRNA * srna
Definition RNA_types.hh:909
void * storage
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:226
std::string ui_description
Definition BKE_node.hh:232
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:277
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:347
const char * enum_name_legacy
Definition BKE_node.hh:235
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:355
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:4227