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(
const int3 v,
98 unordered_map<size_t, int> &used_verts)
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);
104 if (it != used_verts.end()) {
108 const int vertex_offset = vertices.size();
109 used_verts[vert_key] = vertex_offset;
110 vertices.push_back(
v);
111 return vertex_offset;
114static void create_quad(
const int3 corners[8],
118 unordered_map<size_t, int> &used_verts,
119 const int face_index)
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];
128 quads.push_back(
quad);
146 openvdb::MaskGrid::Ptr topology_grid;
147 openvdb::CoordBBox bbox;
154 void add_grid(openvdb::GridBase::ConstPtr grid,
bool do_clipping,
const float volume_clipping);
161 const float face_overlap_avoidance);
167 const float face_overlap_avoidance);
174 template<
typename Gr
idType>
175 void merge_grid(openvdb::GridBase::ConstPtr grid,
bool do_clipping,
const float volume_clipping)
177 typename GridType::ConstPtr typed_grid = openvdb::gridConstPtrCast<GridType>(grid);
180 using ValueType =
typename GridType::ValueType;
181 const typename GridType::Ptr
copy = typed_grid->deepCopy();
182 typename GridType::ValueOnIter iter =
copy->beginValueOn();
184 for (; iter; ++iter) {
185 if (openvdb::math::Abs(iter.getValue()) < ValueType(volume_clipping)) {
193 topology_grid->topologyUnion(*typed_grid);
204void VolumeMeshBuilder::add_grid(openvdb::GridBase::ConstPtr grid,
206 const float volume_clipping)
210 topology_grid = openvdb::MaskGrid::create();
211 topology_grid->setTransform(grid->transform().copy());
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());
225 if (grid->isType<openvdb::FloatGrid>()) {
226 merge_grid<openvdb::FloatGrid>(grid, do_clipping, volume_clipping);
228 else if (grid->isType<openvdb::Vec3fGrid>()) {
229 merge_grid<openvdb::Vec3fGrid>(grid, do_clipping, volume_clipping);
231 else if (grid->isType<openvdb::Vec4fGrid>()) {
232 merge_grid<openvdb::Vec4fGrid>(grid, do_clipping, volume_clipping);
234 else if (grid->isType<openvdb::BoolGrid>()) {
235 merge_grid<openvdb::BoolGrid>(grid, do_clipping, volume_clipping);
237 else if (grid->isType<openvdb::DoubleGrid>()) {
238 merge_grid<openvdb::DoubleGrid>(grid, do_clipping, volume_clipping);
240 else if (grid->isType<openvdb::Int32Grid>()) {
241 merge_grid<openvdb::Int32Grid>(grid, do_clipping, volume_clipping);
243 else if (grid->isType<openvdb::Int64Grid>()) {
244 merge_grid<openvdb::Int64Grid>(grid, do_clipping, volume_clipping);
246 else if (grid->isType<openvdb::Vec3IGrid>()) {
247 merge_grid<openvdb::Vec3IGrid>(grid, do_clipping, volume_clipping);
249 else if (grid->isType<openvdb::Vec3dGrid>()) {
250 merge_grid<openvdb::Vec3dGrid>(grid, do_clipping, volume_clipping);
252 else if (grid->isType<openvdb::MaskGrid>()) {
253 topology_grid->topologyUnion(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid));
261 openvdb::tools::dilateActiveValues(
262 topology_grid->tree(), pad_size, openvdb::tools::NN_FACE, openvdb::tools::IGNORE_TILES);
270 const float face_overlap_avoidance)
280 topology_grid->tree().voxelizeActiveTiles();
290 (void)face_overlap_avoidance;
295static bool is_non_empty_leaf(
const openvdb::MaskGrid::TreeType &
tree,
const openvdb::Coord coord)
297 const auto *leaf_node =
tree.probeLeaf(coord);
298 return (leaf_node && !leaf_node->isEmpty());
306 const openvdb::MaskGrid::TreeType &
tree = topology_grid->tree();
307 tree.evalLeafBoundingBox(bbox);
309 const int3 resolution =
make_int3(bbox.dim().x(), bbox.dim().y(), bbox.dim().z());
311 unordered_map<size_t, int> used_verts;
313 for (
auto iter =
tree.cbeginLeaf(); iter; ++iter) {
314 if (iter->isEmpty()) {
318 openvdb::CoordBBox leaf_bbox = iter->getNodeBoundingBox();
320 leaf_bbox.max() = leaf_bbox.max().offsetBy(1);
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());
342 static const int LEAF_DIM = openvdb::MaskGrid::TreeType::LeafNodeType::DIM;
343 auto center = leaf_bbox.min() + openvdb::Coord(LEAF_DIM / 2);
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);
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);
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);
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);
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);
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);
377 const float face_overlap_avoidance)
381 bbox = topology_grid->evalActiveVoxelBoundingBox();
382 openvdb::Coord dim = bbox.dim();
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;
387 out_vertices.reserve(vertices.size());
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));
393 out_vertices.push_back(vertex + point_offset);
398 (void)face_overlap_avoidance;
404 int index_offset = 0;
405 tris.resize(quads.size() * 6);
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;
412 tris[index_offset++] = quads[
i].v0;
413 tris[index_offset++] = quads[
i].v3;
414 tris[index_offset++] = quads[
i].v2;
421 return !topology_grid ||
422 (!topology_grid->tree().hasActiveTiles() && topology_grid->tree().leafCount() == 0);
429template<
typename Gr
idType>
430static openvdb::GridBase::ConstPtr openvdb_grid_from_device_texture(
device_texture *image_memory,
431 const float volume_clipping,
434 using ValueType =
typename GridType::ValueType;
436 const openvdb::CoordBBox dense_bbox(0,
443 typename GridType::Ptr sparse = GridType::create(ValueType(0.0f));
444 if (dense_bbox.empty()) {
448 const openvdb::tools::Dense<ValueType, openvdb::tools::MemoryLayout::LayoutXYZ> dense(
449 dense_bbox,
static_cast<ValueType *
>(image_memory->
host_pointer));
451 openvdb::tools::copyFromDense(dense, *sparse, ValueType(volume_clipping));
457 sparse->tree().voxelizeActiveTiles();
466 const openvdb::Mat4R index_to_world_mat((
double)(voxel_size.
x * transform_3d[0][0]),
471 (
double)(voxel_size.
y * transform_3d[1][1]),
476 (
double)(voxel_size.
z * transform_3d[2][2]),
478 (
double)transform_3d[0][3],
479 (
double)transform_3d[1][3],
480 (
double)transform_3d[2][3],
483 const openvdb::math::Transform::Ptr index_to_world_tfm =
484 openvdb::math::Transform::createLinearTransform(index_to_world_mat);
486 sparse->setTransform(index_to_world_tfm);
491static int estimate_required_velocity_padding(openvdb::GridBase::ConstPtr grid,
492 const float velocity_scale)
495 openvdb::math::Extrema extrema;
496 openvdb::Vec3d voxel_size;
500 if (grid->isType<openvdb::Vec3fGrid>()) {
501 const openvdb::Vec3fGrid::ConstPtr vel_grid = openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(
503 extrema = openvdb::tools::extrema(vel_grid->cbeginValueOn());
504 voxel_size = vel_grid->voxelSize();
506 else if (grid->isType<openvdb::Vec4fGrid>()) {
507 const openvdb::Vec4fGrid::ConstPtr vel_grid = openvdb::gridConstPtrCast<openvdb::Vec4fGrid>(
509 extrema = openvdb::tools::extrema(vel_grid->cbeginValueOn());
510 voxel_size = vel_grid->voxelSize();
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) {
523 const double estimated_padding = extrema.max() *
static_cast<double>(velocity_scale) /
526 return static_cast<int>(std::ceil(estimated_padding));
542 const openvdb::GridBase::ConstPtr grid = vdb_loader->get_grid();
547 if (!grid->isType<openvdb::FloatGrid>()) {
551 return openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid);
554class MergeScalarGrids {
557 openvdb::tree::ValueAccessor<const ScalarTree> m_acc_x, m_acc_y, m_acc_z;
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)
565 MergeScalarGrids(
const MergeScalarGrids &other)
569 void operator()(
const openvdb::Vec3STree::ValueOnIter &it)
const
571 using namespace openvdb;
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);
578 it.setValue(math::Vec3s(
x,
y,
z));
582static void merge_scalar_grids_for_velocity(
const Scene *scene,
Volume *volume)
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(
596 if (!(vel_x_grid && vel_y_grid && vel_z_grid)) {
600 const openvdb::Vec3fGrid::Ptr vecgrid = openvdb::Vec3SGrid::create(openvdb::Vec3s(0.0f));
604 vecgrid->tree().topologyUnion(vel_x_grid->tree());
605 vecgrid->tree().topologyUnion(vel_y_grid->tree());
606 vecgrid->tree().topologyUnion(vel_z_grid->tree());
608 MergeScalarGrids op(&vel_x_grid->tree(), &vel_y_grid->tree(), &vel_z_grid->tree());
609 openvdb::tools::foreach(vecgrid->beginValueOn(), op,
true,
false);
612 const openvdb::math::Transform::Ptr
transform = openvdb::ConstPtrCast<openvdb::math::Transform>(
613 vel_x_grid->transformPtr());
628 const string msg =
string_printf(
"Computing Volume Mesh %s", volume->
name.c_str());
629 progress.set_status(
"Updating Mesh", msg);
632 Shader *volume_shader =
nullptr;
635 for (
Node *node : volume->get_used_shaders()) {
642 volume_shader = shader;
645 pad_size =
max(1, pad_size);
648 pad_size =
max(2, pad_size);
661 if (!volume_shader) {
669 merge_scalar_grids_for_velocity(scene, volume);
676 bool do_clipping =
false;
680 if (handle.
empty()) {
686 openvdb::GridBase::ConstPtr grid;
688 grid = vdb_loader->get_grid();
699 grid = openvdb_grid_from_device_texture<openvdb::FloatGrid>(
703 grid = openvdb_grid_from_device_texture<openvdb::Vec3fGrid>(
707 grid = openvdb_grid_from_device_texture<openvdb::Vec4fGrid>(
715 pad_size =
max(pad_size,
716 estimate_required_velocity_padding(grid, volume->get_velocity_scale()));
719 builder.add_grid(grid, do_clipping, volume->get_clipping());
737 const float face_overlap_avoidance = 0.1f *
746 volume->used_shaders.
clear();
747 volume->used_shaders.push_back_slow(volume_shader);
749 for (
size_t i = 0;
i < vertices.size(); ++
i) {
753 for (
size_t i = 0;
i <
indices.size();
i += 3) {
758 VLOG_WORK <<
"Memory usage volume mesh: "
759 << (vertices.size() *
sizeof(
float3) +
indices.size() *
sizeof(
int)) /
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.
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)
Geometry(const NodeType *node_type, const Type type)
VDBImageLoader * vdb_loader() const
device_texture * image_memory() const
void add_padding(const int pad_size)
void create_mesh(vector< float3 > &vertices, vector< int > &indices, const float face_overlap_avoidance)
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
blender::gpu::Batch * quad
#define assert(assertion)
static uint hash_string(const char *str)
ccl_device_inline float hash_uint_to_float(const 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,...)
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)
MotionType need_motion() const
unique_ptr< ImageManager > image_manager
void clear(bool preserve_shaders=false) override