Blender  V3.3
scene/volume.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright 2020-2022 Blender Foundation */
3 
4 #include "scene/volume.h"
5 #include "scene/attribute.h"
6 #include "scene/image_vdb.h"
7 #include "scene/scene.h"
8 
9 #ifdef WITH_OPENVDB
10 # include <openvdb/tools/Dense.h>
11 # include <openvdb/tools/GridTransformer.h>
12 # include <openvdb/tools/Morphology.h>
13 # include <openvdb/tools/Statistics.h>
14 #endif
15 
16 #include "util/hash.h"
17 #include "util/log.h"
18 #include "util/openvdb.h"
19 #include "util/progress.h"
20 #include "util/types.h"
21 
23 
25 {
26  NodeType *type = NodeType::add("volume", create, NodeType::NONE, Mesh::get_node_type());
27 
28  SOCKET_FLOAT(clipping, "Clipping", 0.001f);
29  SOCKET_FLOAT(step_size, "Step Size", 0.0f);
30  SOCKET_BOOLEAN(object_space, "Object Space", false);
31  SOCKET_FLOAT(velocity_scale, "Velocity Scale", 1.0f);
32 
33  return type;
34 }
35 
36 Volume::Volume() : Mesh(get_node_type(), Geometry::VOLUME)
37 {
38  clipping = 0.001f;
39  step_size = 0.0f;
40  object_space = false;
41 }
42 
43 void Volume::clear(bool preserve_shaders)
44 {
45  Mesh::clear(preserve_shaders, true);
46 }
47 
48 struct QuadData {
49  int v0, v1, v2, v3;
50 
52 };
53 
54 enum {
61 };
62 
63 #ifdef WITH_OPENVDB
64 const int quads_indices[6][4] = {
65  /* QUAD_X_MIN */
66  {4, 0, 3, 7},
67  /* QUAD_X_MAX */
68  {1, 5, 6, 2},
69  /* QUAD_Y_MIN */
70  {4, 5, 1, 0},
71  /* QUAD_Y_MAX */
72  {3, 2, 6, 7},
73  /* QUAD_Z_MIN */
74  {0, 1, 2, 3},
75  /* QUAD_Z_MAX */
76  {5, 4, 7, 6},
77 };
78 
79 const float3 quads_normals[6] = {
80  /* QUAD_X_MIN */
81  make_float3(-1.0f, 0.0f, 0.0f),
82  /* QUAD_X_MAX */
83  make_float3(1.0f, 0.0f, 0.0f),
84  /* QUAD_Y_MIN */
85  make_float3(0.0f, -1.0f, 0.0f),
86  /* QUAD_Y_MAX */
87  make_float3(0.0f, 1.0f, 0.0f),
88  /* QUAD_Z_MIN */
89  make_float3(0.0f, 0.0f, -1.0f),
90  /* QUAD_Z_MAX */
91  make_float3(0.0f, 0.0f, 1.0f),
92 };
93 
94 static int add_vertex(int3 v,
95  vector<int3> &vertices,
96  int3 res,
97  unordered_map<size_t, int> &used_verts)
98 {
99  size_t vert_key = v.x + v.y * (res.x + 1) + v.z * (res.x + 1) * (res.y + 1);
100  unordered_map<size_t, int>::iterator it = used_verts.find(vert_key);
101 
102  if (it != used_verts.end()) {
103  return it->second;
104  }
105 
106  int vertex_offset = vertices.size();
107  used_verts[vert_key] = vertex_offset;
108  vertices.push_back(v);
109  return vertex_offset;
110 }
111 
112 static void create_quad(int3 corners[8],
113  vector<int3> &vertices,
114  vector<QuadData> &quads,
115  int3 res,
116  unordered_map<size_t, int> &used_verts,
117  int face_index)
118 {
119  QuadData quad;
120  quad.v0 = add_vertex(corners[quads_indices[face_index][0]], vertices, res, used_verts);
121  quad.v1 = add_vertex(corners[quads_indices[face_index][1]], vertices, res, used_verts);
122  quad.v2 = add_vertex(corners[quads_indices[face_index][2]], vertices, res, used_verts);
123  quad.v3 = add_vertex(corners[quads_indices[face_index][3]], vertices, res, used_verts);
124  quad.normal = quads_normals[face_index];
125 
126  quads.push_back(quad);
127 }
128 #endif
129 
130 /* Create a mesh from a volume.
131  *
132  * The way the algorithm works is as follows:
133  *
134  * - The topologies of input OpenVDB grids are merged into a temporary grid.
135  * - Voxels of the temporary grid are dilated to account for the padding necessary for volume
136  * sampling.
137  * - Quads are created on the boundary between active and inactive leaf nodes of the temporary
138  * grid.
139  */
141  public:
142 #ifdef WITH_OPENVDB
143  /* use a MaskGrid to store the topology to save memory */
144  openvdb::MaskGrid::Ptr topology_grid;
145  openvdb::CoordBBox bbox;
146 #endif
148 
150 
151 #ifdef WITH_OPENVDB
152  void add_grid(openvdb::GridBase::ConstPtr grid, bool do_clipping, float volume_clipping);
153 #endif
154 
155  void add_padding(int pad_size);
156 
157  void create_mesh(vector<float3> &vertices,
159  vector<float3> &face_normals,
160  const float face_overlap_avoidance);
161 
162  void generate_vertices_and_quads(vector<int3> &vertices_is, vector<QuadData> &quads);
163 
164  void convert_object_space(const vector<int3> &vertices,
165  vector<float3> &out_vertices,
166  const float face_overlap_avoidance);
167 
168  void convert_quads_to_tris(const vector<QuadData> &quads,
169  vector<int> &tris,
170  vector<float3> &face_normals);
171 
172  bool empty_grid() const;
173 
174 #ifdef WITH_OPENVDB
175  template<typename GridType>
176  void merge_grid(openvdb::GridBase::ConstPtr grid, bool do_clipping, float volume_clipping)
177  {
178  typename GridType::ConstPtr typed_grid = openvdb::gridConstPtrCast<GridType>(grid);
179 
180  if (do_clipping) {
181  using ValueType = typename GridType::ValueType;
182  typename GridType::Ptr copy = typed_grid->deepCopy();
183  typename GridType::ValueOnIter iter = copy->beginValueOn();
184 
185  for (; iter; ++iter) {
186  if (openvdb::math::Abs(iter.getValue()) < ValueType(volume_clipping)) {
187  iter.setValueOff();
188  }
189  }
190 
191  typed_grid = copy;
192  }
193 
194  topology_grid->topologyUnion(*typed_grid);
195  }
196 #endif
197 };
198 
200 {
201  first_grid = true;
202 }
203 
204 #ifdef WITH_OPENVDB
205 void VolumeMeshBuilder::add_grid(openvdb::GridBase::ConstPtr grid,
206  bool do_clipping,
207  float volume_clipping)
208 {
209  /* set the transform of our grid from the first one */
210  if (first_grid) {
211  topology_grid = openvdb::MaskGrid::create();
212  topology_grid->setTransform(grid->transform().copy());
213  first_grid = false;
214  }
215  /* if the transforms do not match, we need to resample one of the grids so that
216  * its index space registers with that of the other, here we resample our mask
217  * grid so memory usage is kept low */
218  else if (topology_grid->transform() != grid->transform()) {
219  openvdb::MaskGrid::Ptr temp_grid = topology_grid->copyWithNewTree();
220  temp_grid->setTransform(grid->transform().copy());
221  openvdb::tools::resampleToMatch<openvdb::tools::BoxSampler>(*topology_grid, *temp_grid);
222  topology_grid = temp_grid;
223  topology_grid->setTransform(grid->transform().copy());
224  }
225 
226  if (grid->isType<openvdb::FloatGrid>()) {
227  merge_grid<openvdb::FloatGrid>(grid, do_clipping, volume_clipping);
228  }
229  else if (grid->isType<openvdb::Vec3fGrid>()) {
230  merge_grid<openvdb::Vec3fGrid>(grid, do_clipping, volume_clipping);
231  }
232  else if (grid->isType<openvdb::Vec4fGrid>()) {
233  merge_grid<openvdb::Vec4fGrid>(grid, do_clipping, volume_clipping);
234  }
235  else if (grid->isType<openvdb::BoolGrid>()) {
236  merge_grid<openvdb::BoolGrid>(grid, do_clipping, volume_clipping);
237  }
238  else if (grid->isType<openvdb::DoubleGrid>()) {
239  merge_grid<openvdb::DoubleGrid>(grid, do_clipping, volume_clipping);
240  }
241  else if (grid->isType<openvdb::Int32Grid>()) {
242  merge_grid<openvdb::Int32Grid>(grid, do_clipping, volume_clipping);
243  }
244  else if (grid->isType<openvdb::Int64Grid>()) {
245  merge_grid<openvdb::Int64Grid>(grid, do_clipping, volume_clipping);
246  }
247  else if (grid->isType<openvdb::Vec3IGrid>()) {
248  merge_grid<openvdb::Vec3IGrid>(grid, do_clipping, volume_clipping);
249  }
250  else if (grid->isType<openvdb::Vec3dGrid>()) {
251  merge_grid<openvdb::Vec3dGrid>(grid, do_clipping, volume_clipping);
252  }
253  else if (grid->isType<openvdb::MaskGrid>()) {
254  topology_grid->topologyUnion(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid));
255  }
256 }
257 #endif
258 
260 {
261 #ifdef WITH_OPENVDB
262  openvdb::tools::dilateActiveValues(
263  topology_grid->tree(), pad_size, openvdb::tools::NN_FACE, openvdb::tools::IGNORE_TILES);
264 #else
265  (void)pad_size;
266 #endif
267 }
268 
271  vector<float3> &face_normals,
272  const float face_overlap_avoidance)
273 {
274 #ifdef WITH_OPENVDB
275  /* We create vertices in index space (is), and only convert them to object
276  * space when done. */
277  vector<int3> vertices_is;
278  vector<QuadData> quads;
279 
280  /* make sure we only have leaf nodes in the tree, as tiles are not handled by
281  * this algorithm */
282  topology_grid->tree().voxelizeActiveTiles();
283 
284  generate_vertices_and_quads(vertices_is, quads);
285 
286  convert_object_space(vertices_is, vertices, face_overlap_avoidance);
287 
288  convert_quads_to_tris(quads, indices, face_normals);
289 #else
290  (void)vertices;
291  (void)indices;
292  (void)face_normals;
293  (void)face_overlap_avoidance;
294 #endif
295 }
296 
297 #ifdef WITH_OPENVDB
298 static bool is_non_empty_leaf(const openvdb::MaskGrid::TreeType &tree, const openvdb::Coord coord)
299 {
300  auto *leaf_node = tree.probeLeaf(coord);
301  return (leaf_node && !leaf_node->isEmpty());
302 }
303 #endif
304 
306  vector<QuadData> &quads)
307 {
308 #ifdef WITH_OPENVDB
309  const openvdb::MaskGrid::TreeType &tree = topology_grid->tree();
310  tree.evalLeafBoundingBox(bbox);
311 
312  const int3 resolution = make_int3(bbox.dim().x(), bbox.dim().y(), bbox.dim().z());
313 
314  unordered_map<size_t, int> used_verts;
315 
316  for (auto iter = tree.cbeginLeaf(); iter; ++iter) {
317  if (iter->isEmpty()) {
318  continue;
319  }
320 
321  openvdb::CoordBBox leaf_bbox = iter->getNodeBoundingBox();
322  /* +1 to convert from exclusive to include bounds. */
323  leaf_bbox.max() = leaf_bbox.max().offsetBy(1);
324 
325  int3 min = make_int3(leaf_bbox.min().x(), leaf_bbox.min().y(), leaf_bbox.min().z());
326  int3 max = make_int3(leaf_bbox.max().x(), leaf_bbox.max().y(), leaf_bbox.max().z());
327 
328  int3 corners[8] = {
329  make_int3(min[0], min[1], min[2]),
330  make_int3(max[0], min[1], min[2]),
331  make_int3(max[0], max[1], min[2]),
332  make_int3(min[0], max[1], min[2]),
333  make_int3(min[0], min[1], max[2]),
334  make_int3(max[0], min[1], max[2]),
335  make_int3(max[0], max[1], max[2]),
336  make_int3(min[0], max[1], max[2]),
337  };
338 
339  /* Only create a quad if on the border between an active and an inactive leaf.
340  *
341  * We verify that a leaf exists by probing a coordinate that is at its center,
342  * to do so we compute the center of the current leaf and offset this coordinate
343  * by the size of a leaf in each direction.
344  */
345  static const int LEAF_DIM = openvdb::MaskGrid::TreeType::LeafNodeType::DIM;
346  auto center = leaf_bbox.min() + openvdb::Coord(LEAF_DIM / 2);
347 
348  if (!is_non_empty_leaf(tree, openvdb::Coord(center.x() - LEAF_DIM, center.y(), center.z()))) {
349  create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_X_MIN);
350  }
351 
352  if (!is_non_empty_leaf(tree, openvdb::Coord(center.x() + LEAF_DIM, center.y(), center.z()))) {
353  create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_X_MAX);
354  }
355 
356  if (!is_non_empty_leaf(tree, openvdb::Coord(center.x(), center.y() - LEAF_DIM, center.z()))) {
357  create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Y_MIN);
358  }
359 
360  if (!is_non_empty_leaf(tree, openvdb::Coord(center.x(), center.y() + LEAF_DIM, center.z()))) {
361  create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Y_MAX);
362  }
363 
364  if (!is_non_empty_leaf(tree, openvdb::Coord(center.x(), center.y(), center.z() - LEAF_DIM))) {
365  create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Z_MIN);
366  }
367 
368  if (!is_non_empty_leaf(tree, openvdb::Coord(center.x(), center.y(), center.z() + LEAF_DIM))) {
369  create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Z_MAX);
370  }
371  }
372 #else
373  (void)vertices_is;
374  (void)quads;
375 #endif
376 }
377 
379  vector<float3> &out_vertices,
380  const float face_overlap_avoidance)
381 {
382 #ifdef WITH_OPENVDB
383  /* compute the offset for the face overlap avoidance */
384  bbox = topology_grid->evalActiveVoxelBoundingBox();
385  openvdb::Coord dim = bbox.dim();
386 
387  float3 cell_size = make_float3(1.0f / dim.x(), 1.0f / dim.y(), 1.0f / dim.z());
388  float3 point_offset = cell_size * face_overlap_avoidance;
389 
390  out_vertices.reserve(vertices.size());
391 
392  for (size_t i = 0; i < vertices.size(); ++i) {
393  openvdb::math::Vec3d p = topology_grid->indexToWorld(
394  openvdb::math::Vec3d(vertices[i].x, vertices[i].y, vertices[i].z));
395  float3 vertex = make_float3((float)p.x(), (float)p.y(), (float)p.z());
396  out_vertices.push_back(vertex + point_offset);
397  }
398 #else
399  (void)vertices;
400  (void)out_vertices;
401  (void)face_overlap_avoidance;
402 #endif
403 }
404 
406  vector<int> &tris,
407  vector<float3> &face_normals)
408 {
409  int index_offset = 0;
410  tris.resize(quads.size() * 6);
411  face_normals.reserve(quads.size() * 2);
412 
413  for (size_t i = 0; i < quads.size(); ++i) {
414  tris[index_offset++] = quads[i].v0;
415  tris[index_offset++] = quads[i].v2;
416  tris[index_offset++] = quads[i].v1;
417 
418  face_normals.push_back(quads[i].normal);
419 
420  tris[index_offset++] = quads[i].v0;
421  tris[index_offset++] = quads[i].v3;
422  tris[index_offset++] = quads[i].v2;
423 
424  face_normals.push_back(quads[i].normal);
425  }
426 }
427 
429 {
430 #ifdef WITH_OPENVDB
431  return !topology_grid ||
432  (!topology_grid->tree().hasActiveTiles() && topology_grid->tree().leafCount() == 0);
433 #else
434  return true;
435 #endif
436 }
437 
438 #ifdef WITH_OPENVDB
439 template<typename GridType>
440 static openvdb::GridBase::ConstPtr openvdb_grid_from_device_texture(device_texture *image_memory,
441  float volume_clipping,
442  Transform transform_3d)
443 {
444  using ValueType = typename GridType::ValueType;
445 
446  openvdb::CoordBBox dense_bbox(0,
447  0,
448  0,
449  image_memory->data_width - 1,
450  image_memory->data_height - 1,
451  image_memory->data_depth - 1);
452 
453  typename GridType::Ptr sparse = GridType::create(ValueType(0.0f));
454  if (dense_bbox.empty()) {
455  return sparse;
456  }
457 
458  openvdb::tools::Dense<ValueType, openvdb::tools::MemoryLayout::LayoutXYZ> dense(
459  dense_bbox, static_cast<ValueType *>(image_memory->host_pointer));
460 
461  openvdb::tools::copyFromDense(dense, *sparse, ValueType(volume_clipping));
462 
463  /* #copyFromDense will remove any leaf node that contains constant data and replace it with a
464  * tile, however, we need to preserve the leaves in order to generate the mesh, so re-voxelize
465  * the leaves that were pruned. This should not affect areas that were skipped due to the
466  * volume_clipping parameter. */
467  sparse->tree().voxelizeActiveTiles();
468 
469  /* Compute index to world matrix. */
470  float3 voxel_size = make_float3(1.0f / image_memory->data_width,
471  1.0f / image_memory->data_height,
472  1.0f / image_memory->data_depth);
473 
474  transform_3d = transform_inverse(transform_3d);
475 
476  openvdb::Mat4R index_to_world_mat((double)(voxel_size.x * transform_3d[0][0]),
477  0.0,
478  0.0,
479  0.0,
480  0.0,
481  (double)(voxel_size.y * transform_3d[1][1]),
482  0.0,
483  0.0,
484  0.0,
485  0.0,
486  (double)(voxel_size.z * transform_3d[2][2]),
487  0.0,
488  (double)transform_3d[0][3],
489  (double)transform_3d[1][3],
490  (double)transform_3d[2][3],
491  1.0);
492 
493  openvdb::math::Transform::Ptr index_to_world_tfm =
494  openvdb::math::Transform::createLinearTransform(index_to_world_mat);
495 
496  sparse->setTransform(index_to_world_tfm);
497 
498  return sparse;
499 }
500 
501 static int estimate_required_velocity_padding(openvdb::GridBase::ConstPtr grid,
502  float velocity_scale)
503 {
504  /* TODO: we may need to also find outliers and clamp them to avoid adding too much padding. */
505  openvdb::math::Extrema extrema;
506  openvdb::Vec3d voxel_size;
507 
508  /* External .vdb files have a vec3 type for velocity, but the Blender exporter creates a vec4. */
509  if (grid->isType<openvdb::Vec3fGrid>()) {
510  openvdb::Vec3fGrid::ConstPtr vel_grid = openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid);
511  extrema = openvdb::tools::extrema(vel_grid->cbeginValueOn());
512  voxel_size = vel_grid->voxelSize();
513  }
514  else if (grid->isType<openvdb::Vec4fGrid>()) {
515  openvdb::Vec4fGrid::ConstPtr vel_grid = openvdb::gridConstPtrCast<openvdb::Vec4fGrid>(grid);
516  extrema = openvdb::tools::extrema(vel_grid->cbeginValueOn());
517  voxel_size = vel_grid->voxelSize();
518  }
519  else {
520  assert(0);
521  return 0;
522  }
523 
524  /* We should only have uniform grids, so x = y = z, but we never know. */
525  const double max_voxel_size = openvdb::math::Max(voxel_size.x(), voxel_size.y(), voxel_size.z());
526  if (max_voxel_size == 0.0) {
527  return 0;
528  }
529 
530  const double estimated_padding = extrema.max() * static_cast<double>(velocity_scale) /
531  max_voxel_size;
532 
533  return static_cast<int>(std::ceil(estimated_padding));
534 }
535 
536 static openvdb::FloatGrid::ConstPtr get_vdb_for_attribute(Volume *volume, AttributeStandard std)
537 {
538  Attribute *attr = volume->attributes.find(std);
539  if (!attr) {
540  return nullptr;
541  }
542 
543  ImageHandle &handle = attr->data_voxel();
544  VDBImageLoader *vdb_loader = handle.vdb_loader();
545  if (!vdb_loader) {
546  return nullptr;
547  }
548 
549  openvdb::GridBase::ConstPtr grid = vdb_loader->get_grid();
550  if (!grid) {
551  return nullptr;
552  }
553 
554  if (!grid->isType<openvdb::FloatGrid>()) {
555  return nullptr;
556  }
557 
558  return openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid);
559 }
560 
561 class MergeScalarGrids {
562  typedef openvdb::FloatTree ScalarTree;
563 
564  openvdb::tree::ValueAccessor<const ScalarTree> m_acc_x, m_acc_y, m_acc_z;
565 
566  public:
567  MergeScalarGrids(const ScalarTree *x_tree, const ScalarTree *y_tree, const ScalarTree *z_tree)
568  : m_acc_x(*x_tree), m_acc_y(*y_tree), m_acc_z(*z_tree)
569  {
570  }
571 
572  MergeScalarGrids(const MergeScalarGrids &other)
573  : m_acc_x(other.m_acc_x), m_acc_y(other.m_acc_y), m_acc_z(other.m_acc_z)
574  {
575  }
576 
577  void operator()(const openvdb::Vec3STree::ValueOnIter &it) const
578  {
579  using namespace openvdb;
580 
581  const math::Coord xyz = it.getCoord();
582  float x = m_acc_x.getValue(xyz);
583  float y = m_acc_y.getValue(xyz);
584  float z = m_acc_z.getValue(xyz);
585 
586  it.setValue(math::Vec3s(x, y, z));
587  }
588 };
589 
590 static void merge_scalar_grids_for_velocity(const Scene *scene, Volume *volume)
591 {
593  /* A vector grid for velocity is already available. */
594  return;
595  }
596 
597  openvdb::FloatGrid::ConstPtr vel_x_grid = get_vdb_for_attribute(volume,
599  openvdb::FloatGrid::ConstPtr vel_y_grid = get_vdb_for_attribute(volume,
601  openvdb::FloatGrid::ConstPtr vel_z_grid = get_vdb_for_attribute(volume,
603 
604  if (!(vel_x_grid && vel_y_grid && vel_z_grid)) {
605  return;
606  }
607 
608  openvdb::Vec3fGrid::Ptr vecgrid = openvdb::Vec3SGrid::create(openvdb::Vec3s(0.0f));
609 
610  /* Activate voxels in the vector grid based on the scalar grids to ensure thread safety during
611  * the merge. */
612  vecgrid->tree().topologyUnion(vel_x_grid->tree());
613  vecgrid->tree().topologyUnion(vel_y_grid->tree());
614  vecgrid->tree().topologyUnion(vel_z_grid->tree());
615 
616  MergeScalarGrids op(&vel_x_grid->tree(), &vel_y_grid->tree(), &vel_z_grid->tree());
617  openvdb::tools::foreach (vecgrid->beginValueOn(), op, true, false);
618 
619  /* Assume all grids have the same transformation. */
620  openvdb::math::Transform::Ptr transform = openvdb::ConstPtrCast<openvdb::math::Transform>(
621  vel_x_grid->transformPtr());
622  vecgrid->setTransform(transform);
623 
624  /* Make an attribute for it. */
626  ImageLoader *loader = new VDBImageLoader(vecgrid, "merged_velocity");
628  attr->data_voxel() = scene->image_manager->add_image(loader, params);
629 }
630 #endif
631 
632 /* ************************************************************************** */
633 
635 {
636  string msg = string_printf("Computing Volume Mesh %s", volume->name.c_str());
637  progress.set_status("Updating Mesh", msg);
638 
639  /* Find shader and compute padding based on volume shader interpolation settings. */
640  Shader *volume_shader = NULL;
641  int pad_size = 0;
642 
643  for (Node *node : volume->get_used_shaders()) {
644  Shader *shader = static_cast<Shader *>(node);
645 
646  if (!shader->has_volume) {
647  continue;
648  }
649 
650  volume_shader = shader;
651 
652  if (shader->get_volume_interpolation_method() == VOLUME_INTERPOLATION_LINEAR) {
653  pad_size = max(1, pad_size);
654  }
655  else if (shader->get_volume_interpolation_method() == VOLUME_INTERPOLATION_CUBIC) {
656  pad_size = max(2, pad_size);
657  }
658 
659  break;
660  }
661 
662  /* Clear existing volume mesh, done here in case we early out due to
663  * empty grid or missing volume shader.
664  * Also keep the shaders to avoid infinite loops when synchronizing, as this will tag the shaders
665  * as having changed. */
666  volume->clear(true);
667  volume->need_update_rebuild = true;
668 
669  if (!volume_shader) {
670  return;
671  }
672 
673  /* Create volume mesh builder. */
674  VolumeMeshBuilder builder;
675 
676 #ifdef WITH_OPENVDB
677  merge_scalar_grids_for_velocity(scene, volume);
678 
679  for (Attribute &attr : volume->attributes.attributes) {
680  if (attr.element != ATTR_ELEMENT_VOXEL) {
681  continue;
682  }
683 
684  bool do_clipping = false;
685 
686  ImageHandle &handle = attr.data_voxel();
687 
688  /* Try building from OpenVDB grid directly. */
689  VDBImageLoader *vdb_loader = handle.vdb_loader();
690  openvdb::GridBase::ConstPtr grid;
691  if (vdb_loader) {
692  grid = vdb_loader->get_grid();
693 
694  /* If building from an OpenVDB grid, we need to manually clip the values. */
695  do_clipping = true;
696  }
697 
698  /* Else fall back to creating an OpenVDB grid from the dense volume data. */
699  if (!grid) {
700  device_texture *image_memory = handle.image_memory();
701 
702  if (image_memory->data_elements == 1) {
703  grid = openvdb_grid_from_device_texture<openvdb::FloatGrid>(
704  image_memory, volume->get_clipping(), handle.metadata().transform_3d);
705  }
706  else if (image_memory->data_elements == 3) {
707  grid = openvdb_grid_from_device_texture<openvdb::Vec3fGrid>(
708  image_memory, volume->get_clipping(), handle.metadata().transform_3d);
709  }
710  else if (image_memory->data_elements == 4) {
711  grid = openvdb_grid_from_device_texture<openvdb::Vec4fGrid>(
712  image_memory, volume->get_clipping(), handle.metadata().transform_3d);
713  }
714  }
715 
716  if (grid) {
717  /* Add padding based on the maximum velocity vector. */
719  pad_size = max(pad_size,
720  estimate_required_velocity_padding(grid, volume->get_velocity_scale()));
721  }
722 
723  builder.add_grid(grid, do_clipping, volume->get_clipping());
724  }
725  }
726 #else
727  (void)scene;
728 #endif
729 
730  /* If nothing to build, early out. */
731  if (builder.empty_grid()) {
732  return;
733  }
734 
735  builder.add_padding(pad_size);
736 
737  /* Slightly offset vertex coordinates to avoid overlapping faces with other
738  * volumes or meshes. The proper solution would be to improve intersection in
739  * the kernel to support robust handling of multiple overlapping faces or use
740  * an all-hit intersection similar to shadows. */
741  const float face_overlap_avoidance = 0.1f *
742  hash_uint_to_float(hash_string(volume->name.c_str()));
743 
744  /* Create mesh. */
745  vector<float3> vertices;
747  vector<float3> face_normals;
748  builder.create_mesh(vertices, indices, face_normals, face_overlap_avoidance);
749 
750  volume->reserve_mesh(vertices.size(), indices.size() / 3);
751  volume->used_shaders.clear();
752  volume->used_shaders.push_back_slow(volume_shader);
753 
754  for (size_t i = 0; i < vertices.size(); ++i) {
755  volume->add_vertex(vertices[i]);
756  }
757 
758  for (size_t i = 0; i < indices.size(); i += 3) {
759  volume->add_triangle(indices[i], indices[i + 1], indices[i + 2], 0, false);
760  }
761 
762  Attribute *attr_fN = volume->attributes.add(ATTR_STD_FACE_NORMAL);
763  float3 *fN = attr_fN->data_float3();
764 
765  for (size_t i = 0; i < face_normals.size(); ++i) {
766  fN[i] = face_normals[i];
767  }
768 
769  /* Print stats. */
770  VLOG_WORK << "Memory usage volume mesh: "
771  << ((vertices.size() + face_normals.size()) * sizeof(float3) +
772  indices.size() * sizeof(int)) /
773  (1024.0 * 1024.0)
774  << "Mb.";
775 }
776 
NSNotificationCenter * center
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble z
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
ATTR_WARN_UNUSED_RESULT const BMVert * v
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
SIMD_FORCE_INLINE btVector3 operator()(const btVector3 &x) const
Return the transform of the vector.
Definition: btTransform.h:90
Attribute * add(ustring name, TypeDesc type, AttributeElement element)
list< Attribute > attributes
Attribute * find(ustring name) const
ImageHandle & data_voxel()
AttributeElement element
AttributeStandard std
float3 * data_float3()
void create_volume_mesh(const Scene *scene, Volume *volume, Progress &progress)
bool need_update_rebuild
AttributeSet attributes
VDBImageLoader * vdb_loader(const int tile_index=0) const
device_texture * image_memory(const int tile_index=0) const
ImageMetaData metadata()
ImageHandle add_image(const string &filename, const ImageParams &params)
Transform transform_3d
void set_status(const string &status_, const string &substatus_="")
Definition: progress.h:248
bool has_volume
Definition: scene/shader.h:107
void convert_quads_to_tris(const vector< QuadData > &quads, vector< int > &tris, vector< float3 > &face_normals)
void add_padding(int pad_size)
bool empty_grid() const
void convert_object_space(const vector< int3 > &vertices, vector< float3 > &out_vertices, const float face_overlap_avoidance)
void create_mesh(vector< float3 > &vertices, vector< int > &indices, vector< float3 > &face_normals, const float face_overlap_avoidance)
void generate_vertices_and_quads(vector< int3 > &vertices_is, vector< QuadData > &quads)
#define CCL_NAMESPACE_END
Definition: cuda/compat.h:9
OperationNode * node
Scene scene
SyclQueue void void size_t num_bytes void
void * tree
#define foreach(x, y)
Definition: foreach.h:9
GPUBatch * quad
static uint hash_string(const char *str)
Definition: hash.h:363
ccl_device_inline float hash_uint_to_float(uint kx)
Definition: hash.h:117
IconTextureDrawCall normal
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline Transform transform_inverse(const Transform tfm)
ccl_gpu_kernel_postfix int ccl_global int * indices
AttributeStandard
Definition: kernel/types.h:612
@ ATTR_STD_VOLUME_VELOCITY_Y
Definition: kernel/types.h:640
@ ATTR_STD_VOLUME_VELOCITY_Z
Definition: kernel/types.h:641
@ ATTR_STD_VOLUME_VELOCITY
Definition: kernel/types.h:638
@ ATTR_STD_FACE_NORMAL
Definition: kernel/types.h:615
@ ATTR_STD_VOLUME_VELOCITY_X
Definition: kernel/types.h:639
@ ATTR_ELEMENT_VOXEL
Definition: kernel/types.h:609
#define VLOG_WORK
Definition: log.h:80
ccl_device_inline float3 ceil(const float3 &a)
Definition: math_float3.h:363
#define make_int3(x, y, z)
Definition: metal/compat.h:207
#define make_float3(x, y, z)
Definition: metal/compat.h:204
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
static void copy(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node)
#define SOCKET_FLOAT(name, ui_name, default_value,...)
Definition: node_type.h:191
#define SOCKET_BOOLEAN(name, ui_name, default_value,...)
Definition: node_type.h:185
@ VOLUME_INTERPOLATION_LINEAR
Definition: scene/shader.h:46
@ VOLUME_INTERPOLATION_CUBIC
Definition: scene/shader.h:47
CCL_NAMESPACE_BEGIN NODE_DEFINE(Volume)
@ QUAD_X_MAX
@ QUAD_Y_MIN
@ QUAD_Z_MIN
@ QUAD_Y_MAX
@ QUAD_X_MIN
@ QUAD_Z_MAX
#define min(a, b)
Definition: sort.c:35
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
Definition: string.cpp:22
void clear(bool preserve_shaders=false) override
Definition: scene/mesh.cpp:315
void reserve_mesh(int numverts, int numfaces)
Definition: scene/mesh.cpp:220
void add_vertex(float3 P)
Definition: scene/mesh.cpp:320
void add_triangle(int v0, int v1, int v2, int shader, bool smooth)
Definition: scene/mesh.cpp:342
static NodeType * add(const char *name, CreateFunc create, Type type=NONE, const NodeType *base=NULL)
ustring name
Definition: graph/node.h:174
float3 normal
MotionType need_motion() const
Definition: scene.cpp:387
ImageManager * image_manager
Definition: scene.h:222
@ MOTION_NONE
Definition: scene.h:259
NODE_DECLARE Volume()
virtual void clear(bool preserve_shaders=false) override
float z
float y
float x
float max