Blender  V3.3
volume_render.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include "MEM_guardedalloc.h"
8 
9 #include "BLI_array.hh"
10 #include "BLI_math_matrix.h"
11 #include "BLI_math_vec_types.hh"
12 #include "BLI_math_vector.h"
13 #include "BLI_vector.hh"
14 
15 #include "DNA_volume_types.h"
16 
17 #include "BKE_volume.h"
18 #include "BKE_volume_render.h"
19 
20 #ifdef WITH_OPENVDB
21 # include <openvdb/openvdb.h>
22 # include <openvdb/tools/Dense.h>
23 #endif
24 
25 /* Dense Voxels */
26 
27 #ifdef WITH_OPENVDB
28 
29 template<typename GridType, typename VoxelType>
30 static void extract_dense_voxels(const openvdb::GridBase &grid,
31  const openvdb::CoordBBox bbox,
32  VoxelType *r_voxels)
33 {
34  BLI_assert(grid.isType<GridType>());
35  openvdb::tools::Dense<VoxelType, openvdb::tools::LayoutXYZ> dense(bbox, r_voxels);
36  openvdb::tools::copyToDense(static_cast<const GridType &>(grid), dense);
37 }
38 
39 static void extract_dense_float_voxels(const VolumeGridType grid_type,
40  const openvdb::GridBase &grid,
41  const openvdb::CoordBBox &bbox,
42  float *r_voxels)
43 {
44  switch (grid_type) {
46  return extract_dense_voxels<openvdb::BoolGrid, float>(grid, bbox, r_voxels);
47  case VOLUME_GRID_FLOAT:
48  return extract_dense_voxels<openvdb::FloatGrid, float>(grid, bbox, r_voxels);
49  case VOLUME_GRID_DOUBLE:
50  return extract_dense_voxels<openvdb::DoubleGrid, float>(grid, bbox, r_voxels);
51  case VOLUME_GRID_INT:
52  return extract_dense_voxels<openvdb::Int32Grid, float>(grid, bbox, r_voxels);
53  case VOLUME_GRID_INT64:
54  return extract_dense_voxels<openvdb::Int64Grid, float>(grid, bbox, r_voxels);
55  case VOLUME_GRID_MASK:
56  return extract_dense_voxels<openvdb::MaskGrid, float>(grid, bbox, r_voxels);
58  return extract_dense_voxels<openvdb::Vec3fGrid, openvdb::Vec3f>(
59  grid, bbox, reinterpret_cast<openvdb::Vec3f *>(r_voxels));
61  return extract_dense_voxels<openvdb::Vec3dGrid, openvdb::Vec3f>(
62  grid, bbox, reinterpret_cast<openvdb::Vec3f *>(r_voxels));
64  return extract_dense_voxels<openvdb::Vec3IGrid, openvdb::Vec3f>(
65  grid, bbox, reinterpret_cast<openvdb::Vec3f *>(r_voxels));
66  case VOLUME_GRID_POINTS:
68  /* Zero channels to copy. */
69  break;
70  }
71 }
72 
73 static void create_texture_to_object_matrix(const openvdb::Mat4d &grid_transform,
74  const openvdb::CoordBBox &bbox,
75  float r_texture_to_object[4][4])
76 {
77  float index_to_object[4][4];
78  memcpy(index_to_object, openvdb::Mat4s(grid_transform).asPointer(), sizeof(index_to_object));
79 
80  float texture_to_index[4][4];
81  const openvdb::Vec3f loc = bbox.min().asVec3s();
82  const openvdb::Vec3f size = bbox.dim().asVec3s();
83  size_to_mat4(texture_to_index, size.asV());
84  copy_v3_v3(texture_to_index[3], loc.asV());
85 
86  mul_m4_m4m4(r_texture_to_object, index_to_object, texture_to_index);
87 }
88 
89 #endif
90 
92  const VolumeGrid *volume_grid,
93  DenseFloatVolumeGrid *r_dense_grid)
94 {
95 #ifdef WITH_OPENVDB
96  const VolumeGridType grid_type = BKE_volume_grid_type(volume_grid);
97  openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
98 
99  const openvdb::CoordBBox bbox = grid->evalActiveVoxelBoundingBox();
100  if (bbox.empty()) {
101  return false;
102  }
103 
104  const openvdb::Vec3i resolution = bbox.dim().asVec3i();
105  const int64_t num_voxels = static_cast<int64_t>(resolution[0]) *
106  static_cast<int64_t>(resolution[1]) *
107  static_cast<int64_t>(resolution[2]);
108  const int channels = BKE_volume_grid_channels(volume_grid);
109  const int elem_size = sizeof(float) * channels;
110  float *voxels = static_cast<float *>(MEM_malloc_arrayN(num_voxels, elem_size, __func__));
111  if (voxels == nullptr) {
112  return false;
113  }
114 
115  extract_dense_float_voxels(grid_type, *grid, bbox, voxels);
116  create_texture_to_object_matrix(grid->transform().baseMap()->getAffineMap()->getMat4(),
117  bbox,
118  r_dense_grid->texture_to_object);
119 
120  r_dense_grid->voxels = voxels;
121  r_dense_grid->channels = channels;
122  copy_v3_v3_int(r_dense_grid->resolution, resolution.asV());
123  return true;
124 #endif
125  UNUSED_VARS(volume, volume_grid, r_dense_grid);
126  return false;
127 }
128 
130 {
131  if (dense_grid->voxels != nullptr) {
132  MEM_freeN(dense_grid->voxels);
133  }
134 }
135 
136 /* Wireframe */
137 
138 #ifdef WITH_OPENVDB
139 
141 template<typename GridType>
142 static blender::Vector<openvdb::CoordBBox> get_bounding_boxes(const GridType &grid,
143  const bool coarse)
144 {
145  using TreeType = typename GridType::TreeType;
146  using Depth2Type = typename TreeType::RootNodeType::ChildNodeType::ChildNodeType;
147  using NodeCIter = typename TreeType::NodeCIter;
148 
150  const int depth = coarse ? 2 : 3;
151 
152  NodeCIter iter = grid.tree().cbeginNode();
153  iter.setMaxDepth(depth);
154 
155  for (; iter; ++iter) {
156  if (iter.getDepth() != depth) {
157  continue;
158  }
159 
160  openvdb::CoordBBox box;
161  if (depth == 2) {
162  /* Internal node at depth 2. */
163  const Depth2Type *node = nullptr;
164  iter.getNode(node);
165  if (node) {
166  node->evalActiveBoundingBox(box, false);
167  }
168  else {
169  continue;
170  }
171  }
172  else {
173  /* Leaf node. */
174  if (!iter.getBoundingBox(box)) {
175  continue;
176  }
177  }
178 
179  /* +1 to convert from exclusive to inclusive bounds. */
180  box.max() = box.max().offsetBy(1);
181 
182  boxes.append(box);
183  }
184 
185  return boxes;
186 }
187 
188 struct GetBoundingBoxesOp {
189  const openvdb::GridBase &grid;
190  const bool coarse;
191 
192  template<typename GridType> blender::Vector<openvdb::CoordBBox> operator()()
193  {
194  return get_bounding_boxes(static_cast<const GridType &>(grid), coarse);
195  }
196 };
197 
198 static blender::Vector<openvdb::CoordBBox> get_bounding_boxes(VolumeGridType grid_type,
199  const openvdb::GridBase &grid,
200  const bool coarse)
201 {
202  GetBoundingBoxesOp op{grid, coarse};
203  return BKE_volume_grid_type_operation(grid_type, op);
204 }
205 
206 static void boxes_to_center_points(blender::Span<openvdb::CoordBBox> boxes,
209 {
210  BLI_assert(boxes.size() == r_verts.size());
211  for (const int i : boxes.index_range()) {
212  openvdb::Vec3d center = transform.indexToWorld(boxes[i].getCenter());
213  r_verts[i] = blender::float3(center[0], center[1], center[2]);
214  }
215 }
216 
217 static void boxes_to_corner_points(blender::Span<openvdb::CoordBBox> boxes,
220 {
221  BLI_assert(boxes.size() * 8 == r_verts.size());
222  for (const int i : boxes.index_range()) {
223  const openvdb::CoordBBox &box = boxes[i];
224 
225  /* The ordering of the corner points is lexicographic. */
226  std::array<openvdb::Coord, 8> corners;
227  box.getCornerPoints(corners.data());
228 
229  for (int j = 0; j < 8; j++) {
230  openvdb::Coord corner_i = corners[j];
231  openvdb::Vec3d corner_d = transform.indexToWorld(corner_i);
232  r_verts[8 * i + j] = blender::float3(corner_d[0], corner_d[1], corner_d[2]);
233  }
234  }
235 }
236 
237 static void boxes_to_edge_mesh(blender::Span<openvdb::CoordBBox> boxes,
240  blender::Vector<std::array<int, 2>> &r_edges)
241 {
242  /* TODO: Deduplicate edges, hide flat edges? */
243 
244  const int box_edges[12][2] = {
245  {0, 1},
246  {0, 2},
247  {0, 4},
248  {1, 3},
249  {1, 5},
250  {2, 3},
251  {2, 6},
252  {3, 7},
253  {4, 5},
254  {4, 6},
255  {5, 7},
256  {6, 7},
257  };
258 
259  int vert_offset = r_verts.size();
260  int edge_offset = r_edges.size();
261 
262  const int vert_amount = 8 * boxes.size();
263  const int edge_amount = 12 * boxes.size();
264 
265  r_verts.resize(r_verts.size() + vert_amount);
266  r_edges.resize(r_edges.size() + edge_amount);
267  boxes_to_corner_points(boxes, transform, r_verts.as_mutable_span().take_back(vert_amount));
268 
269  for (int i = 0; i < boxes.size(); i++) {
270  for (int j = 0; j < 12; j++) {
271  r_edges[edge_offset + j] = {vert_offset + box_edges[j][0], vert_offset + box_edges[j][1]};
272  }
273  vert_offset += 8;
274  edge_offset += 12;
275  }
276 }
277 
278 static void boxes_to_cube_mesh(blender::Span<openvdb::CoordBBox> boxes,
281  blender::Vector<std::array<int, 3>> &r_tris)
282 {
283  const int box_tris[12][3] = {
284  {0, 1, 4},
285  {4, 1, 5},
286  {0, 2, 1},
287  {1, 2, 3},
288  {1, 3, 5},
289  {5, 3, 7},
290  {6, 4, 5},
291  {7, 5, 6},
292  {2, 0, 4},
293  {2, 4, 6},
294  {3, 7, 2},
295  {6, 2, 7},
296  };
297 
298  int vert_offset = r_verts.size();
299  int tri_offset = r_tris.size();
300 
301  const int vert_amount = 8 * boxes.size();
302  const int tri_amount = 12 * boxes.size();
303 
304  r_verts.resize(r_verts.size() + vert_amount);
305  r_tris.resize(r_tris.size() + tri_amount);
306  boxes_to_corner_points(boxes, transform, r_verts.as_mutable_span().take_back(vert_amount));
307 
308  for (int i = 0; i < boxes.size(); i++) {
309  for (int j = 0; j < 12; j++) {
310  r_tris[tri_offset + j] = {vert_offset + box_tris[j][0],
311  vert_offset + box_tris[j][1],
312  vert_offset + box_tris[j][2]};
313  }
314  vert_offset += 8;
315  tri_offset += 12;
316  }
317 }
318 
319 #endif
320 
322  const VolumeGrid *volume_grid,
324  void *cb_userdata)
325 {
327  cb(cb_userdata, nullptr, nullptr, 0, 0);
328  return;
329  }
330 
331 #ifdef WITH_OPENVDB
332  openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
333 
335  /* Bounding box. */
336  openvdb::CoordBBox box;
339  if (grid->baseTree().evalLeafBoundingBox(box)) {
340  boxes_to_edge_mesh({box}, grid->transform(), verts, edges);
341  }
342  cb(cb_userdata,
343  (float(*)[3])verts.data(),
344  (int(*)[2])edges.data(),
345  verts.size(),
346  edges.size());
347  }
348  else {
349  blender::Vector<openvdb::CoordBBox> boxes = get_bounding_boxes(
350  BKE_volume_grid_type(volume_grid),
351  *grid,
353 
356 
358  verts.resize(boxes.size());
359  boxes_to_center_points(boxes, grid->transform(), verts);
360  }
361  else {
362  boxes_to_edge_mesh(boxes, grid->transform(), verts, edges);
363  }
364 
365  cb(cb_userdata,
366  (float(*)[3])verts.data(),
367  (int(*)[2])edges.data(),
368  verts.size(),
369  edges.size());
370  }
371 
372 #else
373  UNUSED_VARS(volume, volume_grid);
374  cb(cb_userdata, nullptr, nullptr, 0, 0);
375 #endif
376 }
377 
378 #ifdef WITH_OPENVDB
379 static void grow_triangles(blender::MutableSpan<blender::float3> verts,
380  blender::Span<std::array<int, 3>> tris,
381  const float factor)
382 {
383  /* Compute the offset for every vertex based on the connected edges.
384  * This formula simply tries increases the length of all edges. */
385  blender::Array<blender::float3> offsets(verts.size(), {0, 0, 0});
386  for (const std::array<int, 3> &tri : tris) {
387  offsets[tri[0]] += factor * (2 * verts[tri[0]] - verts[tri[1]] - verts[tri[2]]);
388  offsets[tri[1]] += factor * (2 * verts[tri[1]] - verts[tri[0]] - verts[tri[2]]);
389  offsets[tri[2]] += factor * (2 * verts[tri[2]] - verts[tri[0]] - verts[tri[1]]);
390  }
391  /* Apply the computed offsets. */
392  for (const int i : verts.index_range()) {
393  verts[i] += offsets[i];
394  }
395 }
396 #endif /* WITH_OPENVDB */
397 
399  const VolumeGrid *volume_grid,
401  void *cb_userdata)
402 {
403 #ifdef WITH_OPENVDB
404  openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
405  blender::Vector<openvdb::CoordBBox> boxes = get_bounding_boxes(
406  BKE_volume_grid_type(volume_grid), *grid, true);
407 
410  boxes_to_cube_mesh(boxes, grid->transform(), verts, tris);
411 
412  /* By slightly scaling the individual boxes up, we can avoid some artifacts when drawing the
413  * selection outline. */
414  const float offset_factor = 0.01f;
415  grow_triangles(verts, tris, offset_factor);
416 
417  cb(cb_userdata, (float(*)[3])verts.data(), (int(*)[3])tris.data(), verts.size(), tris.size());
418 #else
419  UNUSED_VARS(volume, volume_grid);
420  cb(cb_userdata, nullptr, nullptr, 0, 0);
421 #endif
422 }
423 
424 /* Render */
425 
426 float BKE_volume_density_scale(const Volume *volume, const float matrix[4][4])
427 {
428  if (volume->render.space == VOLUME_SPACE_OBJECT) {
429  float unit[3] = {1.0f, 1.0f, 1.0f};
430  normalize_v3(unit);
431  mul_mat3_m4_v3(matrix, unit);
432  return 1.0f / len_v3(unit);
433  }
434 
435  return 1.0f;
436 }
typedef float(TangentPoint)[2]
Volume data-block.
VolumeGridType BKE_volume_grid_type(const struct VolumeGrid *grid)
VolumeGridType
Definition: BKE_volume.h:90
@ VOLUME_GRID_VECTOR_FLOAT
Definition: BKE_volume.h:98
@ VOLUME_GRID_MASK
Definition: BKE_volume.h:97
@ VOLUME_GRID_VECTOR_DOUBLE
Definition: BKE_volume.h:99
@ VOLUME_GRID_VECTOR_INT
Definition: BKE_volume.h:100
@ VOLUME_GRID_UNKNOWN
Definition: BKE_volume.h:91
@ VOLUME_GRID_DOUBLE
Definition: BKE_volume.h:94
@ VOLUME_GRID_BOOLEAN
Definition: BKE_volume.h:92
@ VOLUME_GRID_INT
Definition: BKE_volume.h:95
@ VOLUME_GRID_INT64
Definition: BKE_volume.h:96
@ VOLUME_GRID_POINTS
Definition: BKE_volume.h:101
@ VOLUME_GRID_FLOAT
Definition: BKE_volume.h:93
int BKE_volume_grid_channels(const struct VolumeGrid *grid)
Volume data-block rendering and viewport drawing utilities.
void(* BKE_volume_wireframe_cb)(void *userdata, const float(*verts)[3], const int(*edges)[2], int totvert, int totedge)
void(* BKE_volume_selection_surface_cb)(void *userdata, float(*verts)[3], int(*tris)[3], int totvert, int tottris)
#define BLI_assert(a)
Definition: BLI_assert.h:46
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
Definition: math_matrix.c:259
void mul_mat3_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:790
void size_to_mat4(float R[4][4], const float size[3])
Definition: math_matrix.c:2111
MINLINE float normalize_v3(float r[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void copy_v3_v3_int(int r[3], const int a[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
#define UNUSED_VARS(...)
@ VOLUME_SPACE_OBJECT
@ VOLUME_WIREFRAME_NONE
@ VOLUME_WIREFRAME_POINTS
@ VOLUME_WIREFRAME_BOUNDS
@ VOLUME_WIREFRAME_COARSE
NSNotificationCenter * center
Read Guarded memory(de)allocation.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert a producing a negative Combine Generate a color from its and blue channels(Deprecated)") DefNode(ShaderNode
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
SIMD_FORCE_INLINE btVector3 operator()(const btVector3 &x) const
Return the transform of the vector.
Definition: btTransform.h:90
constexpr int64_t size() const
Definition: BLI_span.hh:511
constexpr int64_t size() const
Definition: BLI_span.hh:240
constexpr IndexRange index_range() const
Definition: BLI_span.hh:401
int64_t size() const
Definition: BLI_vector.hh:694
MutableSpan< T > as_mutable_span()
Definition: BLI_vector.hh:330
void append(const T &value)
Definition: BLI_vector.hh:433
void resize(const int64_t new_size)
Definition: BLI_vector.hh:353
OperationNode * node
struct Vec3f Vec3f
static float verts[][3]
CCL_NAMESPACE_BEGIN struct Transform Transform
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:34
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
VecMat::Vec3< double > Vec3d
Definition: Geom.h:27
VecMat::Vec3< int > Vec3i
Definition: Geom.h:25
vec_base< float, 3 > float3
__int64 int64_t
Definition: stdint.h:89
float texture_to_object[4][4]
VolumeRender render
VolumeDisplay display
bool BKE_volume_grid_dense_floats(const Volume *volume, const VolumeGrid *volume_grid, DenseFloatVolumeGrid *r_dense_grid)
void BKE_volume_dense_float_grid_clear(DenseFloatVolumeGrid *dense_grid)
void BKE_volume_grid_wireframe(const Volume *volume, const VolumeGrid *volume_grid, BKE_volume_wireframe_cb cb, void *cb_userdata)
float BKE_volume_density_scale(const Volume *volume, const float matrix[4][4])
void BKE_volume_grid_selection_surface(const Volume *volume, const VolumeGrid *volume_grid, BKE_volume_selection_surface_cb cb, void *cb_userdata)