Files
test/source/blender/geometry/intern/points_to_volume.cc
Lukas Tönne ce8f30f92c Fix #138447: Invalid voxel size due to arbitrary threshold
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
2025-05-06 16:08:24 +02:00

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