Geometry Nodes: Update bounding box to work on individual instances

This commit makes the bounding box node work on each unique geometry
(including instances) individually instead of making one large bounding
box for everything. This makes the node much faster, and is often the
desired result anyway. For the old behavior, a realize instances node
can be used in front of this node (versioning adds it automatically).

The min and max outputs now only output the values from the realized
geometry.

Differential Revision: https://developer.blender.org/D12951
This commit is contained in:
Hans Goudey 2021-10-21 11:51:56 -05:00
parent deb7ec312a
commit 7b9319adf9
Notes: blender-bot 2023-02-13 17:26:34 +01:00
Referenced by issue #92157, Update bounding box node to work with instances
3 changed files with 50 additions and 119 deletions

View File

@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 36
#define BLENDER_FILE_SUBVERSION 37
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file

View File

@ -754,6 +754,19 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 37)) {
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type == NTREE_GEOMETRY) {
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_BOUNDING_BOX) {
bNodeSocket *geometry_socket = node->inputs.first;
add_realize_instances_before_socket(ntree, node, geometry_socket);
}
}
}
}
}
/**
* Versioning code until next subversion bump goes here.
*

View File

@ -14,9 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BKE_spline.hh"
#include "BKE_volume.h"
#include "node_geometry_util.hh"
namespace blender::nodes {
@ -29,135 +26,56 @@ static void geo_node_bounding_box_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>("Max");
}
using bke::GeometryInstanceGroup;
static void compute_min_max_from_position_and_transform(const GeometryComponent &component,
Span<float4x4> transforms,
float3 &r_min,
float3 &r_max)
{
GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
for (const float4x4 &transform : transforms) {
for (const int i : positions.index_range()) {
const float3 position = positions[i];
const float3 transformed_position = transform * position;
minmax_v3v3_v3(r_min, r_max, transformed_position);
}
}
}
static void compute_min_max_from_volume_and_transforms(const VolumeComponent &volume_component,
Span<float4x4> transforms,
float3 &r_min,
float3 &r_max)
{
#ifdef WITH_OPENVDB
const Volume *volume = volume_component.get_for_read();
if (volume == nullptr) {
return;
}
for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i);
openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
for (const float4x4 &transform : transforms) {
openvdb::GridBase::ConstPtr instance_grid = BKE_volume_grid_shallow_transform(grid,
transform);
float3 grid_min = float3(FLT_MAX);
float3 grid_max = float3(-FLT_MAX);
if (BKE_volume_grid_bounds(instance_grid, grid_min, grid_max)) {
DO_MIN(grid_min, r_min);
DO_MAX(grid_max, r_max);
}
}
}
#else
UNUSED_VARS(volume_component, transforms, r_min, r_max);
#endif
}
static void compute_min_max_from_curve_and_transforms(const CurveComponent &curve_component,
Span<float4x4> transforms,
float3 &r_min,
float3 &r_max)
{
const CurveEval *curve = curve_component.get_for_read();
if (curve == nullptr) {
return;
}
for (const SplinePtr &spline : curve->splines()) {
Span<float3> positions = spline->evaluated_positions();
for (const float4x4 &transform : transforms) {
for (const int i : positions.index_range()) {
const float3 position = positions[i];
const float3 transformed_position = transform * position;
minmax_v3v3_v3(r_min, r_max, transformed_position);
}
}
}
}
static void compute_geometry_set_instances_boundbox(const GeometrySet &geometry_set,
float3 &r_min,
float3 &r_max)
{
Vector<GeometryInstanceGroup> set_groups;
bke::geometry_set_gather_instances(geometry_set, set_groups);
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
Span<float4x4> transforms = set_group.transforms;
if (set.has<PointCloudComponent>()) {
compute_min_max_from_position_and_transform(
*set.get_component_for_read<PointCloudComponent>(), transforms, r_min, r_max);
}
if (set.has<MeshComponent>()) {
compute_min_max_from_position_and_transform(
*set.get_component_for_read<MeshComponent>(), transforms, r_min, r_max);
}
if (set.has<VolumeComponent>()) {
compute_min_max_from_volume_and_transforms(
*set.get_component_for_read<VolumeComponent>(), transforms, r_min, r_max);
}
if (set.has<CurveComponent>()) {
compute_min_max_from_curve_and_transforms(
*set.get_component_for_read<CurveComponent>(), transforms, r_min, r_max);
}
}
}
static void geo_node_bounding_box_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
/* Compute the min and max of all realized geometry for the two
* vector outputs, which are only meant to consider real geometry. */
float3 min = float3(FLT_MAX);
float3 max = float3(-FLT_MAX);
if (geometry_set.has_instances()) {
compute_geometry_set_instances_boundbox(geometry_set, min, max);
}
else {
geometry_set.compute_boundbox_without_instances(&min, &max);
}
geometry_set.compute_boundbox_without_instances(&min, &max);
if (min == float3(FLT_MAX)) {
params.set_output("Bounding Box", GeometrySet());
params.set_output("Min", float3(0));
params.set_output("Max", float3(0));
}
else {
const float3 scale = max - min;
const float3 center = min + scale / 2.0f;
Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2);
transform_mesh(*mesh, center, float3(0), float3(1));
params.set_output("Bounding Box", GeometrySet::create_with_mesh(mesh));
params.set_output("Min", min);
params.set_output("Max", max);
}
/* Generate the bounding box meshes inside each unique geometry set (including individually for
* every instance). Because geometry components are reference counted anyway, we can just
* repurpose the original geometry sets for the output. */
if (params.output_is_required("Bounding Box")) {
geometry_set.modify_geometry_sets([&](GeometrySet &sub_geometry) {
float3 sub_min = float3(FLT_MAX);
float3 sub_max = float3(-FLT_MAX);
/* Reuse the min and max calculation if this is the main "real" geometry set. */
if (&sub_geometry == &geometry_set) {
sub_min = min;
sub_max = max;
}
else {
sub_geometry.compute_boundbox_without_instances(&sub_min, &sub_max);
}
if (sub_min == float3(FLT_MAX)) {
sub_geometry.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
}
else {
const float3 scale = sub_max - sub_min;
const float3 center = sub_min + scale / 2.0f;
Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2);
transform_mesh(*mesh, center, float3(0), float3(1));
sub_geometry.replace_mesh(mesh);
sub_geometry.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
}
});
params.set_output("Bounding Box", std::move(geometry_set));
}
}
} // namespace blender::nodes