Blender  V2.93
volume_render.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
21 #include "MEM_guardedalloc.h"
22 
23 #include "BLI_array.hh"
24 #include "BLI_float3.hh"
25 #include "BLI_math_matrix.h"
26 #include "BLI_math_vector.h"
27 #include "BLI_vector.hh"
28 
29 #include "DNA_volume_types.h"
30 
31 #include "BKE_volume.h"
32 #include "BKE_volume_render.h"
33 
34 #ifdef WITH_OPENVDB
35 # include <openvdb/openvdb.h>
36 # include <openvdb/tools/Dense.h>
37 #endif
38 
39 /* Dense Voxels */
40 
41 #ifdef WITH_OPENVDB
42 
43 template<typename GridType, typename VoxelType>
44 static void extract_dense_voxels(const openvdb::GridBase &grid,
45  const openvdb::CoordBBox bbox,
46  VoxelType *r_voxels)
47 {
48  BLI_assert(grid.isType<GridType>());
49  openvdb::tools::Dense<VoxelType, openvdb::tools::LayoutXYZ> dense(bbox, r_voxels);
50  openvdb::tools::copyToDense(static_cast<const GridType &>(grid), dense);
51 }
52 
53 static void extract_dense_float_voxels(const VolumeGridType grid_type,
54  const openvdb::GridBase &grid,
55  const openvdb::CoordBBox &bbox,
56  float *r_voxels)
57 {
58  switch (grid_type) {
60  return extract_dense_voxels<openvdb::BoolGrid, float>(grid, bbox, r_voxels);
61  case VOLUME_GRID_FLOAT:
62  return extract_dense_voxels<openvdb::FloatGrid, float>(grid, bbox, r_voxels);
63  case VOLUME_GRID_DOUBLE:
64  return extract_dense_voxels<openvdb::DoubleGrid, float>(grid, bbox, r_voxels);
65  case VOLUME_GRID_INT:
66  return extract_dense_voxels<openvdb::Int32Grid, float>(grid, bbox, r_voxels);
67  case VOLUME_GRID_INT64:
68  return extract_dense_voxels<openvdb::Int64Grid, float>(grid, bbox, r_voxels);
69  case VOLUME_GRID_MASK:
70  return extract_dense_voxels<openvdb::MaskGrid, float>(grid, bbox, r_voxels);
72  return extract_dense_voxels<openvdb::Vec3fGrid, openvdb::Vec3f>(
73  grid, bbox, reinterpret_cast<openvdb::Vec3f *>(r_voxels));
75  return extract_dense_voxels<openvdb::Vec3dGrid, openvdb::Vec3f>(
76  grid, bbox, reinterpret_cast<openvdb::Vec3f *>(r_voxels));
78  return extract_dense_voxels<openvdb::Vec3IGrid, openvdb::Vec3f>(
79  grid, bbox, reinterpret_cast<openvdb::Vec3f *>(r_voxels));
80  case VOLUME_GRID_STRING:
81  case VOLUME_GRID_POINTS:
83  /* Zero channels to copy. */
84  break;
85  }
86 }
87 
88 static void create_texture_to_object_matrix(const openvdb::Mat4d &grid_transform,
89  const openvdb::CoordBBox &bbox,
90  float r_texture_to_object[4][4])
91 {
92  float index_to_object[4][4];
93  memcpy(index_to_object, openvdb::Mat4s(grid_transform).asPointer(), sizeof(index_to_object));
94 
95  float texture_to_index[4][4];
96  const openvdb::Vec3f loc = bbox.min().asVec3s();
97  const openvdb::Vec3f size = bbox.dim().asVec3s();
98  size_to_mat4(texture_to_index, size.asV());
99  copy_v3_v3(texture_to_index[3], loc.asV());
100 
101  mul_m4_m4m4(r_texture_to_object, index_to_object, texture_to_index);
102 }
103 
104 #endif
105 
107  const VolumeGrid *volume_grid,
108  DenseFloatVolumeGrid *r_dense_grid)
109 {
110 #ifdef WITH_OPENVDB
111  const VolumeGridType grid_type = BKE_volume_grid_type(volume_grid);
112  openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
113 
114  const openvdb::CoordBBox bbox = grid->evalActiveVoxelBoundingBox();
115  if (bbox.empty()) {
116  return false;
117  }
118 
119  const openvdb::Vec3i resolution = bbox.dim().asVec3i();
120  const int64_t num_voxels = static_cast<int64_t>(resolution[0]) *
121  static_cast<int64_t>(resolution[1]) *
122  static_cast<int64_t>(resolution[2]);
123  const int channels = BKE_volume_grid_channels(volume_grid);
124  const int elem_size = sizeof(float) * channels;
125  float *voxels = static_cast<float *>(MEM_malloc_arrayN(num_voxels, elem_size, __func__));
126  if (voxels == nullptr) {
127  return false;
128  }
129 
130  extract_dense_float_voxels(grid_type, *grid, bbox, voxels);
131  create_texture_to_object_matrix(grid->transform().baseMap()->getAffineMap()->getMat4(),
132  bbox,
133  r_dense_grid->texture_to_object);
134 
135  r_dense_grid->voxels = voxels;
136  r_dense_grid->channels = channels;
137  copy_v3_v3_int(r_dense_grid->resolution, resolution.asV());
138  return true;
139 #endif
140  UNUSED_VARS(volume, volume_grid, r_dense_grid);
141  return false;
142 }
143 
145 {
146  if (dense_grid->voxels != nullptr) {
147  MEM_freeN(dense_grid->voxels);
148  }
149 }
150 
151 /* Wireframe */
152 
153 #ifdef WITH_OPENVDB
154 
156 template<typename GridType>
157 static blender::Vector<openvdb::CoordBBox> get_bounding_boxes(const GridType &grid,
158  const bool coarse)
159 {
160  using TreeType = typename GridType::TreeType;
161  using Depth2Type = typename TreeType::RootNodeType::ChildNodeType::ChildNodeType;
162  using NodeCIter = typename TreeType::NodeCIter;
163 
165  const int depth = coarse ? 2 : 3;
166 
167  NodeCIter iter = grid.tree().cbeginNode();
168  iter.setMaxDepth(depth);
169 
170  for (; iter; ++iter) {
171  if (iter.getDepth() != depth) {
172  continue;
173  }
174 
175  openvdb::CoordBBox box;
176  if (depth == 2) {
177  /* Internal node at depth 2. */
178  const Depth2Type *node = nullptr;
179  iter.getNode(node);
180  if (node) {
181  node->evalActiveBoundingBox(box, false);
182  }
183  else {
184  continue;
185  }
186  }
187  else {
188  /* Leaf node. */
189  if (!iter.getBoundingBox(box)) {
190  continue;
191  }
192  }
193 
194  /* +1 to convert from exclusive to inclusive bounds. */
195  box.max() = box.max().offsetBy(1);
196 
197  boxes.append(box);
198  }
199 
200  return boxes;
201 }
202 
203 struct GetBoundingBoxesOp {
204  const openvdb::GridBase &grid;
205  const bool coarse;
206 
207  template<typename GridType> blender::Vector<openvdb::CoordBBox> operator()()
208  {
209  return get_bounding_boxes(static_cast<const GridType &>(grid), coarse);
210  }
211 };
212 
213 static blender::Vector<openvdb::CoordBBox> get_bounding_boxes(VolumeGridType grid_type,
214  const openvdb::GridBase &grid,
215  const bool coarse)
216 {
217  GetBoundingBoxesOp op{grid, coarse};
218  return BKE_volume_grid_type_operation(grid_type, op);
219 }
220 
221 static void boxes_to_center_points(blender::Span<openvdb::CoordBBox> boxes,
224 {
225  BLI_assert(boxes.size() == r_verts.size());
226  for (const int i : boxes.index_range()) {
227  openvdb::Vec3d center = transform.indexToWorld(boxes[i].getCenter());
228  r_verts[i] = blender::float3(center[0], center[1], center[2]);
229  }
230 }
231 
232 static void boxes_to_corner_points(blender::Span<openvdb::CoordBBox> boxes,
235 {
236  BLI_assert(boxes.size() * 8 == r_verts.size());
237  for (const int i : boxes.index_range()) {
238  const openvdb::CoordBBox &box = boxes[i];
239 
240  /* The ordering of the corner points is lexicographic. */
241  std::array<openvdb::Coord, 8> corners;
242  box.getCornerPoints(corners.data());
243 
244  for (int j = 0; j < 8; j++) {
245  openvdb::Coord corner_i = corners[j];
246  openvdb::Vec3d corner_d = transform.indexToWorld(corner_i);
247  r_verts[8 * i + j] = blender::float3(corner_d[0], corner_d[1], corner_d[2]);
248  }
249  }
250 }
251 
252 static void boxes_to_edge_mesh(blender::Span<openvdb::CoordBBox> boxes,
255  blender::Vector<std::array<int, 2>> &r_edges)
256 {
257  /* TODO: Deduplicate edges, hide flat edges? */
258 
259  const int box_edges[12][2] = {
260  {0, 1},
261  {0, 2},
262  {0, 4},
263  {1, 3},
264  {1, 5},
265  {2, 3},
266  {2, 6},
267  {3, 7},
268  {4, 5},
269  {4, 6},
270  {5, 7},
271  {6, 7},
272  };
273 
274  int vert_offset = r_verts.size();
275  int edge_offset = r_edges.size();
276 
277  const int vert_amount = 8 * boxes.size();
278  const int edge_amount = 12 * boxes.size();
279 
280  r_verts.resize(r_verts.size() + vert_amount);
281  r_edges.resize(r_edges.size() + edge_amount);
282  boxes_to_corner_points(boxes, transform, r_verts.as_mutable_span().take_back(vert_amount));
283 
284  for (int i = 0; i < boxes.size(); i++) {
285  for (int j = 0; j < 12; j++) {
286  r_edges[edge_offset + j] = {vert_offset + box_edges[j][0], vert_offset + box_edges[j][1]};
287  }
288  vert_offset += 8;
289  edge_offset += 12;
290  }
291 }
292 
293 static void boxes_to_cube_mesh(blender::Span<openvdb::CoordBBox> boxes,
296  blender::Vector<std::array<int, 3>> &r_tris)
297 {
298  const int box_tris[12][3] = {
299  {0, 1, 4},
300  {4, 1, 5},
301  {0, 2, 1},
302  {1, 2, 3},
303  {1, 3, 5},
304  {5, 3, 7},
305  {6, 4, 5},
306  {7, 5, 6},
307  {2, 0, 4},
308  {2, 4, 6},
309  {3, 7, 2},
310  {6, 2, 7},
311  };
312 
313  int vert_offset = r_verts.size();
314  int tri_offset = r_tris.size();
315 
316  const int vert_amount = 8 * boxes.size();
317  const int tri_amount = 12 * boxes.size();
318 
319  r_verts.resize(r_verts.size() + vert_amount);
320  r_tris.resize(r_tris.size() + tri_amount);
321  boxes_to_corner_points(boxes, transform, r_verts.as_mutable_span().take_back(vert_amount));
322 
323  for (int i = 0; i < boxes.size(); i++) {
324  for (int j = 0; j < 12; j++) {
325  r_tris[tri_offset + j] = {vert_offset + box_tris[j][0],
326  vert_offset + box_tris[j][1],
327  vert_offset + box_tris[j][2]};
328  }
329  vert_offset += 8;
330  tri_offset += 12;
331  }
332 }
333 
334 #endif
335 
337  const VolumeGrid *volume_grid,
339  void *cb_userdata)
340 {
342  cb(cb_userdata, nullptr, nullptr, 0, 0);
343  return;
344  }
345 
346 #ifdef WITH_OPENVDB
347  openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
348 
350  /* Bounding box. */
351  openvdb::CoordBBox box;
354  if (grid->baseTree().evalLeafBoundingBox(box)) {
355  boxes_to_edge_mesh({box}, grid->transform(), verts, edges);
356  }
357  cb(cb_userdata,
358  (float(*)[3])verts.data(),
359  (int(*)[2])edges.data(),
360  verts.size(),
361  edges.size());
362  }
363  else {
364  blender::Vector<openvdb::CoordBBox> boxes = get_bounding_boxes(
365  BKE_volume_grid_type(volume_grid),
366  *grid,
368 
371 
373  verts.resize(boxes.size());
374  boxes_to_center_points(boxes, grid->transform(), verts);
375  }
376  else {
377  boxes_to_edge_mesh(boxes, grid->transform(), verts, edges);
378  }
379 
380  cb(cb_userdata,
381  (float(*)[3])verts.data(),
382  (int(*)[2])edges.data(),
383  verts.size(),
384  edges.size());
385  }
386 
387 #else
388  UNUSED_VARS(volume, volume_grid);
389  cb(cb_userdata, nullptr, nullptr, 0, 0);
390 #endif
391 }
392 
393 #ifdef WITH_OPENVDB
394 static void grow_triangles(blender::MutableSpan<blender::float3> verts,
395  blender::Span<std::array<int, 3>> tris,
396  const float factor)
397 {
398  /* Compute the offset for every vertex based on the connected edges.
399  * This formula simply tries increases the length of all edges. */
400  blender::Array<blender::float3> offsets(verts.size(), {0, 0, 0});
401  for (const std::array<int, 3> &tri : tris) {
402  offsets[tri[0]] += factor * (2 * verts[tri[0]] - verts[tri[1]] - verts[tri[2]]);
403  offsets[tri[1]] += factor * (2 * verts[tri[1]] - verts[tri[0]] - verts[tri[2]]);
404  offsets[tri[2]] += factor * (2 * verts[tri[2]] - verts[tri[0]] - verts[tri[1]]);
405  }
406  /* Apply the computed offsets. */
407  for (const int i : verts.index_range()) {
408  verts[i] += offsets[i];
409  }
410 }
411 #endif /* WITH_OPENVDB */
412 
414  const VolumeGrid *volume_grid,
416  void *cb_userdata)
417 {
418 #ifdef WITH_OPENVDB
419  openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
420  blender::Vector<openvdb::CoordBBox> boxes = get_bounding_boxes(
421  BKE_volume_grid_type(volume_grid), *grid, true);
422 
425  boxes_to_cube_mesh(boxes, grid->transform(), verts, tris);
426 
427  /* By slightly scaling the individual boxes up, we can avoid some artifacts when drawing the
428  * selection outline. */
429  const float offset_factor = 0.01f;
430  grow_triangles(verts, tris, offset_factor);
431 
432  cb(cb_userdata, (float(*)[3])verts.data(), (int(*)[3])tris.data(), verts.size(), tris.size());
433 #else
434  UNUSED_VARS(volume, volume_grid);
435  cb(cb_userdata, nullptr, nullptr, 0, 0);
436 #endif
437 }
438 
439 /* Render */
440 
441 float BKE_volume_density_scale(const Volume *volume, const float matrix[4][4])
442 {
443  if (volume->render.space == VOLUME_SPACE_OBJECT) {
444  float unit[3] = {1.0f, 1.0f, 1.0f};
445  normalize_v3(unit);
446  mul_mat3_m4_v3(matrix, unit);
447  return 1.0f / len_v3(unit);
448  }
449 
450  return 1.0f;
451 }
typedef float(TangentPoint)[2]
Volume datablock.
VolumeGridType BKE_volume_grid_type(const struct VolumeGrid *grid)
VolumeGridType
Definition: BKE_volume.h:98
@ VOLUME_GRID_VECTOR_FLOAT
Definition: BKE_volume.h:107
@ VOLUME_GRID_MASK
Definition: BKE_volume.h:105
@ VOLUME_GRID_VECTOR_DOUBLE
Definition: BKE_volume.h:108
@ VOLUME_GRID_VECTOR_INT
Definition: BKE_volume.h:109
@ VOLUME_GRID_UNKNOWN
Definition: BKE_volume.h:99
@ VOLUME_GRID_DOUBLE
Definition: BKE_volume.h:102
@ VOLUME_GRID_BOOLEAN
Definition: BKE_volume.h:100
@ VOLUME_GRID_INT
Definition: BKE_volume.h:103
@ VOLUME_GRID_INT64
Definition: BKE_volume.h:104
@ VOLUME_GRID_POINTS
Definition: BKE_volume.h:110
@ VOLUME_GRID_FLOAT
Definition: BKE_volume.h:101
@ VOLUME_GRID_STRING
Definition: BKE_volume.h:106
int BKE_volume_grid_channels(const struct VolumeGrid *grid)
Volume data-block rendering and viewport drawing utilities.
void(* BKE_volume_wireframe_cb)(void *userdata, float(*verts)[3], 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:58
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
Definition: math_matrix.c:262
void mul_mat3_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:794
void size_to_mat4(float R[4][4], const float size[3])
Definition: math_matrix.c:2118
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.
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:524
constexpr int64_t size() const
Definition: BLI_span.hh:254
constexpr IndexRange index_range() const
Definition: BLI_span.hh:414
int64_t size() const
Definition: BLI_vector.hh:662
MutableSpan< T > as_mutable_span()
Definition: BLI_vector.hh:345
void append(const T &value)
Definition: BLI_vector.hh:438
void resize(const int64_t new_size)
Definition: BLI_vector.hh:368
OperationNode * node
struct Vec3f Vec3f
static float verts[][3]
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:48
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
VecMat::Vec3< double > Vec3d
Definition: Geom.h:41
VecMat::Vec3< int > Vec3i
Definition: Geom.h:39
__int64 int64_t
Definition: stdint.h:92
float texture_to_object[4][4]
VolumeRender render
VolumeDisplay display
CCL_NAMESPACE_BEGIN struct Transform Transform
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)