From 11ceddb9dfd4f63add4941f1c4f11f2ed056ec49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Mon, 12 May 2025 13:46:40 +0200 Subject: [PATCH] New Grid Info node for reading grid transforms and background value These are generic properties of grids (not stored in voxels) which are useful to know in geometry nodes. The transform in particular defines the voxel size. Background value is used outside of active voxels. Pull Request: https://projects.blender.org/blender/blender/pulls/138592 --- .../startup/bl_ui/node_add_menu_geometry.py | 1 + .../blender/blenkernel/BKE_volume_openvdb.hh | 3 + source/blender/blenkernel/intern/volume.cc | 26 ++++ .../blender/blenkernel/intern/volume_grid.cc | 24 +--- .../blender/makesrna/intern/rna_nodetree.cc | 1 + source/blender/nodes/geometry/CMakeLists.txt | 1 + .../geometry/nodes/node_geo_grid_info.cc | 117 ++++++++++++++++++ 7 files changed, 151 insertions(+), 22 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_grid_info.cc diff --git a/scripts/startup/bl_ui/node_add_menu_geometry.py b/scripts/startup/bl_ui/node_add_menu_geometry.py index b7374cd4af6..b5f17777f82 100644 --- a/scripts/startup/bl_ui/node_add_menu_geometry.py +++ b/scripts/startup/bl_ui/node_add_menu_geometry.py @@ -796,6 +796,7 @@ class NODE_MT_geometry_node_GEO_VOLUME_READ(Menu): def draw(self, context): layout = self.layout node_add_menu.add_node_type(layout, "GeometryNodeGetNamedGrid") + node_add_menu.add_node_type(layout, "GeometryNodeGridInfo") node_add_menu.draw_assets_for_catalog(layout, "Volume/Read") diff --git a/source/blender/blenkernel/BKE_volume_openvdb.hh b/source/blender/blenkernel/BKE_volume_openvdb.hh index 490b546da72..369bb80597f 100644 --- a/source/blender/blenkernel/BKE_volume_openvdb.hh +++ b/source/blender/blenkernel/BKE_volume_openvdb.hh @@ -41,6 +41,9 @@ std::optional> BKE_volume_grid_bounds( openvdb::GridBase::ConstPtr BKE_volume_grid_shallow_transform(openvdb::GridBase::ConstPtr grid, const blender::float4x4 &transform); +blender::float4x4 BKE_volume_transform_to_blender(const openvdb::math::Transform &transform); +openvdb::math::Transform BKE_volume_transform_to_openvdb(const blender::float4x4 &transform); + template auto BKE_volume_grid_type_operation(const VolumeGridType grid_type, OpType &&op) { diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 0617ddfe3b3..41aeb34a837 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -1092,6 +1092,32 @@ openvdb::GridBase::ConstPtr BKE_volume_grid_shallow_transform(openvdb::GridBase: return grid->copyGridReplacingTransform(grid_transform); } +blender::float4x4 BKE_volume_transform_to_blender(const openvdb::math::Transform &transform) +{ + /* Perspective not supported for now, getAffineMap() will leave out the + * perspective part of the transform. */ + const openvdb::math::Mat4f matrix = transform.baseMap()->getAffineMap()->getMat4(); + /* Blender column-major and OpenVDB right-multiplication conventions match. */ + float4x4 result; + for (int col = 0; col < 4; col++) { + for (int row = 0; row < 4; row++) { + result[col][row] = matrix(col, row); + } + } + return result; +} + +openvdb::math::Transform BKE_volume_transform_to_openvdb(const blender::float4x4 &transform) +{ + openvdb::math::Mat4f matrix_openvdb; + for (int col = 0; col < 4; col++) { + for (int row = 0; row < 4; row++) { + matrix_openvdb(col, row) = transform[col][row]; + } + } + return openvdb::math::Transform(std::make_shared(matrix_openvdb)); +} + /* Changing the resolution of a grid. */ /** diff --git a/source/blender/blenkernel/intern/volume_grid.cc b/source/blender/blenkernel/intern/volume_grid.cc index 5325dcb9444..7e1d8a08f7a 100644 --- a/source/blender/blenkernel/intern/volume_grid.cc +++ b/source/blender/blenkernel/intern/volume_grid.cc @@ -436,19 +436,7 @@ int get_channels_num(const VolumeGridType type) float4x4 get_transform_matrix(const VolumeGridData &grid) { #ifdef WITH_OPENVDB - const openvdb::math::Transform &transform = grid.transform(); - - /* Perspective not supported for now, getAffineMap() will leave out the - * perspective part of the transform. */ - openvdb::math::Mat4f matrix = transform.baseMap()->getAffineMap()->getMat4(); - /* Blender column-major and OpenVDB right-multiplication conventions match. */ - float4x4 result; - for (int col = 0; col < 4; col++) { - for (int row = 0; row < 4; row++) { - result[col][row] = matrix(col, row); - } - } - return result; + return BKE_volume_transform_to_blender(grid.transform()); #else UNUSED_VARS(grid); return float4x4::identity(); @@ -458,15 +446,7 @@ float4x4 get_transform_matrix(const VolumeGridData &grid) void set_transform_matrix(VolumeGridData &grid, const float4x4 &matrix) { #ifdef WITH_OPENVDB - openvdb::math::Mat4f matrix_openvdb; - for (int col = 0; col < 4; col++) { - for (int row = 0; row < 4; row++) { - matrix_openvdb(col, row) = matrix[col][row]; - } - } - - grid.transform_for_write() = openvdb::math::Transform( - std::make_shared(matrix_openvdb)); + grid.transform_for_write() = BKE_volume_transform_to_openvdb(matrix); #else UNUSED_VARS(grid, matrix); #endif diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index bbd21b3eb8c..5d6f805f76f 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -13883,6 +13883,7 @@ static void rna_def_nodes(BlenderRNA *brna) define("GeometryNode", "GeometryNodeGizmoLinear"); define("GeometryNode", "GeometryNodeGizmoTransform", rna_def_geo_gizmo_transform); define("GeometryNode", "GeometryNodeGreasePencilToCurves"); + define("GeometryNode", "GeometryNodeGridInfo"); define("GeometryNode", "GeometryNodeGridToMesh"); define("GeometryNode", "GeometryNodeImageInfo"); define("GeometryNode", "GeometryNodeImageTexture", def_geo_image_texture); diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index 45140c6fb47..c8ed3b0b8be 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -89,6 +89,7 @@ set(SRC nodes/node_geo_gizmo_linear.cc nodes/node_geo_gizmo_transform.cc nodes/node_geo_grease_pencil_to_curves.cc + nodes/node_geo_grid_info.cc nodes/node_geo_grid_to_mesh.cc nodes/node_geo_image.cc nodes/node_geo_image_info.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_grid_info.cc b/source/blender/nodes/geometry/nodes/node_geo_grid_info.cc new file mode 100644 index 00000000000..89301ed5ccc --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_grid_info.cc @@ -0,0 +1,117 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_geometry_util.hh" + +#include "BLI_math_matrix.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_volume_grid.hh" +#include "BKE_volume_openvdb.hh" + +#include "NOD_rna_define.hh" +#include "NOD_socket.hh" + +#include "UI_interface.hh" +#include "UI_resources.hh" + +#include "RNA_enum_types.hh" + +namespace blender::nodes::node_geo_grid_info_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + const bNode *node = b.node_or_null(); + if (!node) { + return; + } + + const eNodeSocketDatatype data_type = eNodeSocketDatatype(node->custom1); + + b.add_input(data_type, "Grid").hide_value(); + + b.add_output("Transform") + .description("Transform from grid index space to object space"); + b.add_output(data_type, "Background Value").description("Default value outside of grid voxels"); +} + +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + layout->prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ +#ifdef WITH_OPENVDB + const eNodeSocketDatatype data_type = eNodeSocketDatatype(params.node().custom1); + + const auto grid = params.extract_input("Grid"); + if (!grid) { + params.set_default_remaining_outputs(); + return; + } + + bke::VolumeTreeAccessToken tree_token; + const std::shared_ptr vdb_grid = grid->grid_ptr(tree_token); + params.set_output("Transform", BKE_volume_transform_to_blender(vdb_grid->transform())); + + bke::attribute_math::convert_to_static_type( + *bke::socket_type_to_geo_nodes_base_cpp_type(data_type), [&](auto type_tag) { + using ValueT = decltype(type_tag); + using type_traits = typename bke::VolumeGridTraits; + using TreeType = typename type_traits::TreeType; + using GridType = openvdb::Grid; + + if constexpr (!std::is_same_v) { + const std::shared_ptr vdb_typed_grid = openvdb::GridBase::grid( + vdb_grid); + params.set_output("Background Value", + type_traits::to_blender(vdb_typed_grid->background())); + } + }); +#else + params.set_default_remaining_outputs(); + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenVDB")); +#endif +} + +static void node_init(bNodeTree * /*tree*/, bNode *node) +{ + node->custom1 = SOCK_FLOAT; +} + +static void node_rna(StructRNA *srna) +{ + RNA_def_node_enum(srna, + "data_type", + "Data Type", + "Type of grid data", + rna_enum_node_socket_data_type_items, + NOD_inline_enum_accessors(custom1), + SOCK_FLOAT, + grid_socket_type_items_filter_fn); +} + +static void node_register() +{ + static blender::bke::bNodeType ntype; + + geo_node_type_base(&ntype, "GeometryNodeGridInfo"); + ntype.ui_name = "Grid Info"; + ntype.ui_description = "Retrieve information about a volume grid"; + ntype.nclass = NODE_CLASS_INPUT; + ntype.initfunc = node_init; + ntype.geometry_node_execute = node_geo_exec; + ntype.draw_buttons = node_layout; + ntype.declare = node_declare; + blender::bke::node_register_type(ntype); + + node_rna(ntype.rna_ext.srna); +} +NOD_REGISTER_NODE(node_register) + +} // namespace blender::nodes::node_geo_grid_info_cc