Blender V4.3
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#include <fmt/format.h>
6#include <vector>
7
9#include "BLI_span.hh"
10#include "BLI_utildefines.h"
11
12#include "BKE_mesh.hh"
13#include "BKE_volume_grid.hh"
14#include "BKE_volume_openvdb.hh"
15
16#ifdef WITH_OPENVDB
17# include <openvdb/tools/GridTransformer.h>
18# include <openvdb/tools/VolumeToMesh.h>
19#endif
20
21#include "BKE_volume_to_mesh.hh"
22
23#include "BLT_translation.hh"
24
25namespace blender::bke {
26
27#ifdef WITH_OPENVDB
28
29struct VolumeToMeshOp {
30 const openvdb::GridBase &base_grid;
31 const VolumeToMeshResolution resolution;
32 const float threshold;
33 const float adaptivity;
34 std::vector<openvdb::Vec3s> verts;
35 std::vector<openvdb::Vec3I> tris;
36 std::vector<openvdb::Vec4I> quads;
37 std::string error;
38
39 template<typename GridType> bool operator()()
40 {
41 if constexpr (std::is_scalar_v<typename GridType::ValueType>) {
42 this->generate_mesh_data<GridType>();
43 return true;
44 }
45 return false;
46 }
47
48 template<typename GridType> void generate_mesh_data()
49 {
50 const GridType &grid = static_cast<const GridType &>(base_grid);
51
52 if (this->resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_GRID) {
53 this->grid_to_mesh(grid);
54 return;
55 }
56
57 const float resolution_factor = this->compute_resolution_factor(base_grid);
58 typename GridType::Ptr temp_grid = this->create_grid_with_changed_resolution(
59 grid, resolution_factor);
60 this->grid_to_mesh(*temp_grid);
61 }
62
63 template<typename GridType>
64 typename GridType::Ptr create_grid_with_changed_resolution(const GridType &old_grid,
65 const float resolution_factor)
66 {
67 BLI_assert(resolution_factor > 0.0f);
68
69 openvdb::Mat4R xform;
70 xform.setToScale(openvdb::Vec3d(resolution_factor));
71 openvdb::tools::GridTransformer transformer{xform};
72
73 typename GridType::Ptr new_grid = GridType::create();
74 transformer.transformGrid<openvdb::tools::BoxSampler>(old_grid, *new_grid);
75 new_grid->transform() = old_grid.transform();
76 new_grid->transform().preScale(1.0f / resolution_factor);
77 return new_grid;
78 }
79
80 float compute_resolution_factor(const openvdb::GridBase &grid) const
81 {
82 const openvdb::Vec3s voxel_size{grid.voxelSize()};
83 const float current_voxel_size = std::max({voxel_size[0], voxel_size[1], voxel_size[2]});
84 const float desired_voxel_size = this->compute_desired_voxel_size(grid);
85 return current_voxel_size / desired_voxel_size;
86 }
87
88 float compute_desired_voxel_size(const openvdb::GridBase &grid) const
89 {
90 if (this->resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) {
91 return this->resolution.settings.voxel_size;
92 }
93 const openvdb::CoordBBox coord_bbox = base_grid.evalActiveVoxelBoundingBox();
94 const openvdb::BBoxd bbox = grid.transform().indexToWorld(coord_bbox);
95 const float max_extent = bbox.extents()[bbox.maxExtent()];
96 const float voxel_size = max_extent / this->resolution.settings.voxel_amount;
97 return voxel_size;
98 }
99
100 template<typename GridType> void grid_to_mesh(const GridType &grid)
101 {
102 try {
103 openvdb::tools::volumeToMesh(
104 grid, this->verts, this->tris, this->quads, this->threshold, this->adaptivity);
105 }
106 catch (const std::exception &e) {
107 this->error = fmt::format(TIP_("OpenVDB error: {}"), e.what());
108 this->verts.clear();
109 this->tris.clear();
110 this->quads.clear();
111 }
112
113 /* Better align generated mesh with volume (see #85312). */
114 openvdb::Vec3s offset = grid.voxelSize() / 2.0f;
115 for (openvdb::Vec3s &position : this->verts) {
116 position += offset;
117 }
118 }
119};
120
121void fill_mesh_from_openvdb_data(const Span<openvdb::Vec3s> vdb_verts,
122 const Span<openvdb::Vec3I> vdb_tris,
123 const Span<openvdb::Vec4I> vdb_quads,
124 const int vert_offset,
125 const int face_offset,
126 const int loop_offset,
127 MutableSpan<float3> vert_positions,
128 MutableSpan<int> face_offsets,
129 MutableSpan<int> corner_verts)
130{
131 /* Write vertices. */
132 vert_positions.slice(vert_offset, vdb_verts.size()).copy_from(vdb_verts.cast<float3>());
133
134 /* Write triangles. */
135 for (const int i : vdb_tris.index_range()) {
136 face_offsets[face_offset + i] = loop_offset + 3 * i;
137 for (int j = 0; j < 3; j++) {
138 /* Reverse vertex order to get correct normals. */
139 corner_verts[loop_offset + 3 * i + j] = vert_offset + vdb_tris[i][2 - j];
140 }
141 }
142
143 /* Write quads. */
144 const int quad_offset = face_offset + vdb_tris.size();
145 const int quad_loop_offset = loop_offset + vdb_tris.size() * 3;
146 for (const int i : vdb_quads.index_range()) {
147 face_offsets[quad_offset + i] = quad_loop_offset + 4 * i;
148 for (int j = 0; j < 4; j++) {
149 /* Reverse vertex order to get correct normals. */
150 corner_verts[quad_loop_offset + 4 * i + j] = vert_offset + vdb_quads[i][3 - j];
151 }
152 }
153}
154
155bke::VolumeToMeshDataResult volume_to_mesh_data(const openvdb::GridBase &grid,
156 const VolumeToMeshResolution &resolution,
157 const float threshold,
158 const float adaptivity)
159{
160 const VolumeGridType grid_type = bke::volume_grid::get_type(grid);
161
162 VolumeToMeshOp to_mesh_op{grid, resolution, threshold, adaptivity};
163 if (!BKE_volume_grid_type_operation(grid_type, to_mesh_op)) {
164 return {};
165 }
166 return {{std::move(to_mesh_op.verts), std::move(to_mesh_op.tris), std::move(to_mesh_op.quads)},
167 to_mesh_op.error};
168}
169
170Mesh *volume_to_mesh(const openvdb::GridBase &grid,
171 const VolumeToMeshResolution &resolution,
172 const float threshold,
173 const float adaptivity)
174{
175 using namespace blender::bke;
176 const OpenVDBMeshData mesh_data =
177 volume_to_mesh_data(grid, resolution, threshold, adaptivity).data;
178
179 const int tot_loops = 3 * mesh_data.tris.size() + 4 * mesh_data.quads.size();
180 const int tot_faces = mesh_data.tris.size() + mesh_data.quads.size();
181 Mesh *mesh = BKE_mesh_new_nomain(mesh_data.verts.size(), 0, tot_faces, tot_loops);
182
183 fill_mesh_from_openvdb_data(mesh_data.verts,
184 mesh_data.tris,
185 mesh_data.quads,
186 0,
187 0,
188 0,
189 mesh->vert_positions_for_write(),
190 mesh->face_offsets_for_write(),
191 mesh->corner_verts_for_write());
192
193 mesh_calc_edges(*mesh, false, false);
194 mesh_smooth_set(*mesh, false);
195
196 mesh->tag_overlapping_none();
197
198 return mesh;
199}
200
201Mesh *volume_grid_to_mesh(const openvdb::GridBase &grid,
202 const float threshold,
203 const float adaptivity)
204{
205 return volume_to_mesh(grid, {VOLUME_TO_MESH_RESOLUTION_MODE_GRID}, threshold, adaptivity);
206}
207
208#endif /* WITH_OPENVDB */
209
210} // namespace blender::bke
Mesh * BKE_mesh_new_nomain(int verts_num, int edges_num, int faces_num, int corners_num)
VolumeGridType
#define BLI_assert(a)
Definition BLI_assert.h:50
#define TIP_(msgid)
@ VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE
@ VOLUME_TO_MESH_RESOLUTION_MODE_GRID
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
SIMD_FORCE_INLINE btVector3 operator()(const btVector3 &x) const
Return the transform of the vector.
Definition btTransform.h:90
static float verts[][3]
static void error(const char *str)
VolumeGridType get_type(const VolumeGridData &grid)
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)