OpenVDB has a voxel size limit defined by the determinant of the grid transform, which is equivalent to a uniform voxel size of `sqrt3(3e-15) ~= 1.44e-5`. The `mesh_to_density_grid` function was using an arbitrary threshold of `1.0e-5` for the uniform voxel size. In this case the voxel size is `~1.343e-5` so it passes the Blender threshold but crashes in OpenVDB. This fix adds some convenience functions to check for valid grid voxel size and transform based on the same determinant metric. This is now employed consistently in the mesh_to_density_grid, mesh_to_sdf_grid, and points_to_sdf_grid functions to avoid exceptions in OpenVDB. MOD_volume_to_mesh, node_geo_volume_to_mesh, BKE_mesh_remesh_voxel have not been modified, since they have their own error checks with larger thresholds. Pull Request: https://projects.blender.org/blender/blender/pulls/138481
118 lines
3.8 KiB
C++
118 lines
3.8 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "BLI_math_base.hh"
|
|
|
|
#include "BKE_volume.hh"
|
|
#include "BKE_volume_grid.hh"
|
|
#include "BKE_volume_openvdb.hh"
|
|
|
|
#include "GEO_points_to_volume.hh"
|
|
|
|
#ifdef WITH_OPENVDB
|
|
# include <openvdb/openvdb.h>
|
|
# include <openvdb/tools/LevelSetUtil.h>
|
|
# include <openvdb/tools/ParticlesToLevelSet.h>
|
|
|
|
namespace blender::geometry {
|
|
|
|
/* Implements the interface required by #openvdb::tools::ParticlesToLevelSet. */
|
|
class OpenVDBParticleList {
|
|
public:
|
|
using PosType = openvdb::Vec3R;
|
|
|
|
private:
|
|
Span<float3> positions_;
|
|
Span<float> radii_;
|
|
float voxel_size_inv_;
|
|
|
|
public:
|
|
OpenVDBParticleList(const Span<float3> positions,
|
|
const Span<float> radii,
|
|
const float voxel_size)
|
|
: positions_(positions), radii_(radii), voxel_size_inv_(math::rcp(voxel_size))
|
|
{
|
|
BLI_assert(voxel_size > 0.0f);
|
|
}
|
|
|
|
size_t size() const
|
|
{
|
|
return size_t(positions_.size());
|
|
}
|
|
|
|
void getPos(size_t n, openvdb::Vec3R &xyz) const
|
|
{
|
|
float3 pos = positions_[n] * voxel_size_inv_;
|
|
/* Better align generated grid with source points. */
|
|
pos -= float3(0.5f);
|
|
xyz = &pos.x;
|
|
}
|
|
|
|
void getPosRad(size_t n, openvdb::Vec3R &xyz, openvdb::Real &radius) const
|
|
{
|
|
this->getPos(n, xyz);
|
|
radius = radii_[n] * voxel_size_inv_;
|
|
}
|
|
};
|
|
|
|
static openvdb::FloatGrid::Ptr points_to_sdf_grid_impl(const Span<float3> positions,
|
|
const Span<float> radii,
|
|
const float voxel_size)
|
|
{
|
|
if (!BKE_volume_voxel_size_valid(float3(voxel_size))) {
|
|
return nullptr;
|
|
}
|
|
|
|
/* Create a new grid that will be filled. #ParticlesToLevelSet requires
|
|
* the background value to be positive */
|
|
openvdb::FloatGrid::Ptr new_grid = openvdb::FloatGrid::create(1.0f);
|
|
|
|
/* Create a narrow-band level set grid based on the positions and radii. */
|
|
openvdb::tools::ParticlesToLevelSet op{*new_grid};
|
|
/* Don't ignore particles based on their radius. */
|
|
op.setRmin(0.0f);
|
|
op.setRmax(std::numeric_limits<float>::max());
|
|
OpenVDBParticleList particles{positions, radii, voxel_size};
|
|
op.rasterizeSpheres(particles);
|
|
op.finalize();
|
|
|
|
new_grid->transform().postScale(voxel_size);
|
|
new_grid->setGridClass(openvdb::GRID_LEVEL_SET);
|
|
|
|
return new_grid;
|
|
}
|
|
|
|
bke::VolumeGrid<float> points_to_sdf_grid(const Span<float3> positions,
|
|
const Span<float> radii,
|
|
const float voxel_size)
|
|
{
|
|
return bke::VolumeGrid<float>(points_to_sdf_grid_impl(positions, radii, voxel_size));
|
|
}
|
|
|
|
bke::VolumeGridData *fog_volume_grid_add_from_points(Volume *volume,
|
|
const StringRefNull name,
|
|
const Span<float3> positions,
|
|
const Span<float> radii,
|
|
const float voxel_size,
|
|
const float density)
|
|
{
|
|
openvdb::FloatGrid::Ptr new_grid = points_to_sdf_grid_impl(positions, radii, voxel_size);
|
|
new_grid->setGridClass(openvdb::GRID_FOG_VOLUME);
|
|
|
|
/* Convert the level set to a fog volume. This also sets the background value to zero. Inside the
|
|
* fog there will be a density of 1. */
|
|
openvdb::tools::sdfToFogVolume(*new_grid);
|
|
|
|
/* Take the desired density into account. */
|
|
openvdb::tools::foreach(new_grid->beginValueOn(),
|
|
[&](const openvdb::FloatGrid::ValueOnIter &iter) {
|
|
iter.modifyValue([&](float &value) { value *= density; });
|
|
});
|
|
|
|
return BKE_volume_grid_add_vdb(*volume, name, std::move(new_grid));
|
|
}
|
|
|
|
} // namespace blender::geometry
|
|
#endif
|