Geometry Nodes: Add Mesh To Volume Node

This adds a Mesh To Volume Node T86838 based on the existing modifier.
The mesh to volume conversion is implemented in the geometry module,
and shared between the node and the modifier.

Currently the node outputs a grid with the name "density". This may
change in the future depending on the decisions made in T91668.

The original patch was by Kris (@Metricity), further implementation
by Geramy Loveless (@GeramyLoveless), then finished by Erik Abrahamsson
(@erik85).

Differential Revision: https://developer.blender.org/D10895
This commit is contained in:
Erik Abrahamsson 2022-06-29 10:56:17 -05:00 committed by Hans Goudey
parent 6b508eb012
commit 1516f7dcde
Notes: blender-bot 2023-02-14 07:31:34 +01:00
Referenced by issue #86838, Mesh to Volume Node
15 changed files with 502 additions and 122 deletions

View File

@ -113,6 +113,7 @@ def mesh_node_items(context):
yield NodeItem("GeometryNodeMeshBoolean")
yield NodeItem("GeometryNodeMeshToCurve")
yield NodeItem("GeometryNodeMeshToPoints")
yield NodeItem("GeometryNodeMeshToVolume")
yield NodeItem("GeometryNodeSplitEdges")
yield NodeItem("GeometryNodeSubdivideMesh")
yield NodeItem("GeometryNodeSubdivisionSurface")

View File

