Geometry Nodes: SDF Volume nodes milestone 1 Adds initial support for SDF volume creation and manipulation. `SDF volume` is Blender's name of an OpenVDB grid of type Level Set. See the discussion about naming in #91668. The new nodes are: - Mesh to SDF Volume: Converts a mesh to an SDF Volume - Points to SDF Volume: Converts points to an SDF Volume - Mean Filter SDF Volume: Applies a Mean Filter to an SDF - Offset SDF Volume: Applies an offset to an SDF - SDF Volume Sphere: Creates an SDF Volume in the shape of a sphere For now an experimental option `New Volume Nodes` needs to be enabled in Blender preferences for the nodes to be visible. See the current work plan for Volume Nodes in #103248. Pull Request: blender/blender#105090
97 lines
3.2 KiB
C++
97 lines
3.2 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "BLI_math_matrix.hh"
|
|
|
|
#include "BKE_volume.h"
|
|
|
|
#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. */
|
|
struct OpenVDBParticleList {
|
|
using PosType = openvdb::Vec3R;
|
|
|
|
Span<float3> positions;
|
|
Span<float> radii;
|
|
|
|
size_t size() const
|
|
{
|
|
return size_t(positions.size());
|
|
}
|
|
|
|
void getPos(size_t n, openvdb::Vec3R &xyz) const
|
|
{
|
|
xyz = &positions[n].x;
|
|
}
|
|
|
|
void getPosRad(size_t n, openvdb::Vec3R &xyz, openvdb::Real &radius) const
|
|
{
|
|
xyz = &positions[n].x;
|
|
radius = radii[n];
|
|
}
|
|
};
|
|
|
|
static openvdb::FloatGrid::Ptr points_to_sdf_grid(const Span<float3> positions,
|
|
const Span<float> radii)
|
|
{
|
|
/* 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(FLT_MAX);
|
|
OpenVDBParticleList particles{positions, radii};
|
|
op.rasterizeSpheres(particles);
|
|
op.finalize();
|
|
|
|
return new_grid;
|
|
}
|
|
|
|
VolumeGrid *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(positions, radii);
|
|
new_grid->transform().postScale(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));
|
|
}
|
|
|
|
VolumeGrid *sdf_volume_grid_add_from_points(Volume *volume,
|
|
const StringRefNull name,
|
|
const Span<float3> positions,
|
|
const Span<float> radii,
|
|
const float voxel_size)
|
|
{
|
|
openvdb::FloatGrid::Ptr new_grid = points_to_sdf_grid(positions, radii);
|
|
new_grid->transform().postScale(voxel_size);
|
|
new_grid->setGridClass(openvdb::GRID_LEVEL_SET);
|
|
|
|
return BKE_volume_grid_add_vdb(*volume, name, std::move(new_grid));
|
|
}
|
|
} // namespace blender::geometry
|
|
#endif
|