Blender V5.0
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
17
18#include "BKE_lib_id.hh"
19#include "BKE_volume.hh"
20
22
23#ifdef WITH_OPENVDB
24
25static void gather_point_data_from_component(Field<float> radius_field,
26 const GeometryComponent &component,
27 Vector<float3> &r_positions,
28 Vector<float> &r_radii)
29{
30 if (component.is_empty()) {
31 return;
32 }
33 const VArray<float3> positions = *component.attributes()->lookup<float3>("position");
34
35 const bke::GeometryFieldContext field_context{component, AttrDomain::Point};
36 const int domain_num = component.attribute_domain_size(AttrDomain::Point);
37
38 r_positions.resize(r_positions.size() + domain_num);
39 positions.materialize(r_positions.as_mutable_span().take_back(domain_num));
40
41 r_radii.resize(r_radii.size() + domain_num);
42 fn::FieldEvaluator evaluator{field_context, domain_num};
43 evaluator.add_with_destination(radius_field, r_radii.as_mutable_span().take_back(domain_num));
44 evaluator.evaluate();
45}
46
47static float compute_voxel_size_from_amount(const float voxel_amount,
48 Span<float3> positions,
49 const float radius)
50{
51 if (positions.is_empty()) {
52 return 0.0f;
53 }
54
55 if (voxel_amount <= 1) {
56 return 0.0f;
57 }
58
59 const Bounds<float3> bounds = *bounds::min_max(positions);
60
61 /* The voxel size adapts to the final size of the volume. */
62 const float diagonal = math::distance(bounds.min, bounds.max);
63 const float extended_diagonal = diagonal + 2.0f * radius;
64 const float voxel_size = extended_diagonal / voxel_amount;
65 return voxel_size;
66}
67
72static void initialize_volume_component_from_points(GeoNodeExecParams &params,
73 GeometrySet &r_geometry_set)
74{
75 Vector<float3> positions;
76 Vector<float> radii;
77 Field<float> radius_field = params.get_input<Field<float>>("Radius");
78
79 for (const GeometryComponent::Type type : {GeometryComponent::Type::Mesh,
80 GeometryComponent::Type::PointCloud,
81 GeometryComponent::Type::Curve})
82 {
83 if (r_geometry_set.has(type)) {
84 gather_point_data_from_component(
85 radius_field, *r_geometry_set.get_component(type), positions, radii);
86 }
87 }
88
89 if (positions.is_empty()) {
90 return;
91 }
92
93 const auto resolution_mode = params.get_input<GeometryNodePointsToVolumeResolutionMode>(
94 "Resolution Mode");
95
96 float voxel_size = 0.0f;
98 voxel_size = params.get_input<float>("Voxel Size");
99 }
100 else if (resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT) {
101 const float voxel_amount = params.get_input<float>("Voxel Amount");
102 const float max_radius = *std::max_element(radii.begin(), radii.end());
103 voxel_size = compute_voxel_size_from_amount(voxel_amount, positions, max_radius);
104 }
105 else {
106 BLI_assert_msg(0, "Unknown volume resolution mode");
107 }
108
109 if (!BKE_volume_voxel_size_valid(float3(voxel_size))) {
110 return;
111 }
112
113 Volume *volume = BKE_id_new_nomain<Volume>(nullptr);
114
115 const float density = params.get_input<float>("Density");
116 blender::geometry::fog_volume_grid_add_from_points(
117 volume, "density", positions, radii, voxel_size, density);
118
119 r_geometry_set.keep_only({GeometryComponent::Type::Volume, GeometryComponent::Type::Edit});
120 r_geometry_set.replace_volume(volume);
121}
122
123#endif /* WITH_OPENVDB */
124
126
129 "VOXEL_AMOUNT",
130 0,
132 N_("Specify the approximate number of voxels along the diagonal")},
134 "VOXEL_SIZE",
135 0,
136
138 N_("Specify the voxel side length")},
139 {0, nullptr, 0, nullptr, nullptr},
140};
141
143{
144 b.add_input<decl::Geometry>("Points").is_default_link_socket().description(
145 "Points which are converted to a volume");
146 b.add_input<decl::Float>("Density").default_value(1.0f).min(0.0f);
147 b.add_input<decl::Menu>("Resolution Mode")
148 .static_items(resolution_mode_items)
150 .description("How the voxel size is specified")
151 .translation_context(BLT_I18NCONTEXT_COUNTABLE);
152 b.add_input<decl::Float>("Voxel Size")
153 .default_value(0.3f)
154 .min(0.01f)
157 b.add_input<decl::Float>("Voxel Amount")
158 .default_value(64.0f)
159 .min(0.0f)
161 b.add_input<decl::Float>("Radius")
162 .default_value(0.5f)
163 .min(0.0f)
165 .field_on_all();
166 b.add_output<decl::Geometry>("Volume").translation_context(BLT_I18NCONTEXT_ID_ID);
167}
168
169static void node_init(bNodeTree * /*tree*/, bNode *node)
170{
171 /* Still used for forward compatibility. */
173}
174
176{
177#ifdef WITH_OPENVDB
178 GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
179 geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
180 initialize_volume_component_from_points(params, geometry_set);
181 });
182 params.set_output("Volume", std::move(geometry_set));
183#else
185#endif
186}
187
188static void node_register()
189{
190 static blender::bke::bNodeType ntype;
191
192 geo_node_type_base(&ntype, "GeometryNodePointsToVolume", GEO_NODE_POINTS_TO_VOLUME);
193 ntype.ui_name = "Points to Volume";
194 ntype.ui_description = "Generate a fog volume sphere around every point";
195 ntype.enum_name_legacy = "POINTS_TO_VOLUME";
198 ntype, "NodeGeometryPointsToVolume", node_free_standard_storage, node_copy_standard_storage);
199 bke::node_type_size(ntype, 170, 120, 700);
200 ntype.initfunc = node_init;
201 ntype.declare = node_declare;
204}
206
207} // namespace blender::nodes::node_geo_points_to_volume_cc
void * BKE_id_new_nomain(short type, const char *name)
Definition lib_id.cc:1519
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1240
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#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 CTX_N_(context, msgid)
#define BLT_I18NCONTEXT_ID_ID
#define BLT_I18NCONTEXT_COUNTABLE
GeometryNodePointsToVolumeResolutionMode
@ GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT
@ GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_DISTANCE
Definition RNA_types.hh:256
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
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:5384
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
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:55
void foreach_real_geometry(bke::GeometrySet &geometry, FunctionRef< void(bke::GeometrySet &geometry_set)> fn)
T distance(const T &a, const T &b)
static void node_geo_exec(GeoNodeExecParams params)
static void node_declare(NodeDeclarationBuilder &b)
static void node_init(bNodeTree *, bNode *node)
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
void * storage
void replace_volume(Volume *volume, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
void keep_only(Span< GeometryComponent::Type > component_types)
bool has(const GeometryComponent::Type component_type) const
const GeometryComponent * get_component(GeometryComponent::Type component_type) const
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
NodeDeclareFunction declare
Definition BKE_node.hh:362
#define N_(msgid)