Blender  V3.3
volume_to_mesh.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include <vector>
4 
5 #include "BLI_math_vec_types.hh"
6 #include "BLI_span.hh"
7 #include "BLI_utildefines.h"
8 
9 #include "DNA_mesh_types.h"
10 #include "DNA_meshdata_types.h"
11 #include "DNA_volume_types.h"
12 
13 #include "BKE_mesh.h"
14 #include "BKE_volume.h"
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 namespace blender::bke {
24 
25 #ifdef WITH_OPENVDB
26 
27 struct VolumeToMeshOp {
28  const openvdb::GridBase &base_grid;
29  const VolumeToMeshResolution resolution;
30  const float threshold;
31  const float adaptivity;
32  std::vector<openvdb::Vec3s> verts;
33  std::vector<openvdb::Vec3I> tris;
34  std::vector<openvdb::Vec4I> quads;
35 
36  template<typename GridType> bool operator()()
37  {
38  if constexpr (std::is_scalar_v<typename GridType::ValueType>) {
39  this->generate_mesh_data<GridType>();
40  return true;
41  }
42  return false;
43  }
44 
45  template<typename GridType> void generate_mesh_data()
46  {
47  const GridType &grid = static_cast<const GridType &>(base_grid);
48 
49  if (this->resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_GRID) {
50  this->grid_to_mesh(grid);
51  return;
52  }
53 
54  const float resolution_factor = this->compute_resolution_factor(base_grid);
55  typename GridType::Ptr temp_grid = this->create_grid_with_changed_resolution(
56  grid, resolution_factor);
57  this->grid_to_mesh(*temp_grid);
58  }
59 
60  template<typename GridType>
61  typename GridType::Ptr create_grid_with_changed_resolution(const GridType &old_grid,
62  const float resolution_factor)
63  {
64  BLI_assert(resolution_factor > 0.0f);
65 
66  openvdb::Mat4R xform;
67  xform.setToScale(openvdb::Vec3d(resolution_factor));
68  openvdb::tools::GridTransformer transformer{xform};
69 
70  typename GridType::Ptr new_grid = GridType::create();
71  transformer.transformGrid<openvdb::tools::BoxSampler>(old_grid, *new_grid);
72  new_grid->transform() = old_grid.transform();
73  new_grid->transform().preScale(1.0f / resolution_factor);
74  return new_grid;
75  }
76 
77  float compute_resolution_factor(const openvdb::GridBase &grid) const
78  {
79  const openvdb::Vec3s voxel_size{grid.voxelSize()};
80  const float current_voxel_size = std::max({voxel_size[0], voxel_size[1], voxel_size[2]});
81  const float desired_voxel_size = this->compute_desired_voxel_size(grid);
82  return current_voxel_size / desired_voxel_size;
83  }
84 
85  float compute_desired_voxel_size(const openvdb::GridBase &grid) const
86  {
87  if (this->resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) {
88  return this->resolution.settings.voxel_size;
89  }
90  const openvdb::CoordBBox coord_bbox = base_grid.evalActiveVoxelBoundingBox();
91  const openvdb::BBoxd bbox = grid.transform().indexToWorld(coord_bbox);
92  const float max_extent = bbox.extents()[bbox.maxExtent()];
93  const float voxel_size = max_extent / this->resolution.settings.voxel_amount;
94  return voxel_size;
95  }
96 
97  template<typename GridType> void grid_to_mesh(const GridType &grid)
98  {
99  openvdb::tools::volumeToMesh(
100  grid, this->verts, this->tris, this->quads, this->threshold, this->adaptivity);
101 
102  /* Better align generated mesh with volume (see T85312). */
103  openvdb::Vec3s offset = grid.voxelSize() / 2.0f;
104  for (openvdb::Vec3s &position : this->verts) {
105  position += offset;
106  }
107  }
108 };
109 
110 void fill_mesh_from_openvdb_data(const Span<openvdb::Vec3s> vdb_verts,
111  const Span<openvdb::Vec3I> vdb_tris,
112  const Span<openvdb::Vec4I> vdb_quads,
113  const int vert_offset,
114  const int poly_offset,
115  const int loop_offset,
116  MutableSpan<MVert> verts,
117  MutableSpan<MPoly> polys,
118  MutableSpan<MLoop> loops)
119 {
120  /* Write vertices. */
121  for (const int i : vdb_verts.index_range()) {
122  const blender::float3 co = blender::float3(vdb_verts[i].asV());
123  copy_v3_v3(verts[vert_offset + i].co, co);
124  }
125 
126  /* Write triangles. */
127  for (const int i : vdb_tris.index_range()) {
128  polys[poly_offset + i].loopstart = loop_offset + 3 * i;
129  polys[poly_offset + i].totloop = 3;
130  for (int j = 0; j < 3; j++) {
131  /* Reverse vertex order to get correct normals. */
132  loops[loop_offset + 3 * i + j].v = vert_offset + vdb_tris[i][2 - j];
133  }
134  }
135 
136  /* Write quads. */
137  const int quad_offset = poly_offset + vdb_tris.size();
138  const int quad_loop_offset = loop_offset + vdb_tris.size() * 3;
139  for (const int i : vdb_quads.index_range()) {
140  polys[quad_offset + i].loopstart = quad_loop_offset + 4 * i;
141  polys[quad_offset + i].totloop = 4;
142  for (int j = 0; j < 4; j++) {
143  /* Reverse vertex order to get correct normals. */
144  loops[quad_loop_offset + 4 * i + j].v = vert_offset + vdb_quads[i][3 - j];
145  }
146  }
147 }
148 
149 bke::OpenVDBMeshData volume_to_mesh_data(const openvdb::GridBase &grid,
150  const VolumeToMeshResolution &resolution,
151  const float threshold,
152  const float adaptivity)
153 {
154  const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid);
155 
156  VolumeToMeshOp to_mesh_op{grid, resolution, threshold, adaptivity};
157  if (!BKE_volume_grid_type_operation(grid_type, to_mesh_op)) {
158  return {};
159  }
160  return {std::move(to_mesh_op.verts), std::move(to_mesh_op.tris), std::move(to_mesh_op.quads)};
161 }
162 
163 Mesh *volume_to_mesh(const openvdb::GridBase &grid,
164  const VolumeToMeshResolution &resolution,
165  const float threshold,
166  const float adaptivity)
167 {
168  const bke::OpenVDBMeshData mesh_data = volume_to_mesh_data(
169  grid, resolution, threshold, adaptivity);
170 
171  const int tot_loops = 3 * mesh_data.tris.size() + 4 * mesh_data.quads.size();
172  const int tot_polys = mesh_data.tris.size() + mesh_data.quads.size();
173  Mesh *mesh = BKE_mesh_new_nomain(mesh_data.verts.size(), 0, 0, tot_loops, tot_polys);
174 
175  fill_mesh_from_openvdb_data(mesh_data.verts,
176  mesh_data.tris,
177  mesh_data.quads,
178  0,
179  0,
180  0,
181  {mesh->mvert, mesh->totvert},
182  {mesh->mpoly, mesh->totpoly},
183  {mesh->mloop, mesh->totloop});
184 
185  BKE_mesh_calc_edges(mesh, false, false);
186 
187  return mesh;
188 }
189 
190 #endif /* WITH_OPENVDB */
191 
192 } // namespace blender::bke
struct Mesh * BKE_mesh_new_nomain(int verts_len, int edges_len, int tessface_len, int loops_len, int polys_len)
Definition: mesh.cc:991
void BKE_mesh_calc_edges(struct Mesh *mesh, bool keep_existing_edges, bool select_new_edges)
Volume data-block.
VolumeGridType
Definition: BKE_volume.h:90
#define BLI_assert(a)
Definition: BLI_assert.h:46
MINLINE void copy_v3_v3(float r[3], const float a[3])
@ VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE
@ VOLUME_TO_MESH_RESOLUTION_MODE_GRID
SIMD_FORCE_INLINE btVector3 operator()(const btVector3 &x) const
Return the transform of the vector.
Definition: btTransform.h:90
static float verts[][3]
ccl_gpu_kernel_postfix ccl_global float int int int int float threshold
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
VecMat::Vec3< double > Vec3d
Definition: Geom.h:27
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRefNull prop_name, int32_t value)
Allocate a new IDProperty of type IDP_INT, set its name and value.
std::unique_ptr< T > Ptr
Definition: BLI_any.hh:58
vec_base< float, 3 > float3
float max