11# include <openvdb/tools/Dense.h>
12# include <openvdb/tools/GridTransformer.h>
13# include <openvdb/tools/Morphology.h>
14# include <openvdb/tools/Statistics.h>
65const int quads_indices[6][4] = {
80const float3 quads_normals[6] = {
95static int add_vertex(
int3 v,
98 unordered_map<size_t, int> &used_verts)
100 size_t vert_key =
v.x +
v.y * (res.
x + 1) +
v.z * (res.
x + 1) * (res.
y + 1);
101 unordered_map<size_t, int>::iterator it = used_verts.find(vert_key);
103 if (it != used_verts.end()) {
107 int vertex_offset = vertices.size();
108 used_verts[vert_key] = vertex_offset;
109 vertices.push_back(
v);
110 return vertex_offset;
113static void create_quad(
int3 corners[8],
117 unordered_map<size_t, int> &used_verts,
121 quad.
v0 = add_vertex(corners[quads_indices[face_index][0]], vertices, res, used_verts);
122 quad.v1 = add_vertex(corners[quads_indices[face_index][1]], vertices, res, used_verts);
123 quad.v2 = add_vertex(corners[quads_indices[face_index][2]], vertices, res, used_verts);
124 quad.v3 = add_vertex(corners[quads_indices[face_index][3]], vertices, res, used_verts);
125 quad.normal = quads_normals[face_index];
127 quads.push_back(
quad);
145 openvdb::MaskGrid::Ptr topology_grid;
146 openvdb::CoordBBox bbox;
153 void add_grid(openvdb::GridBase::ConstPtr grid,
bool do_clipping,
float volume_clipping);
161 const float face_overlap_avoidance);
167 const float face_overlap_avoidance);
176 template<
typename Gr
idType>
177 void merge_grid(openvdb::GridBase::ConstPtr grid,
bool do_clipping,
float volume_clipping)
179 typename GridType::ConstPtr typed_grid = openvdb::gridConstPtrCast<GridType>(grid);
182 using ValueType =
typename GridType::ValueType;
183 typename GridType::Ptr
copy = typed_grid->deepCopy();
184 typename GridType::ValueOnIter iter =
copy->beginValueOn();
186 for (; iter; ++iter) {
187 if (openvdb::math::Abs(iter.getValue()) < ValueType(volume_clipping)) {
195 topology_grid->topologyUnion(*typed_grid);
206void VolumeMeshBuilder::add_grid(openvdb::GridBase::ConstPtr grid,
208 float volume_clipping)
212 topology_grid = openvdb::MaskGrid::create();
213 topology_grid->setTransform(grid->transform().copy());
219 else if (topology_grid->transform() != grid->transform()) {
220 openvdb::MaskGrid::Ptr temp_grid = topology_grid->copyWithNewTree();
221 temp_grid->setTransform(grid->transform().copy());
222 openvdb::tools::resampleToMatch<openvdb::tools::BoxSampler>(*topology_grid, *temp_grid);
223 topology_grid = temp_grid;
224 topology_grid->setTransform(grid->transform().copy());
227 if (grid->isType<openvdb::FloatGrid>()) {
228 merge_grid<openvdb::FloatGrid>(grid, do_clipping, volume_clipping);
230 else if (grid->isType<openvdb::Vec3fGrid>()) {
231 merge_grid<openvdb::Vec3fGrid>(grid, do_clipping, volume_clipping);
233 else if (grid->isType<openvdb::Vec4fGrid>()) {
234 merge_grid<openvdb::Vec4fGrid>(grid, do_clipping, volume_clipping);
236 else if (grid->isType<openvdb::BoolGrid>()) {
237 merge_grid<openvdb::BoolGrid>(grid, do_clipping, volume_clipping);
239 else if (grid->isType<openvdb::DoubleGrid>()) {
240 merge_grid<openvdb::DoubleGrid>(grid, do_clipping, volume_clipping);
242 else if (grid->isType<openvdb::Int32Grid>()) {
243 merge_grid<openvdb::Int32Grid>(grid, do_clipping, volume_clipping);
245 else if (grid->isType<openvdb::Int64Grid>()) {
246 merge_grid<openvdb::Int64Grid>(grid, do_clipping, volume_clipping);
248 else if (grid->isType<openvdb::Vec3IGrid>()) {
249 merge_grid<openvdb::Vec3IGrid>(grid, do_clipping, volume_clipping);
251 else if (grid->isType<openvdb::Vec3dGrid>()) {
252 merge_grid<openvdb::Vec3dGrid>(grid, do_clipping, volume_clipping);
254 else if (grid->isType<openvdb::MaskGrid>()) {
255 topology_grid->topologyUnion(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid));
263 openvdb::tools::dilateActiveValues(
264 topology_grid->tree(), pad_size, openvdb::tools::NN_FACE, openvdb::tools::IGNORE_TILES);
273 const float face_overlap_avoidance)
283 topology_grid->tree().voxelizeActiveTiles();
294 (void)face_overlap_avoidance;
299static bool is_non_empty_leaf(
const openvdb::MaskGrid::TreeType &
tree,
const openvdb::Coord coord)
301 auto *leaf_node =
tree.probeLeaf(coord);
302 return (leaf_node && !leaf_node->isEmpty());
310 const openvdb::MaskGrid::TreeType &
tree = topology_grid->tree();
311 tree.evalLeafBoundingBox(bbox);
313 const int3 resolution =
make_int3(bbox.dim().x(), bbox.dim().y(), bbox.dim().z());
315 unordered_map<size_t, int> used_verts;
317 for (
auto iter =
tree.cbeginLeaf(); iter; ++iter) {
318 if (iter->isEmpty()) {
322 openvdb::CoordBBox leaf_bbox = iter->getNodeBoundingBox();
324 leaf_bbox.max() = leaf_bbox.max().offsetBy(1);
326 int3 min =
make_int3(leaf_bbox.min().x(), leaf_bbox.min().y(), leaf_bbox.min().z());
327 int3 max =
make_int3(leaf_bbox.max().x(), leaf_bbox.max().y(), leaf_bbox.max().z());
346 static const int LEAF_DIM = openvdb::MaskGrid::TreeType::LeafNodeType::DIM;
347 auto center = leaf_bbox.min() + openvdb::Coord(LEAF_DIM / 2);
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_MIN);
353 if (!is_non_empty_leaf(
tree, openvdb::Coord(center.x() + LEAF_DIM, center.y(), center.z()))) {
354 create_quad(corners, vertices_is, quads, resolution, used_verts,
QUAD_X_MAX);
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_MIN);
361 if (!is_non_empty_leaf(
tree, openvdb::Coord(center.x(), center.y() + LEAF_DIM, center.z()))) {
362 create_quad(corners, vertices_is, quads, resolution, used_verts,
QUAD_Y_MAX);
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_MIN);
369 if (!is_non_empty_leaf(
tree, openvdb::Coord(center.x(), center.y(), center.z() + LEAF_DIM))) {
370 create_quad(corners, vertices_is, quads, resolution, used_verts,
QUAD_Z_MAX);
381 const float face_overlap_avoidance)
385 bbox = topology_grid->evalActiveVoxelBoundingBox();
386 openvdb::Coord dim = bbox.dim();
389 float3 point_offset = cell_size * face_overlap_avoidance;
391 out_vertices.reserve(vertices.size());
393 for (
size_t i = 0; i < vertices.size(); ++i) {
394 openvdb::math::Vec3d p = topology_grid->indexToWorld(
395 openvdb::math::Vec3d(vertices[i].x, vertices[i].y, vertices[i].
z));
397 out_vertices.push_back(vertex + point_offset);
402 (void)face_overlap_avoidance;
410 int index_offset = 0;
411 tris.resize(quads.size() * 6);
412 face_normals.reserve(quads.size() * 2);
414 for (
size_t i = 0; i < quads.size(); ++i) {
415 tris[index_offset++] = quads[i].v0;
416 tris[index_offset++] = quads[i].v2;
417 tris[index_offset++] = quads[i].v1;
419 face_normals.push_back(quads[i].normal);
421 tris[index_offset++] = quads[i].v0;
422 tris[index_offset++] = quads[i].v3;
423 tris[index_offset++] = quads[i].v2;
425 face_normals.push_back(quads[i].normal);
432 return !topology_grid ||
433 (!topology_grid->tree().hasActiveTiles() && topology_grid->tree().leafCount() == 0);
440template<
typename Gr
idType>
441static openvdb::GridBase::ConstPtr openvdb_grid_from_device_texture(
device_texture *image_memory,
442 float volume_clipping,
445 using ValueType =
typename GridType::ValueType;
447 openvdb::CoordBBox dense_bbox(0,
454 typename GridType::Ptr sparse = GridType::create(ValueType(0.0f));
455 if (dense_bbox.empty()) {
459 openvdb::tools::Dense<ValueType, openvdb::tools::MemoryLayout::LayoutXYZ> dense(
460 dense_bbox,
static_cast<ValueType *
>(image_memory->
host_pointer));
462 openvdb::tools::copyFromDense(dense, *sparse, ValueType(volume_clipping));
468 sparse->tree().voxelizeActiveTiles();
477 openvdb::Mat4R index_to_world_mat((
double)(voxel_size.
x * transform_3d[0][0]),
482 (
double)(voxel_size.
y * transform_3d[1][1]),
487 (
double)(voxel_size.
z * transform_3d[2][2]),
489 (
double)transform_3d[0][3],
490 (
double)transform_3d[1][3],
491 (
double)transform_3d[2][3],
494 openvdb::math::Transform::Ptr index_to_world_tfm =
495 openvdb::math::Transform::createLinearTransform(index_to_world_mat);
497 sparse->setTransform(index_to_world_tfm);
502static int estimate_required_velocity_padding(openvdb::GridBase::ConstPtr grid,
503 float velocity_scale)
506 openvdb::math::Extrema extrema;
507 openvdb::Vec3d voxel_size;
511 if (grid->isType<openvdb::Vec3fGrid>()) {
512 openvdb::Vec3fGrid::ConstPtr vel_grid = openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid);
513 extrema = openvdb::tools::extrema(vel_grid->cbeginValueOn());
514 voxel_size = vel_grid->voxelSize();
516 else if (grid->isType<openvdb::Vec4fGrid>()) {
517 openvdb::Vec4fGrid::ConstPtr vel_grid = openvdb::gridConstPtrCast<openvdb::Vec4fGrid>(grid);
518 extrema = openvdb::tools::extrema(vel_grid->cbeginValueOn());
519 voxel_size = vel_grid->voxelSize();
527 const double max_voxel_size = openvdb::math::Max(voxel_size.x(), voxel_size.y(), voxel_size.z());
528 if (max_voxel_size == 0.0) {
532 const double estimated_padding = extrema.max() *
static_cast<double>(velocity_scale) /
535 return static_cast<int>(std::ceil(estimated_padding));
540 Attribute *attr = volume->attributes.find(std);
551 openvdb::GridBase::ConstPtr grid = vdb_loader->get_grid();
556 if (!grid->isType<openvdb::FloatGrid>()) {
560 return openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid);
563class MergeScalarGrids {
566 openvdb::tree::ValueAccessor<const ScalarTree> m_acc_x, m_acc_y, m_acc_z;
569 MergeScalarGrids(
const ScalarTree *x_tree,
const ScalarTree *y_tree,
const ScalarTree *z_tree)
570 : m_acc_x(*x_tree), m_acc_y(*y_tree), m_acc_z(*z_tree)
574 MergeScalarGrids(
const MergeScalarGrids &other)
575 : m_acc_x(other.m_acc_x), m_acc_y(other.m_acc_y), m_acc_z(other.m_acc_z)
579 void operator()(
const openvdb::Vec3STree::ValueOnIter &it)
const
583 const math::Coord xyz = it.getCoord();
584 float x = m_acc_x.getValue(xyz);
585 float y = m_acc_y.getValue(xyz);
586 float z = m_acc_z.getValue(xyz);
588 it.setValue(math::Vec3s(x, y,
z));
592static void merge_scalar_grids_for_velocity(
const Scene *scene,
Volume *volume)
599 openvdb::FloatGrid::ConstPtr vel_x_grid = get_vdb_for_attribute(volume,
601 openvdb::FloatGrid::ConstPtr vel_y_grid = get_vdb_for_attribute(volume,
603 openvdb::FloatGrid::ConstPtr vel_z_grid = get_vdb_for_attribute(volume,
606 if (!(vel_x_grid && vel_y_grid && vel_z_grid)) {
610 openvdb::Vec3fGrid::Ptr vecgrid = openvdb::Vec3SGrid::create(openvdb::Vec3s(0.0f));
614 vecgrid->tree().topologyUnion(vel_x_grid->tree());
615 vecgrid->tree().topologyUnion(vel_y_grid->tree());
616 vecgrid->tree().topologyUnion(vel_z_grid->tree());
618 MergeScalarGrids op(&vel_x_grid->tree(), &vel_y_grid->tree(), &vel_z_grid->tree());
619 openvdb::tools::foreach (vecgrid->beginValueOn(), op,
true,
false);
622 openvdb::math::Transform::Ptr transform = openvdb::ConstPtrCast<openvdb::math::Transform>(
623 vel_x_grid->transformPtr());
624 vecgrid->setTransform(transform);
638 string msg =
string_printf(
"Computing Volume Mesh %s", volume->name.c_str());
645 for (
Node *node : volume->get_used_shaders()) {
648 if (!shader->has_volume) {
652 volume_shader = shader;
655 pad_size =
max(1, pad_size);
658 pad_size =
max(2, pad_size);
669 volume->need_update_rebuild =
true;
671 if (!volume_shader) {
679 merge_scalar_grids_for_velocity(scene, volume);
681 for (
Attribute &attr : volume->attributes.attributes) {
686 bool do_clipping =
false;
692 openvdb::GridBase::ConstPtr grid;
694 grid = vdb_loader->get_grid();
705 grid = openvdb_grid_from_device_texture<openvdb::FloatGrid>(
706 image_memory, volume->get_clipping(), handle.metadata().transform_3d);
709 grid = openvdb_grid_from_device_texture<openvdb::Vec3fGrid>(
710 image_memory, volume->get_clipping(), handle.metadata().transform_3d);
713 grid = openvdb_grid_from_device_texture<openvdb::Vec4fGrid>(
714 image_memory, volume->get_clipping(), handle.metadata().transform_3d);
721 pad_size =
max(pad_size,
722 estimate_required_velocity_padding(grid, volume->get_velocity_scale()));
725 builder.add_grid(grid, do_clipping, volume->get_clipping());
743 const float face_overlap_avoidance = 0.1f *
750 builder.
create_mesh(vertices, indices, face_normals, face_overlap_avoidance);
752 volume->reserve_mesh(vertices.size(), indices.size() / 3);
753 volume->used_shaders.clear();
754 volume->used_shaders.push_back_slow(volume_shader);
756 for (
size_t i = 0; i < vertices.size(); ++i) {
757 volume->add_vertex(vertices[i]);
760 for (
size_t i = 0; i < indices.size(); i += 3) {
761 volume->add_triangle(indices[i], indices[i + 1], indices[i + 2], 0,
false);
767 for (
size_t i = 0; i < face_normals.size(); ++i) {
768 fN[i] = face_normals[i];
772 VLOG_WORK <<
"Memory usage volume mesh: "
773 << ((vertices.size() + face_normals.size()) *
sizeof(
float3) +
774 indices.size() *
sizeof(
int)) /
ATTR_WARN_UNUSED_RESULT const BMVert * v
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
ImageHandle & data_voxel()
void create_volume_mesh(const Scene *scene, Volume *volume, Progress &progress)
void set_status(const string &status_, const string &substatus_="")
void convert_quads_to_tris(const vector< QuadData > &quads, vector< int > &tris, vector< float3 > &face_normals)
void add_padding(int pad_size)
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
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
blender::gpu::Batch * quad
static uint hash_string(const char *str)
ccl_device_inline float hash_uint_to_float(uint kx)
@ ATTR_STD_VOLUME_VELOCITY_Y
@ ATTR_STD_VOLUME_VELOCITY_Z
@ ATTR_STD_VOLUME_VELOCITY
@ ATTR_STD_VOLUME_VELOCITY_X
static void copy(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node)
#define SOCKET_FLOAT(name, ui_name, default_value,...)
#define NODE_DEFINE(structname)
#define SOCKET_BOOLEAN(name, ui_name, default_value,...)
@ VOLUME_INTERPOLATION_LINEAR
@ VOLUME_INTERPOLATION_CUBIC
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
void clear(bool preserve_shaders=false) override
static NodeType * add(const char *name, CreateFunc create, Type type=NONE, const NodeType *base=NULL)
virtual void clear(bool preserve_shaders=false) override