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