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:
2022-06-29 10:56:17 -05:00
committed by Hans Goudey
parent 6b508eb012
commit 1516f7dcde
15 changed files with 502 additions and 122 deletions

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);
}