diff options
Diffstat (limited to 'intern/cycles/scene/volume.cpp')
-rw-r--r-- | intern/cycles/scene/volume.cpp | 149 |
1 files changed, 145 insertions, 4 deletions
diff --git a/intern/cycles/scene/volume.cpp b/intern/cycles/scene/volume.cpp index 0555fdd2fad..1357d5ed762 100644 --- a/intern/cycles/scene/volume.cpp +++ b/intern/cycles/scene/volume.cpp @@ -10,9 +10,9 @@ # include <openvdb/tools/Dense.h> # include <openvdb/tools/GridTransformer.h> # include <openvdb/tools/Morphology.h> +# include <openvdb/tools/Statistics.h> #endif -#include "util/foreach.h" #include "util/hash.h" #include "util/log.h" #include "util/openvdb.h" @@ -28,6 +28,7 @@ NODE_DEFINE(Volume) SOCKET_FLOAT(clipping, "Clipping", 0.001f); SOCKET_FLOAT(step_size, "Step Size", 0.0f); SOCKET_BOOLEAN(object_space, "Object Space", false); + SOCKET_FLOAT(velocity_scale, "Velocity Scale", 1.0f); return type; } @@ -482,11 +483,141 @@ static openvdb::GridBase::ConstPtr openvdb_grid_from_device_texture(device_textu return sparse; } + +static int estimate_required_velocity_padding(openvdb::GridBase::ConstPtr grid, + float velocity_scale) +{ + /* TODO: we may need to also find outliers and clamp them to avoid adding too much padding. */ + openvdb::math::Extrema extrema; + openvdb::Vec3d voxel_size; + + /* External .vdb files have a vec3 type for velocity, but the Blender exporter creates a vec4. */ + if (grid->isType<openvdb::Vec3fGrid>()) { + openvdb::Vec3fGrid::ConstPtr vel_grid = openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid); + extrema = openvdb::tools::extrema(vel_grid->cbeginValueOn()); + voxel_size = vel_grid->voxelSize(); + } + else if (grid->isType<openvdb::Vec4fGrid>()) { + openvdb::Vec4fGrid::ConstPtr vel_grid = openvdb::gridConstPtrCast<openvdb::Vec4fGrid>(grid); + extrema = openvdb::tools::extrema(vel_grid->cbeginValueOn()); + voxel_size = vel_grid->voxelSize(); + } + else { + assert(0); + return 0; + } + + /* We should only have uniform grids, so x = y = z, but we never know. */ + const double max_voxel_size = openvdb::math::Max(voxel_size.x(), voxel_size.y(), voxel_size.z()); + if (max_voxel_size == 0.0) { + return 0; + } + + const double estimated_padding = extrema.max() * static_cast<double>(velocity_scale) / + max_voxel_size; + + return static_cast<int>(std::ceil(estimated_padding)); +} + +static openvdb::FloatGrid::ConstPtr get_vdb_for_attribute(Volume *volume, AttributeStandard std) +{ + Attribute *attr = volume->attributes.find(std); + if (!attr) { + return nullptr; + } + + ImageHandle &handle = attr->data_voxel(); + VDBImageLoader *vdb_loader = handle.vdb_loader(); + if (!vdb_loader) { + return nullptr; + } + + openvdb::GridBase::ConstPtr grid = vdb_loader->get_grid(); + if (!grid) { + return nullptr; + } + + if (!grid->isType<openvdb::FloatGrid>()) { + return nullptr; + } + + return openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid); +} + +class MergeScalarGrids { + typedef openvdb::FloatTree ScalarTree; + + openvdb::tree::ValueAccessor<const ScalarTree> m_acc_x, m_acc_y, m_acc_z; + + public: + MergeScalarGrids(const ScalarTree *x_tree, const ScalarTree *y_tree, const ScalarTree *z_tree) + : m_acc_x(*x_tree), m_acc_y(*y_tree), m_acc_z(*z_tree) + { + } + + MergeScalarGrids(const MergeScalarGrids &other) + : m_acc_x(other.m_acc_x), m_acc_y(other.m_acc_y), m_acc_z(other.m_acc_z) + { + } + + void operator()(const openvdb::Vec3STree::ValueOnIter &it) const + { + using namespace openvdb; + + const math::Coord xyz = it.getCoord(); + float x = m_acc_x.getValue(xyz); + float y = m_acc_y.getValue(xyz); + float z = m_acc_z.getValue(xyz); + + it.setValue(math::Vec3s(x, y, z)); + } +}; + +static void merge_scalar_grids_for_velocity(const Scene *scene, Volume *volume) +{ + if (volume->attributes.find(ATTR_STD_VOLUME_VELOCITY)) { + /* A vector grid for velocity is already available. */ + return; + } + + openvdb::FloatGrid::ConstPtr vel_x_grid = get_vdb_for_attribute(volume, + ATTR_STD_VOLUME_VELOCITY_X); + openvdb::FloatGrid::ConstPtr vel_y_grid = get_vdb_for_attribute(volume, + ATTR_STD_VOLUME_VELOCITY_Y); + openvdb::FloatGrid::ConstPtr vel_z_grid = get_vdb_for_attribute(volume, + ATTR_STD_VOLUME_VELOCITY_Z); + + if (!(vel_x_grid && vel_y_grid && vel_z_grid)) { + return; + } + + openvdb::Vec3fGrid::Ptr vecgrid = openvdb::Vec3SGrid::create(openvdb::Vec3s(0.0f)); + + /* Activate voxels in the vector grid based on the scalar grids to ensure thread safety during + * the merge. */ + vecgrid->tree().topologyUnion(vel_x_grid->tree()); + vecgrid->tree().topologyUnion(vel_y_grid->tree()); + vecgrid->tree().topologyUnion(vel_z_grid->tree()); + + MergeScalarGrids op(&vel_x_grid->tree(), &vel_y_grid->tree(), &vel_z_grid->tree()); + openvdb::tools::foreach (vecgrid->beginValueOn(), op, true, false); + + /* Assume all grids have the same transformation. */ + openvdb::math::Transform::Ptr transform = openvdb::ConstPtrCast<openvdb::math::Transform>( + vel_x_grid->transformPtr()); + vecgrid->setTransform(transform); + + /* Make an attribute for it. */ + Attribute *attr = volume->attributes.add(ATTR_STD_VOLUME_VELOCITY); + ImageLoader *loader = new VDBImageLoader(vecgrid, "merged_velocity"); + ImageParams params; + attr->data_voxel() = scene->image_manager->add_image(loader, params); +} #endif /* ************************************************************************** */ -void GeometryManager::create_volume_mesh(Volume *volume, Progress &progress) +void GeometryManager::create_volume_mesh(const Scene *scene, Volume *volume, Progress &progress) { string msg = string_printf("Computing Volume Mesh %s", volume->name.c_str()); progress.set_status("Updating Mesh", msg); @@ -495,7 +626,7 @@ void GeometryManager::create_volume_mesh(Volume *volume, Progress &progress) Shader *volume_shader = NULL; int pad_size = 0; - foreach (Node *node, volume->get_used_shaders()) { + for (Node *node : volume->get_used_shaders()) { Shader *shader = static_cast<Shader *>(node); if (!shader->has_volume) { @@ -529,7 +660,9 @@ void GeometryManager::create_volume_mesh(Volume *volume, Progress &progress) VolumeMeshBuilder builder; #ifdef WITH_OPENVDB - foreach (Attribute &attr, volume->attributes.attributes) { + merge_scalar_grids_for_velocity(scene, volume); + + for (Attribute &attr : volume->attributes.attributes) { if (attr.element != ATTR_ELEMENT_VOXEL) { continue; } @@ -567,9 +700,17 @@ void GeometryManager::create_volume_mesh(Volume *volume, Progress &progress) } if (grid) { + /* Add padding based on the maximum velocity vector. */ + if (attr.std == ATTR_STD_VOLUME_VELOCITY && scene->need_motion() != Scene::MOTION_NONE) { + pad_size = max(pad_size, + estimate_required_velocity_padding(grid, volume->get_velocity_scale())); + } + builder.add_grid(grid, do_clipping, volume->get_clipping()); } } +#else + (void)scene; #endif /* If nothing to build, early out. */ |