@ -411,6 +411,11 @@ struct GeometrySet {
*/
static GeometrySet create_with_mesh(
Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Create a new geometry set that only contains the given volume.
*/
static GeometrySet create_with_volume(
Volume *volume, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Create a new geometry set that only contains the given point cloud.
*/

View File

@ -1498,6 +1498,7 @@ struct TexResult;
#define GEO_NODE_VOLUME_CUBE 1161
#define GEO_NODE_POINTS 1162
#define GEO_NODE_FIELD_ON_DOMAIN 1163
#define GEO_NODE_MESH_TO_VOLUME 1164
/** \} */

View File

@ -322,6 +322,16 @@ GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType owne
return geometry_set;
}
GeometrySet GeometrySet::create_with_volume(Volume *volume, GeometryOwnershipType ownership)
{
GeometrySet geometry_set;
if (volume != nullptr) {
VolumeComponent &component = geometry_set.get_component_for_write<VolumeComponent>();
component.replace(volume, ownership);
}
return geometry_set;
}
GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud,
GeometryOwnershipType ownership)
{

View File

@ -4804,6 +4804,7 @@ static void registerGeometryNodes()
register_node_type_geo_mesh_subdivide();
register_node_type_geo_mesh_to_curve();
register_node_type_geo_mesh_to_points();
register_node_type_geo_mesh_to_volume();
register_node_type_geo_object_info();
register_node_type_geo_points();
register_node_type_geo_points_to_vertices();

View File

@ -19,6 +19,7 @@ set(SRC
intern/mesh_merge_by_distance.cc
intern/mesh_primitive_cuboid.cc
intern/mesh_to_curve_convert.cc
intern/mesh_to_volume.cc
intern/point_merge_by_distance.cc
intern/realize_instances.cc
intern/resample_curves.cc
@ -29,6 +30,7 @@ set(SRC
GEO_mesh_merge_by_distance.hh
GEO_mesh_primitive_cuboid.hh
GEO_mesh_to_curve.hh
GEO_mesh_to_volume.hh
GEO_point_merge_by_distance.hh
GEO_realize_instances.hh
GEO_resample_curves.hh
@ -41,6 +43,20 @@ set(LIB
bf_blenlib
)
if(WITH_OPENVDB)
list(APPEND INC
../../../intern/openvdb
)
list(APPEND INC_SYS
${OPENVDB_INCLUDE_DIRS}
)
list(APPEND LIB
bf_intern_openvdb
${OPENVDB_LIBRARIES}
)
add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS})
endif()
if(WITH_TBB)
add_definitions(-DWITH_TBB)

View File

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_float4x4.hh"
#include "BLI_string_ref.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#pragma once
struct Volume;
struct VolumeGrid;
struct Depsgraph;
/** \file
* \ingroup geo
*/
namespace blender::geometry {
struct MeshToVolumeResolution {
MeshToVolumeModifierResolutionMode mode;
union {
float voxel_size;
float voxel_amount;
} settings;
};
#ifdef WITH_OPENVDB
float volume_compute_voxel_size(const Depsgraph *depsgraph,
const float3 &bb_min,
const float3 &bb_max,
const MeshToVolumeResolution resolution,
float exterior_band_width,
const float4x4 &transform);
/**
* Add a new VolumeGrid to the Volume by converting the supplied mesh
*/
VolumeGrid *volume_grid_add_from_mesh(Volume *volume,
const StringRefNull name,
const Mesh *mesh,
const float4x4 &mesh_to_volume_space_transform,
float voxel_size,
bool fill_volume,
float exterior_band_width,
float interior_band_width,
float density);
#endif
} // namespace blender::geometry

View File

@ -0,0 +1,168 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_mesh_runtime.h"
#include "BKE_volume.h"
#include "GEO_mesh_to_volume.hh"
#ifdef WITH_OPENVDB
# include <openvdb/openvdb.h>
# include <openvdb/tools/GridTransformer.h>
# include <openvdb/tools/VolumeToMesh.h>
namespace blender::geometry {
/* This class follows the MeshDataAdapter interface from openvdb. */
class OpenVDBMeshAdapter {
private:
Span<MVert> vertices_;
Span<MLoop> loops_;
Span<MLoopTri> looptris_;
float4x4 transform_;
public:
OpenVDBMeshAdapter(const Mesh &mesh, float4x4 transform);
size_t polygonCount() const;
size_t pointCount() const;
size_t vertexCount(size_t UNUSED(polygon_index)) const;
void getIndexSpacePoint(size_t polygon_index, size_t vertex_index, openvdb::Vec3d &pos) const;
};
OpenVDBMeshAdapter::OpenVDBMeshAdapter(const Mesh &mesh, float4x4 transform)
: vertices_(mesh.mvert, mesh.totvert), loops_(mesh.mloop, mesh.totloop), transform_(transform)
{
/* This only updates a cache and can be considered to be logically const. */
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(&mesh);
const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
looptris_ = Span(looptris, looptris_len);
}
size_t OpenVDBMeshAdapter::polygonCount() const
{
return static_cast<size_t>(looptris_.size());
}
size_t OpenVDBMeshAdapter::pointCount() const
{
return static_cast<size_t>(vertices_.size());
}
size_t OpenVDBMeshAdapter::vertexCount(size_t UNUSED(polygon_index)) const
{
/* All polygons are triangles. */
return 3;
}
void OpenVDBMeshAdapter::getIndexSpacePoint(size_t polygon_index,
size_t vertex_index,
openvdb::Vec3d &pos) const
{
const MLoopTri &looptri = looptris_[polygon_index];
const MVert &vertex = vertices_[loops_[looptri.tri[vertex_index]].v];
const float3 transformed_co = transform_ * float3(vertex.co);
pos = &transformed_co.x;
}
float volume_compute_voxel_size(const Depsgraph *depsgraph,
const float3 &bb_min,
const float3 &bb_max,
const MeshToVolumeResolution res,
const float exterior_band_width,
const float4x4 &transform)
{
const float volume_simplify = BKE_volume_simplify_factor(depsgraph);
if (volume_simplify == 0.0f) {
return 0.0f;
}
if (res.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE) {
return res.settings.voxel_size / volume_simplify;
}
if (res.settings.voxel_amount <= 0) {
return 0;
}
/* Compute the voxel size based on the desired number of voxels and the approximated bounding
* box of the volume. */
const float diagonal = math::distance(transform * bb_max, transform * bb_min);
const float approximate_volume_side_length = diagonal + exterior_band_width * 2.0f;
const float voxel_size = approximate_volume_side_length / res.settings.voxel_amount /
volume_simplify;
return voxel_size;
}
static openvdb::FloatGrid::Ptr mesh_to_volume_grid(const Mesh *mesh,
const float4x4 &mesh_to_volume_space_transform,
const float voxel_size,
const bool fill_volume,
const float exterior_band_width,
const float interior_band_width,
const float density)
{
if (voxel_size == 0.0f) {
return nullptr;
}
float4x4 mesh_to_index_space_transform;
scale_m4_fl(mesh_to_index_space_transform.values, 1.0f / voxel_size);
mul_m4_m4_post(mesh_to_index_space_transform.values, mesh_to_volume_space_transform.values);
/* Better align generated grid with the source mesh. */
add_v3_fl(mesh_to_index_space_transform.values[3], -0.5f);
OpenVDBMeshAdapter mesh_adapter{*mesh, mesh_to_index_space_transform};
/* Convert the bandwidths from object in index space. */
const float exterior = MAX2(0.001f, exterior_band_width / voxel_size);
const float interior = MAX2(0.001f, interior_band_width / voxel_size);
openvdb::FloatGrid::Ptr new_grid;
if (fill_volume) {
/* Setting the interior bandwidth to FLT_MAX, will make it fill the entire volume. */
new_grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
mesh_adapter, {}, exterior, FLT_MAX);
}
else {
new_grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
mesh_adapter, {}, exterior, interior);
}
/* Give each grid cell a fixed density for now. */
openvdb::tools::foreach (
new_grid->beginValueOn(),
[density](const openvdb::FloatGrid::ValueOnIter &iter) { iter.setValue(density); });
return new_grid;
}
VolumeGrid *volume_grid_add_from_mesh(Volume *volume,
const StringRefNull name,
const Mesh *mesh,
const float4x4 &mesh_to_volume_space_transform,
const float voxel_size,
const bool fill_volume,
const float exterior_band_width,
const float interior_band_width,
const float density)
{
VolumeGrid *c_grid = BKE_volume_grid_add(volume, name.c_str(), VOLUME_GRID_FLOAT);
openvdb::FloatGrid::Ptr grid = openvdb::gridPtrCast<openvdb::FloatGrid>(
BKE_volume_grid_openvdb_for_write(volume, c_grid, false));
/* Generate grid from mesh */
openvdb::FloatGrid::Ptr mesh_grid = mesh_to_volume_grid(mesh,
mesh_to_volume_space_transform,
voxel_size,
fill_volume,
exterior_band_width,
interior_band_width,
density);
/* Merge the generated grid. Should be cheap because grid has just been created. */
grid->merge(*mesh_grid);
/* Set class to "Fog Volume". */
grid->setGridClass(openvdb::GRID_FOG_VOLUME);
/* Change transform so that the index space is correctly transformed to object space. */
grid->transform().postScale(voxel_size);
return c_grid;
}
} // namespace blender::geometry
#endif

