Blender V5.0
node_geo_volume_to_mesh.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/tools/GridTransformer.h>
7# include <openvdb/tools/VolumeToMesh.h>
8#endif
9
10#include "node_geometry_util.hh"
11
12#include "BKE_material.hh"
13#include "BKE_mesh.hh"
14#include "BKE_volume.hh"
15#include "BKE_volume_grid.hh"
16#include "BKE_volume_to_mesh.hh"
17
19#include "GEO_randomize.hh"
20
22
24
27 "GRID",
28 0,
30 N_("Use resolution of the volume grid")},
32 "VOXEL_AMOUNT",
33 0,
35 N_("Desired number of voxels along one axis")},
37 "VOXEL_SIZE",
38 0,
40 N_("Desired voxel side length")},
41 {0, nullptr, 0, nullptr, nullptr},
42};
43
45{
46 b.add_input<decl::Geometry>("Volume")
47 .supported_type(GeometryComponent::Type::Volume)
49 .is_default_link_socket()
50 .description("Volume to convert to a mesh");
51 b.add_input<decl::Menu>("Resolution Mode")
52 .static_items(resolution_mode_items)
54 .description("How the voxel size is specified")
55 .translation_context(BLT_I18NCONTEXT_COUNTABLE);
56 b.add_input<decl::Float>("Voxel Size")
57 .default_value(0.3f)
58 .min(0.01f)
60 .usage_by_single_menu(VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE);
61 b.add_input<decl::Float>("Voxel Amount")
62 .default_value(64.0f)
63 .min(0.0f)
65 b.add_input<decl::Float>("Threshold")
66 .default_value(0.1f)
67 .description("Values larger than the threshold are inside the generated mesh");
68 b.add_input<decl::Float>("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
69 b.add_output<decl::Geometry>("Mesh");
70}
71
72static void node_init(bNodeTree * /*tree*/, bNode *node)
73{
74 /* Still used for forward compatibility. */
76}
77
78#ifdef WITH_OPENVDB
79
80static bke::VolumeToMeshResolution get_resolution_param(const GeoNodeExecParams &params)
81{
83 resolution.mode = params.get_input<VolumeToMeshResolutionMode>("Resolution Mode");
85 resolution.settings.voxel_amount = std::max(params.get_input<float>("Voxel Amount"), 0.0f);
86 }
87 else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) {
88 resolution.settings.voxel_size = std::max(params.get_input<float>("Voxel Size"), 0.0f);
89 }
90
91 return resolution;
92}
93
94static Mesh *create_mesh_from_volume_grids(Span<const openvdb::GridBase *> grids,
95 GeoNodeExecParams &params,
96 const float threshold,
97 const float adaptivity,
98 const bke::VolumeToMeshResolution &resolution)
99{
100 Array<bke::VolumeToMeshDataResult> mesh_data(grids.size());
101 for (const int i : grids.index_range()) {
102 bke::VolumeToMeshDataResult &result = mesh_data[i];
103 result = bke::volume_to_mesh_data(*grids[i], resolution, threshold, adaptivity);
104 if (!result.error.empty()) {
105 params.error_message_add(NodeWarningType::Error, result.error);
106 }
107 }
108
109 int vert_offset = 0;
110 int face_offset = 0;
111 int loop_offset = 0;
112 Array<int> vert_offsets(mesh_data.size());
113 Array<int> face_offsets(mesh_data.size());
114 Array<int> loop_offsets(mesh_data.size());
115 for (const int i : grids.index_range()) {
116 const bke::OpenVDBMeshData &data = mesh_data[i].data;
117 vert_offsets[i] = vert_offset;
118 face_offsets[i] = face_offset;
119 loop_offsets[i] = loop_offset;
120 vert_offset += data.verts.size();
121 face_offset += (data.tris.size() + data.quads.size());
122 loop_offset += (3 * data.tris.size() + 4 * data.quads.size());
123 }
124
125 Mesh *mesh = BKE_mesh_new_nomain(vert_offset, 0, face_offset, loop_offset);
127 MutableSpan<float3> positions = mesh->vert_positions_for_write();
128 MutableSpan<int> dst_face_offsets = mesh->face_offsets_for_write();
129 MutableSpan<int> corner_verts = mesh->corner_verts_for_write();
130
131 for (const int i : grids.index_range()) {
132 const bke::OpenVDBMeshData &data = mesh_data[i].data;
133 bke::fill_mesh_from_openvdb_data(data.verts,
134 data.tris,
135 data.quads,
136 vert_offsets[i],
137 face_offsets[i],
138 loop_offsets[i],
139 positions,
140 dst_face_offsets,
141 corner_verts);
142 }
143
144 bke::mesh_calc_edges(*mesh, false, false);
145 bke::mesh_smooth_set(*mesh, false);
146
147 mesh->tag_overlapping_none();
148
150
151 return mesh;
152}
153
154static Mesh *create_mesh_from_volume(GeometrySet &geometry_set, GeoNodeExecParams &params)
155{
156 const Volume *volume = geometry_set.get_volume();
157 if (volume == nullptr) {
158 return nullptr;
159 }
160
161 const bke::VolumeToMeshResolution resolution = get_resolution_param(params);
162
163 if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE &&
164 resolution.settings.voxel_size <= 0.0f)
165 {
166 return nullptr;
167 }
168 if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT &&
169 resolution.settings.voxel_amount <= 0)
170 {
171 return nullptr;
172 }
173
174 BKE_volume_load(volume, params.bmain());
175
176 Vector<bke::VolumeTreeAccessToken> tree_tokens;
177 Vector<const openvdb::GridBase *> grids;
178 for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
179 const bke::VolumeGridData *volume_grid = BKE_volume_grid_get(volume, i);
180 tree_tokens.append_as();
181 grids.append(&volume_grid->grid(tree_tokens.last()));
182 }
183
184 if (grids.is_empty()) {
185 return nullptr;
186 }
187
188 return create_mesh_from_volume_grids(grids,
189 params,
190 params.get_input<float>("Threshold"),
191 params.get_input<float>("Adaptivity"),
192 resolution);
193}
194
195#endif /* WITH_OPENVDB */
196
198{
199#ifdef WITH_OPENVDB
200 GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume");
201 geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
202 Mesh *mesh = create_mesh_from_volume(geometry_set, params);
203 geometry_set.replace_mesh(mesh);
204 geometry_set.keep_only({GeometryComponent::Type::Mesh, GeometryComponent::Type::Edit});
205 });
206 params.set_output("Mesh", std::move(geometry_set));
207#else
209#endif
210}
211
212static void node_register()
213{
214 static blender::bke::bNodeType ntype;
215
216 geo_node_type_base(&ntype, "GeometryNodeVolumeToMesh", GEO_NODE_VOLUME_TO_MESH);
217 ntype.ui_name = "Volume to Mesh";
218 ntype.ui_description = "Generate a mesh on the \"surface\" of a volume";
219 ntype.enum_name_legacy = "VOLUME_TO_MESH";
221 ntype.declare = node_declare;
223 ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage);
224 blender::bke::node_type_size(ntype, 170, 120, 700);
225 ntype.initfunc = node_init;
228}
230
231} // namespace blender::nodes::node_geo_volume_to_mesh_cc
General operations, lookup, etc. for materials.
void BKE_id_material_eval_ensure_default_slot(ID *id)
Mesh * BKE_mesh_new_nomain(int verts_num, int edges_num, int faces_num, int corners_num)
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1240
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#define GEO_NODE_VOLUME_TO_MESH
Volume data-block.
int BKE_volume_num_grids(const Volume *volume)
bool BKE_volume_load(const Volume *volume, const Main *bmain)
const blender::bke::VolumeGridData * BKE_volume_grid_get(const Volume *volume, int grid_index)
#define CTX_N_(context, msgid)
#define BLT_I18NCONTEXT_ID_ID
#define BLT_I18NCONTEXT_COUNTABLE
struct Mesh Mesh
VolumeToMeshResolutionMode
@ VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE
@ VOLUME_TO_MESH_RESOLUTION_MODE_GRID
@ VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT
struct Volume Volume
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_DISTANCE
Definition RNA_types.hh:256
@ PROP_FACTOR
Definition RNA_types.hh:251
BMesh const char void * data
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
void append(const T &value)
const T & last(const int64_t n=0) const
bool is_empty() const
void append_as(ForwardValue &&...value)
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:5384
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
void mesh_smooth_set(Mesh &mesh, bool use_smooth, bool keep_sharp_edges=false)
void mesh_calc_edges(Mesh &mesh, bool keep_existing_edges, bool select_new_edges)
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
void foreach_real_geometry(bke::GeometrySet &geometry, FunctionRef< void(bke::GeometrySet &geometry_set)> fn)
void debug_randomize_mesh_order(Mesh *mesh)
Definition randomize.cc:288
static void node_declare(NodeDeclarationBuilder &b)
static void node_geo_exec(GeoNodeExecParams params)
static void node_init(bNodeTree *, bNode *node)
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)
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
const Volume * get_volume() const
void * storage
void keep_only(Span< GeometryComponent::Type > component_types)
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
union blender::bke::VolumeToMeshResolution::@103176042355372060356022076152214323072307172372 settings
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
i
Definition text_draw.cc:230
#define N_(msgid)