View File

@ -1248,6 +1248,11 @@ typedef struct NodeGeometryVolumeToMesh {
uint8_t resolution_mode;
} NodeGeometryVolumeToMesh;
typedef struct NodeGeometryMeshToVolume {
/* MeshToVolumeModifierResolutionMode */
uint8_t resolution_mode;
} NodeGeometryMeshToVolume;
typedef struct NodeGeometrySubdivisionSurface {
/* eSubsurfUVSmooth. */
uint8_t uv_smooth;

View File

@ -9970,6 +9970,32 @@ static void def_geo_volume_to_mesh(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_mesh_to_volume(StructRNA *srna)
{
PropertyRNA *prop;
static EnumPropertyItem resolution_mode_items[] = {
{MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT,
"VOXEL_AMOUNT",
0,
"Amount",
"Desired number of voxels along one axis"},
{MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE,
"VOXEL_SIZE",
0,
"Size",
"Desired voxel side length"},
{0, NULL, 0, NULL, NULL},
};
RNA_def_struct_sdna_from(srna, "NodeGeometryMeshToVolume", "storage");
prop = RNA_def_property(srna, "resolution_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, resolution_mode_items);
RNA_def_property_ui_text(prop, "Resolution Mode", "How the voxel size is specified");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_mesh_circle(StructRNA *srna)
{
PropertyRNA *prop;

View File

@ -22,6 +22,8 @@
#include "DEG_depsgraph.h"
#include "GEO_mesh_to_volume.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@ -39,59 +41,6 @@
#include "RNA_access.h"
#include "RNA_prototypes.h"
#ifdef WITH_OPENVDB
# include <openvdb/openvdb.h>
# include <openvdb/tools/MeshToVolume.h>
#endif
#ifdef WITH_OPENVDB
namespace blender {
/* This class follows the MeshDataAdapter interface from openvdb. */
class OpenVDBMeshAdapter {
private:
Span<MVert> vertices_;
Span<MLoop> loops_;
Span<MLoopTri> looptris_;
float4x4 transform_;
public:
OpenVDBMeshAdapter(Mesh &mesh, float4x4 transform)
: vertices_(mesh.mvert, mesh.totvert),
loops_(mesh.mloop, mesh.totloop),
transform_(transform)
{
const MLoopTri *looptries = BKE_mesh_runtime_looptri_ensure(&mesh);
const int looptries_len = BKE_mesh_runtime_looptri_len(&mesh);
looptris_ = Span(looptries, looptries_len);
}
size_t polygonCount() const
{
return static_cast<size_t>(looptris_.size());
}
size_t pointCount() const
{
return static_cast<size_t>(vertices_.size());
}
size_t vertexCount(size_t UNUSED(polygon_index)) const
{
/* All polygons are triangles. */
return 3;
}
void getIndexSpacePoint(size_t polygon_index, size_t vertex_index, openvdb::Vec3d &pos) const
{
const MLoopTri &looptri = looptris_[polygon_index];
const MVert &vertex = vertices_[loops_[looptri.tri[vertex_index]].v];
const float3 transformed_co = transform_ * float3(vertex.co);
pos = &transformed_co.x;
}
};
} // namespace blender
#endif
static void initData(ModifierData *md)
{
MeshToVolumeModifierData *mvmd = reinterpret_cast<MeshToVolumeModifierData *>(md);
@ -163,35 +112,6 @@ static void panelRegister(ARegionType *region_type)
modifier_panel_register(region_type, eModifierType_MeshToVolume, panel_draw);
}
#ifdef WITH_OPENVDB
static float compute_voxel_size(const ModifierEvalContext *ctx,
const MeshToVolumeModifierData *mvmd,
const blender::float4x4 &transform)
{
using namespace blender;
float volume_simplify = BKE_volume_simplify_factor(ctx->depsgraph);
if (volume_simplify == 0.0f) {
return 0.0f;
}
if (mvmd->resolution_mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE) {
return mvmd->voxel_size / volume_simplify;
}
if (mvmd->voxel_amount <= 0) {
return 0;
}
/* Compute the voxel size based on the desired number of voxels and the approximated bounding box
* of the volume. */
const BoundBox *bb = BKE_object_boundbox_get(mvmd->object);
const float diagonal = math::distance(transform * float3(bb->vec[6]),
transform * float3(bb->vec[0]));
const float approximate_volume_side_length = diagonal + mvmd->exterior_band_width * 2.0f;
const float voxel_size = approximate_volume_side_length / mvmd->voxel_amount / volume_simplify;
return voxel_size;
}
#endif
static Volume *mesh_to_volume(ModifierData *md,
const ModifierEvalContext *ctx,
Volume *input_volume)
@ -213,51 +133,42 @@ static Volume *mesh_to_volume(ModifierData *md,
const float4x4 mesh_to_own_object_space_transform = float4x4(ctx->object->imat) *
float4x4(object_to_convert->obmat);
const float voxel_size = compute_voxel_size(ctx, mvmd, mesh_to_own_object_space_transform);
if (voxel_size == 0.0f) {
return input_volume;
const BoundBox *bb = BKE_object_boundbox_get(mvmd->object);
geometry::MeshToVolumeResolution resolution;
resolution.mode = (MeshToVolumeModifierResolutionMode)mvmd->resolution_mode;
if (resolution.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT) {
resolution.settings.voxel_amount = mvmd->voxel_amount;
if (resolution.settings.voxel_amount <= 0.0f) {
return input_volume;
}
}
else if (resolution.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE) {
resolution.settings.voxel_size = mvmd->voxel_size;
if (resolution.settings.voxel_size <= 0.0f) {
return input_volume;
}
}
float4x4 mesh_to_index_space_transform;
scale_m4_fl(mesh_to_index_space_transform.values, 1.0f / voxel_size);
mul_m4_m4_post(mesh_to_index_space_transform.values, mesh_to_own_object_space_transform.values);
/* Better align generated grid with the source mesh. */
add_v3_fl(mesh_to_index_space_transform.values[3], -0.5f);
const float voxel_size = geometry::volume_compute_voxel_size(ctx->depsgraph,
bb->vec[0],
bb->vec[6],
resolution,
mvmd->exterior_band_width,
mesh_to_own_object_space_transform);
OpenVDBMeshAdapter mesh_adapter{*mesh, mesh_to_index_space_transform};
/* Convert the bandwidths from object in index space. */
const float exterior_band_width = MAX2(0.001f, mvmd->exterior_band_width / voxel_size);
const float interior_band_width = MAX2(0.001f, mvmd->interior_band_width / voxel_size);
openvdb::FloatGrid::Ptr new_grid;
if (mvmd->fill_volume) {
/* Setting the interior bandwidth to FLT_MAX, will make it fill the entire volume. */
new_grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
mesh_adapter, {}, exterior_band_width, FLT_MAX);
}
else {
new_grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
mesh_adapter, {}, exterior_band_width, interior_band_width);
}
/* Create a new volume object and add the density grid. */
/* Create a new volume. */
Volume *volume = BKE_volume_new_for_eval(input_volume);
VolumeGrid *c_density_grid = BKE_volume_grid_add(volume, "density", VOLUME_GRID_FLOAT);
openvdb::FloatGrid::Ptr density_grid = openvdb::gridPtrCast<openvdb::FloatGrid>(
BKE_volume_grid_openvdb_for_write(volume, c_density_grid, false));
/* Merge the generated grid into the density grid. Should be cheap because density_grid has just
* been created as well. */
density_grid->merge(*new_grid);
/* Change transform so that the index space is correctly transformed to object space. */
density_grid->transform().postScale(voxel_size);
/* Give each grid cell a fixed density for now. */
openvdb::tools::foreach (
density_grid->beginValueOn(),
[&](const openvdb::FloatGrid::ValueOnIter &iter) { iter.setValue(mvmd->density); });
/* Convert mesh to grid and add to volume. */
geometry::volume_grid_add_from_mesh(volume,
"density",
mesh,
mesh_to_own_object_space_transform,
voxel_size,
mvmd->fill_volume,
mvmd->exterior_band_width,
mvmd->interior_band_width,
mvmd->density);
return volume;

View File

@ -102,6 +102,7 @@ void register_node_type_geo_mesh_primitive_uv_sphere(void);
void register_node_type_geo_mesh_subdivide(void);
void register_node_type_geo_mesh_to_curve(void);
void register_node_type_geo_mesh_to_points(void);
void register_node_type_geo_mesh_to_volume(void);
void register_node_type_geo_object_info(void);
void register_node_type_geo_points(void);
void register_node_type_geo_points_to_vertices(void);

View File

@ -357,6 +357,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRI
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "")
DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "")
DefNode(GeometryNode, GEO_NODE_MESH_TO_VOLUME, def_geo_mesh_to_volume, "MESH_TO_VOLUME", MeshToVolume, "Mesh To Volume", "")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
DefNode(GeometryNode, GEO_NODE_POINTS, 0, "POINTS", Points, "Points", "")
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "")

View File

@ -112,6 +112,7 @@ set(SRC
nodes/node_geo_mesh_subdivide.cc
nodes/node_geo_mesh_to_curve.cc
nodes/node_geo_mesh_to_points.cc
nodes/node_geo_mesh_to_volume.cc
nodes/node_geo_object_info.cc
nodes/node_geo_points.cc
nodes/node_geo_points_to_vertices.cc

View File

@ -0,0 +1,183 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "DEG_depsgraph_query.h"
#include "node_geometry_util.hh"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_object.h"
#include "BKE_volume.h"
#include "GEO_mesh_to_volume.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "UI_interface.h"
#include "UI_resources.h"
namespace blender::nodes::node_geo_mesh_to_volume_cc {
NODE_STORAGE_FUNCS(NodeGeometryMeshToVolume)
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.01f).max(FLT_MAX);
b.add_input<decl::Float>(N_("Voxel Size"))
.default_value(0.3f)
.min(0.01f)
.max(FLT_MAX)
.subtype(PROP_DISTANCE);
b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f).max(FLT_MAX);
b.add_input<decl::Float>(N_("Exterior Band Width"))
.default_value(0.1f)
.min(0.0f)
.max(FLT_MAX)
.subtype(PROP_DISTANCE)
.description(N_("Width of the volume outside of the mesh"));
b.add_input<decl::Float>(N_("Interior Band Width"))
.default_value(0.0f)
.min(0.0f)
.max(FLT_MAX)
.subtype(PROP_DISTANCE)
.description(N_("Width of the volume inside of the mesh"));
b.add_input<decl::Bool>(N_("Fill Volume"))
.default_value(true)
.description(N_("Initialize the density grid in every cell inside the enclosed volume"));
b.add_output<decl::Geometry>(N_("Volume"));
}
static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE);
}
static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryMeshToVolume *data = (NodeGeometryMeshToVolume *)MEM_callocN(
sizeof(NodeGeometryMeshToVolume), __func__);
data->resolution_mode = MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT;
node->storage = data;
}
static void node_update(bNodeTree *ntree, bNode *node)
{
NodeGeometryMeshToVolume *data = (NodeGeometryMeshToVolume *)node->storage;
bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
nodeSetSocketAvailability(ntree,
voxel_amount_socket,
data->resolution_mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT);
nodeSetSocketAvailability(ntree,
voxel_size_socket,
data->resolution_mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE);
}
#ifdef WITH_OPENVDB
static Volume *create_volume_from_mesh(const Mesh &mesh, GeoNodeExecParams &params)
{
const NodeGeometryMeshToVolume &storage =
*(const NodeGeometryMeshToVolume *)params.node().storage;
const float density = params.get_input<float>("Density");
const float exterior_band_width = params.get_input<float>("Exterior Band Width");
const float interior_band_width = params.get_input<float>("Interior Band Width");
const bool fill_volume = params.get_input<bool>("Fill Volume");
geometry::MeshToVolumeResolution resolution;
resolution.mode = (MeshToVolumeModifierResolutionMode)storage.resolution_mode;
if (resolution.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT) {
resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount");
if (resolution.settings.voxel_amount <= 0.0f) {
return nullptr;
}
}
else if (resolution.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE) {
resolution.settings.voxel_size = params.get_input<float>("Voxel Size");
if (resolution.settings.voxel_size <= 0.0f) {
return nullptr;
}
}
float3 min, max;
INIT_MINMAX(min, max);
if (!BKE_mesh_wrapper_minmax(&mesh, min, max)) {
min = float3(-1.0f);
max = float3(1.0f);
}
const float4x4 mesh_to_volume_space_transform = float4x4::identity();
const float voxel_size = geometry::volume_compute_voxel_size(params.depsgraph(),
min,
max,
resolution,
exterior_band_width,
mesh_to_volume_space_transform);
Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr);
BKE_volume_init_grids(volume);
/* Convert mesh to grid and add to volume. */
geometry::volume_grid_add_from_mesh(volume,
"density",
&mesh,
mesh_to_volume_space_transform,
voxel_size,
fill_volume,
exterior_band_width,
interior_band_width,
density);
return volume;
}
#endif /* WITH_OPENVDB */
static void node_geo_exec(GeoNodeExecParams params)
{
#ifdef WITH_OPENVDB
GeometrySet geometry_set(params.extract_input<GeometrySet>("Mesh"));
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (geometry_set.has_mesh()) {
Volume *volume = create_volume_from_mesh(*geometry_set.get_mesh_for_read(), params);
geometry_set.replace_volume(volume);
geometry_set.keep_only({GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES});
}
});
params.set_output("Volume", std::move(geometry_set));
#else
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
params.set_default_remaining_outputs();
return;
#endif
}
} // namespace blender::nodes::node_geo_mesh_to_volume_cc
void register_node_type_geo_mesh_to_volume()
{
namespace file_ns = blender::nodes::node_geo_mesh_to_volume_cc;
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_MESH_TO_VOLUME, "Mesh to Volume", NODE_CLASS_GEOMETRY);
ntype.declare = file_ns::node_declare;
node_type_size(&ntype, 200, 120, 700);
node_type_init(&ntype, file_ns::node_init);
node_type_update(&ntype, file_ns::node_update);
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.draw_buttons = file_ns::node_layout;
node_type_storage(
&ntype, "NodeGeometryMeshToVolume", node_free_standard_storage, node_copy_standard_storage);
nodeRegisterType(&ntype);
}