Geometry Nodes: Add selection and depth options to realize instances #104832

Closed
Angus Stanton wants to merge 15 commits from ab_stanton/blender:add-selection-realize-instances into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
7 changed files with 421 additions and 185 deletions

View File

@ -18,6 +18,7 @@ set(SRC
intern/add_curves_on_mesh.cc
intern/curve_constraints.cc
intern/fillet_curves.cc
intern/join_geometry.cc
intern/mesh_merge_by_distance.cc
intern/mesh_primitive_cuboid.cc
intern/mesh_split_edges.cc
@ -35,6 +36,7 @@ set(SRC
GEO_add_curves_on_mesh.hh
GEO_curve_constraints.hh
GEO_fillet_curves.hh
GEO_join_geometry.hh
GEO_mesh_merge_by_distance.hh
GEO_mesh_primitive_cuboid.hh
GEO_mesh_split_edges.hh

View File

@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_math_matrix_types.hh"
#include "BKE_geometry_set.hh"
namespace blender::geometry {
/**
* Join all src_components and add the resulting component to result.
*/
template<typename Component>
void join_component_type(Span<const Component *> src_components,
GeometrySet &result,
const bke::AnonymousAttributePropagationInfo &propagation_info);
/**
* Join InstancesComponents and apply corresponding transforms for each.
*/
void join_transform_instance_components(Span<const InstancesComponent *> src_components,
Span<blender::float4x4> src_base_transforms,
GeometrySet &result);
} // namespace blender::geometry

View File

@ -20,6 +20,16 @@ struct RealizeInstancesOptions {
*/
bool realize_instance_attributes = true;
/**
* Selection of top-level instances to realize.
*/
IndexMask selection;
/**
* Depth of realize instances for each selected top-level instance.
*/
VArray<int> depths;
bke::AnonymousAttributePropagationInfo propagation_info;
};

View File

@ -0,0 +1,223 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "GEO_join_geometry.hh"
#include "GEO_realize_instances.hh"
#include "BKE_attribute.hh"
#include "BKE_instances.hh"
namespace blender::geometry {
using bke::AttributeIDRef;
using bke::AttributeMetaData;
using bke::GSpanAttributeWriter;
template<typename Component>
static Array<const GeometryComponent *> to_base_components(Span<const Component *> components)
{
return components;
}
static Map<AttributeIDRef, AttributeMetaData> get_final_attribute_info(
Span<const GeometryComponent *> components, Span<StringRef> ignored_attributes)
{
Map<AttributeIDRef, AttributeMetaData> info;
for (const GeometryComponent *component : components) {
component->attributes()->for_all(
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (ignored_attributes.contains(attribute_id.name())) {
return true;
}
if (meta_data.data_type == CD_PROP_STRING) {
return true;
}
info.add_or_modify(
attribute_id,
[&](AttributeMetaData *meta_data_final) { *meta_data_final = meta_data; },
[&](AttributeMetaData *meta_data_final) {
meta_data_final->data_type = blender::bke::attribute_data_type_highest_complexity(
{meta_data_final->data_type, meta_data.data_type});
meta_data_final->domain = blender::bke::attribute_domain_highest_priority(
{meta_data_final->domain, meta_data.domain});
});
return true;
});
}
return info;
}
static void fill_new_attribute(Span<const GeometryComponent *> src_components,
const AttributeIDRef &attribute_id,
const eCustomDataType data_type,
const eAttrDomain domain,
GMutableSpan dst_span)
{
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
int offset = 0;
for (const GeometryComponent *component : src_components) {
const int domain_num = component->attribute_domain_size(domain);
if (domain_num == 0) {
continue;
}
GVArray read_attribute = component->attributes()->lookup_or_default(
attribute_id, domain, data_type, nullptr);
GVArraySpan src_span{read_attribute};
const void *src_buffer = src_span.data();
void *dst_buffer = dst_span[offset];
cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_num);
offset += domain_num;
}
}
static void join_attributes(Span<const GeometryComponent *> src_components,
GeometryComponent &result,
Span<StringRef> ignored_attributes = {})
{
const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(src_components,
ignored_attributes);
for (const Map<AttributeIDRef, AttributeMetaData>::Item item : info.items()) {
const AttributeIDRef attribute_id = item.key;
const AttributeMetaData &meta_data = item.value;
GSpanAttributeWriter write_attribute =
result.attributes_for_write()->lookup_or_add_for_write_only_span(
attribute_id, meta_data.domain, meta_data.data_type);
if (!write_attribute) {
continue;
}
fill_new_attribute(
src_components, attribute_id, meta_data.data_type, meta_data.domain, write_attribute.span);
write_attribute.finish();
}
}
static void join_components(Span<const InstancesComponent *> src_components,
const VArray<blender::float4x4> &src_base_transforms,
GeometrySet &result)
{
std::unique_ptr<bke::Instances> dst_instances = std::make_unique<bke::Instances>();
int tot_instances = 0;
for (const InstancesComponent *src_component : src_components) {
tot_instances += src_component->get_for_read()->instances_num();
}
dst_instances->reserve(tot_instances);
for (const int i : src_components.index_range()) {
const InstancesComponent *src_component = src_components[i];
const blender::float4x4 &base_transform = src_base_transforms[i];
const bke::Instances &src_instances = *src_component->get_for_read();
Span<bke::InstanceReference> src_references = src_instances.references();
Array<int> handle_map(src_references.size());
for (const int src_handle : src_references.index_range()) {
handle_map[src_handle] = dst_instances->add_reference(src_references[src_handle]);
}
Span<float4x4> src_transforms = src_instances.transforms();
Span<int> src_reference_handles = src_instances.reference_handles();
for (const int i : src_transforms.index_range()) {
const int src_handle = src_reference_handles[i];
const int dst_handle = handle_map[src_handle];
const float4x4 &transform = base_transform * src_transforms[i];
dst_instances->add_instance(dst_handle, transform);
}
}
result.replace_instances(dst_instances.release());
InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>();
join_attributes(to_base_components(src_components), dst_component, {"position"});
}
static void join_components(Span<const VolumeComponent *> /*src_components*/,
VArray<blender::float4x4> /*src_base_transform*/,
GeometrySet & /*result*/)
{
/* Not yet supported. Joining volume grids with the same name requires resampling of at least one
* of the grids. The cell size of the resulting volume has to be determined somehow. */
}
template<typename Component>
void join_component_type(Span<const Component *> src_components,
GeometrySet &result,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
if (src_components.size() == 0) {
return;
}
if (src_components.size() == 1) {
result.add(*src_components[0]);
return;
}
if constexpr (is_same_any_v<Component, InstancesComponent, VolumeComponent>) {
join_components(
src_components,
VArray<blender::float4x4>::ForSingle(blender::float4x4::identity(), src_components.size()),
result);
}
else {
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
for (const Component *component : src_components) {
GeometrySet tmp_geo;
tmp_geo.add(*component);
const int handle = instances->add_reference(bke::InstanceReference{tmp_geo});
instances->add_instance(handle, float4x4::identity());
}
GeometrySet joined_components = geometry::realize_instances(
GeometrySet::create_with_instances(instances.release()),
{true,
false,
IndexMask(instances->instances_num()),
VArray<int>::ForSingle(-1, instances->instances_num()),
propagation_info});
result.add(joined_components.get_component_for_write<Component>());
}
}
void join_transform_instance_components(Span<const InstancesComponent *> src_components,
Span<blender::float4x4> src_base_transforms,
GeometrySet &result)
{
BLI_assert(src_components.size() == src_base_transforms.size());
if (src_components.size() == 0) {
return;
}
join_components(src_components, VArray<blender::float4x4>::ForSpan(src_base_transforms), result);
}
/** Explicit template instantiation for all GeometryComponent subclasses. */
template void join_component_type<MeshComponent>(Span<const MeshComponent *>,
GeometrySet &,
const bke::AnonymousAttributePropagationInfo &);
template void join_component_type<PointCloudComponent>(
Span<const PointCloudComponent *>,
GeometrySet &,
const bke::AnonymousAttributePropagationInfo &);
template void join_component_type<InstancesComponent>(
Span<const InstancesComponent *>,
GeometrySet &,
const bke::AnonymousAttributePropagationInfo &);
template void join_component_type<VolumeComponent>(Span<const VolumeComponent *>,
GeometrySet &,
const bke::AnonymousAttributePropagationInfo &);
template void join_component_type<CurveComponent>(Span<const CurveComponent *>,
GeometrySet &,
const bke::AnonymousAttributePropagationInfo &);
template void join_component_type<GeometryComponentEditData>(
Span<const GeometryComponentEditData *>,
GeometrySet &,
const bke::AnonymousAttributePropagationInfo &);
} // namespace blender::geometry

View File

@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "GEO_join_geometry.hh"
#include "GEO_realize_instances.hh"
#include "DNA_collection_types.h"
@ -236,6 +237,12 @@ struct GatherTasksInfo {
const AllCurvesInfo &curves;
bool create_id_attribute_on_any_component = false;
/** Selection for top-level instances to realize. */
IndexMask selection;
/** Depth to realize instances for each selected top-level instance. */
const VArray<int> &depths;
/**
* Under some circumstances, temporary arrays need to be allocated during the gather operation.
* For example, when an instance attribute has to be realized as a different data type. This
@ -244,6 +251,11 @@ struct GatherTasksInfo {
*/
Vector<std::unique_ptr<GArray<>>> &r_temporary_arrays;
/** Instance components to merge for output geometry. */
Vector<const InstancesComponent *> instances_components_to_merge;
/** Base transform for each instance component. */
Vector<float4x4> instances_components_transforms;
/** All gathered tasks. */
GatherTasks r_tasks;
/** Current offsets while gathering tasks. */
@ -364,6 +376,8 @@ static void create_result_ids(const RealizeInstancesOptions &options,
/* Forward declaration. */
static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
const int current_depth,
const int target_depth,
const GeometrySet &geometry_set,
const float4x4 &base_transform,
const InstanceContext &base_instance_context);
@ -456,6 +470,8 @@ static void foreach_geometry_in_reference(
}
static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info,
const int current_depth,
const int target_depth,
const Instances &instances,
const float4x4 &base_transform,
const InstanceContext &base_instance_context)
@ -481,7 +497,16 @@ static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info,
Vector<std::pair<int, GSpan>> curve_attributes_to_override = prepare_attribute_fallbacks(
gather_info, instances, gather_info.curves.attributes);
for (const int i : transforms.index_range()) {
/* If at top level, get instance indices from selection field, else use all instances. */
const IndexMask indices = current_depth == 0 ? gather_info.selection :
IndexMask(IndexRange(instances.instances_num()));
for (const int mask_index : indices.index_range()) {
const int i = indices[mask_index];
/* If at top level, retrieve depth from gather_info, else continue with target_depth. */
const int depth_target = current_depth == 0 ? gather_info.depths[mask_index] : target_depth;
const int handle = handles[i];
const float4x4 &transform = transforms[i];
const InstanceReference &reference = references[handle];
@ -518,6 +543,8 @@ static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info,
const uint32_t id) {
instance_context.id = id;
gather_realize_tasks_recursive(gather_info,
current_depth + 1,
depth_target,
instance_geometry_set,
transform,
instance_context);
@ -529,6 +556,8 @@ static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info,
* Gather tasks for all geometries in the #geometry_set.
*/
static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
const int current_depth,
const int target_depth,
const GeometrySet &geometry_set,
const float4x4 &base_transform,
const InstanceContext &base_instance_context)
@ -588,12 +617,25 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
break;
}
case GEO_COMPONENT_TYPE_INSTANCES: {
const InstancesComponent &instances_component = *static_cast<const InstancesComponent *>(
component);
const Instances *instances = instances_component.get_for_read();
if (instances != nullptr && instances->instances_num() > 0) {
gather_realize_tasks_for_instances(
gather_info, *instances, base_transform, base_instance_context);
if (current_depth == target_depth) {
const InstancesComponent *instances_component = static_cast<const InstancesComponent *>(
component);
gather_info.instances_components_to_merge.append(instances_component);
gather_info.instances_components_transforms.append(base_transform);
}
else {
const InstancesComponent &instances_component = *static_cast<const InstancesComponent *>(
component);
const Instances *instances = instances_component.get_for_read();
if (instances != nullptr && instances->instances_num() > 0) {
gather_realize_tasks_for_instances(gather_info,
current_depth,
target_depth,
*instances,
base_transform,
base_instance_context);
}
}
break;
}
@ -1452,6 +1494,32 @@ static void remove_id_attribute_from_instances(GeometrySet &geometry_set)
});
}
/** Propagate instances from the old geometry set to the new geometry set if they are not realized.
*/
static void propagate_instances_to_keep(
const GeometrySet &geometry_set,
IndexMask selection,
GeometrySet &new_geometry_set,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const Instances &instances = *geometry_set.get_instances_for_read();
ab_stanton marked this conversation as resolved
Review

Add a null check or use a reference here with *

Add a null check or use a reference here with `*`
Vector<int64_t> inverse_selection_indices;
const IndexMask inverse_selection = selection.invert(IndexRange(instances.instances_num()),
inverse_selection_indices);
/* Check not all instances are being realized. */
if (inverse_selection.is_empty()) {
return;
}
InstancesComponent &new_instances_components =
new_geometry_set.get_component_for_write<InstancesComponent>();
std::unique_ptr<Instances> new_instances = std::make_unique<Instances>(instances);
new_instances->remove(inverse_selection, propagation_info);
new_instances_components.replace(new_instances.release(), GeometryOwnershipType::Owned);
}
GeometrySet realize_instances(GeometrySet geometry_set, const RealizeInstancesOptions &options)
{
/* The algorithm works in three steps:
@ -1465,6 +1533,10 @@ GeometrySet realize_instances(GeometrySet geometry_set, const RealizeInstancesOp
return geometry_set;
}
GeometrySet temp_geometry_set;
propagate_instances_to_keep(
geometry_set, options.selection, temp_geometry_set, options.propagation_info);
if (options.keep_original_ids) {
remove_id_attribute_from_instances(geometry_set);
}
@ -1481,12 +1553,26 @@ GeometrySet realize_instances(GeometrySet geometry_set, const RealizeInstancesOp
all_meshes_info,
all_curves_info,
create_id_attribute,
options.selection,
options.depths,
temporary_arrays};
const float4x4 transform = float4x4::identity();
InstanceContext attribute_fallbacks(gather_info);
gather_realize_tasks_recursive(gather_info, geometry_set, transform, attribute_fallbacks);
GeometrySet new_geometry_set;
const float4x4 transform = float4x4::identity();
InstanceContext attribute_fallbacks(gather_info);
if (temp_geometry_set.has_instances()) {
gather_info.instances_components_to_merge.append(
&temp_geometry_set.get_component_for_write<InstancesComponent>());
gather_info.instances_components_transforms.append(float4x4::identity());
}
gather_realize_tasks_recursive(gather_info, 0, -1, geometry_set, transform, attribute_fallbacks);
geometry::join_transform_instance_components(gather_info.instances_components_to_merge,
gather_info.instances_components_transforms,
new_geometry_set);
execute_realize_pointcloud_tasks(options,
all_pointclouds_info,
gather_info.r_tasks.pointcloud_tasks,

View File

@ -1,8 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
ab_stanton marked this conversation as resolved
Review

Missing newline in between license and includes

Missing newline in between license and includes
#include "GEO_realize_instances.hh"
#include "BKE_instances.hh"
#include "GEO_join_geometry.hh"
#include "node_geometry_util.hh"
@ -15,137 +13,8 @@ static void node_declare(NodeDeclarationBuilder &b)
}
template<typename Component>
static Array<const GeometryComponent *> to_base_components(Span<const Component *> components)
{
return components;
}
static Map<AttributeIDRef, AttributeMetaData> get_final_attribute_info(
Span<const GeometryComponent *> components, Span<StringRef> ignored_attributes)
{
Map<AttributeIDRef, AttributeMetaData> info;
for (const GeometryComponent *component : components) {
component->attributes()->for_all(
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (ignored_attributes.contains(attribute_id.name())) {
return true;
}
if (meta_data.data_type == CD_PROP_STRING) {
return true;
}
info.add_or_modify(
attribute_id,
[&](AttributeMetaData *meta_data_final) { *meta_data_final = meta_data; },
[&](AttributeMetaData *meta_data_final) {
meta_data_final->data_type = blender::bke::attribute_data_type_highest_complexity(
{meta_data_final->data_type, meta_data.data_type});
meta_data_final->domain = blender::bke::attribute_domain_highest_priority(
{meta_data_final->domain, meta_data.domain});
});
return true;
});
}
return info;
}
static void fill_new_attribute(Span<const GeometryComponent *> src_components,
const AttributeIDRef &attribute_id,
const eCustomDataType data_type,
const eAttrDomain domain,
GMutableSpan dst_span)
{
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
int offset = 0;
for (const GeometryComponent *component : src_components) {
const int domain_num = component->attribute_domain_size(domain);
if (domain_num == 0) {
continue;
}
GVArray read_attribute = component->attributes()->lookup_or_default(
attribute_id, domain, data_type, nullptr);
GVArraySpan src_span{read_attribute};
const void *src_buffer = src_span.data();
void *dst_buffer = dst_span[offset];
cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_num);
offset += domain_num;
}
}
static void join_attributes(Span<const GeometryComponent *> src_components,
GeometryComponent &result,
Span<StringRef> ignored_attributes = {})
{
const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(src_components,
ignored_attributes);
for (const Map<AttributeIDRef, AttributeMetaData>::Item item : info.items()) {
const AttributeIDRef attribute_id = item.key;
const AttributeMetaData &meta_data = item.value;
GSpanAttributeWriter write_attribute =
result.attributes_for_write()->lookup_or_add_for_write_only_span(
attribute_id, meta_data.domain, meta_data.data_type);
if (!write_attribute) {
continue;
}
fill_new_attribute(
src_components, attribute_id, meta_data.data_type, meta_data.domain, write_attribute.span);
write_attribute.finish();
}
}
static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result)
{
std::unique_ptr<bke::Instances> dst_instances = std::make_unique<bke::Instances>();
int tot_instances = 0;
for (const InstancesComponent *src_component : src_components) {
tot_instances += src_component->get_for_read()->instances_num();
}
dst_instances->reserve(tot_instances);
for (const InstancesComponent *src_component : src_components) {
const bke::Instances &src_instances = *src_component->get_for_read();
Span<bke::InstanceReference> src_references = src_instances.references();
Array<int> handle_map(src_references.size());
for (const int src_handle : src_references.index_range()) {
handle_map[src_handle] = dst_instances->add_reference(src_references[src_handle]);
}
Span<float4x4> src_transforms = src_instances.transforms();
Span<int> src_reference_handles = src_instances.reference_handles();
for (const int i : src_transforms.index_range()) {
const int src_handle = src_reference_handles[i];
const int dst_handle = handle_map[src_handle];
const float4x4 &transform = src_transforms[i];
dst_instances->add_instance(dst_handle, transform);
}
}
result.replace_instances(dst_instances.release());
InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>();
join_attributes(to_base_components(src_components), dst_component, {"position"});
}
static void join_components(Span<const VolumeComponent *> /*src_components*/,
GeometrySet & /*result*/)
{
/* Not yet supported. Joining volume grids with the same name requires resampling of at least one
* of the grids. The cell size of the resulting volume has to be determined somehow. */
}
template<typename Component>
static void join_component_type(Span<GeometrySet> src_geometry_sets,
GeometrySet &result,
const AnonymousAttributePropagationInfo &propagation_info)
Vector<const Component *> get_components_from_geometry_sets(
const Span<GeometrySet> src_geometry_sets)
{
Vector<const Component *> components;
for (const GeometrySet &geometry_set : src_geometry_sets) {
@ -154,35 +23,7 @@ static void join_component_type(Span<GeometrySet> src_geometry_sets,
components.append(component);
}
}
if (components.size() == 0) {
return;
}
if (components.size() == 1) {
result.add(*components[0]);
return;
}
if constexpr (is_same_any_v<Component, InstancesComponent, VolumeComponent>) {
join_components(components, result);
}
else {
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
for (const Component *component : components) {
GeometrySet tmp_geo;
tmp_geo.add(*component);
const int handle = instances->add_reference(bke::InstanceReference{tmp_geo});
instances->add_instance(handle, float4x4::identity());
}
geometry::RealizeInstancesOptions options;
options.keep_original_ids = true;
options.realize_instance_attributes = false;
options.propagation_info = propagation_info;
GeometrySet joined_components = geometry::realize_instances(
GeometrySet::create_with_instances(instances.release()), options);
result.add(joined_components.get_component_for_write<Component>());
}
return components;
}
static void node_geo_exec(GeoNodeExecParams params)
@ -197,13 +38,30 @@ static void node_geo_exec(GeoNodeExecParams params)
}
GeometrySet geometry_set_result;
join_component_type<MeshComponent>(geometry_sets, geometry_set_result, propagation_info);
join_component_type<PointCloudComponent>(geometry_sets, geometry_set_result, propagation_info);
join_component_type<InstancesComponent>(geometry_sets, geometry_set_result, propagation_info);
join_component_type<VolumeComponent>(geometry_sets, geometry_set_result, propagation_info);
join_component_type<CurveComponent>(geometry_sets, geometry_set_result, propagation_info);
join_component_type<GeometryComponentEditData>(
geometry_sets, geometry_set_result, propagation_info);
geometry::join_component_type<MeshComponent>(
get_components_from_geometry_sets<MeshComponent>(geometry_sets),
geometry_set_result,
propagation_info);
geometry::join_component_type<PointCloudComponent>(
get_components_from_geometry_sets<PointCloudComponent>(geometry_sets),
geometry_set_result,
propagation_info);
geometry::join_component_type<InstancesComponent>(
get_components_from_geometry_sets<InstancesComponent>(geometry_sets),
geometry_set_result,
propagation_info);
geometry::join_component_type<VolumeComponent>(
get_components_from_geometry_sets<VolumeComponent>(geometry_sets),
geometry_set_result,
propagation_info);
geometry::join_component_type<CurveComponent>(
get_components_from_geometry_sets<CurveComponent>(geometry_sets),
geometry_set_result,
propagation_info);
geometry::join_component_type<GeometryComponentEditData>(
get_components_from_geometry_sets<GeometryComponentEditData>(geometry_sets),
geometry_set_result,
propagation_info);
params.set_output("Geometry", std::move(geometry_set_result));
}

View File

@ -2,6 +2,8 @@
#include "node_geometry_util.hh"
#include "BKE_instances.hh"
#include "GEO_realize_instances.hh"
#include "UI_interface.h"
@ -12,6 +14,16 @@ namespace blender::nodes::node_geo_realize_instances_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Geometry"));
b.add_input<decl::Bool>(N_("Selection"))
ab_stanton marked this conversation as resolved
Review

Suggest a description like Which top-level instances to realize

Suggest a description like `Which top-level instances to realize`
.default_value(true)
HooglyBoogly marked this conversation as resolved
Review

Maybe How many levels of nested instances to realize at each top-level instance. Beyond the depth, further instances won't be realized

Though that's probably a bit too long.

Maybe `How many levels of nested instances to realize at each top-level instance. Beyond the depth, further instances won't be realized` Though that's probably a bit too long.
Review

Just removed the last sentence, seems intuitive enough to me to leave it out

Just removed the last sentence, seems intuitive enough to me to leave it out
.hide_value()
.supports_field()
.description(N_("Which top-level instances to realize"));
b.add_input<decl::Int>(N_("Depth"))
.default_value(0)
.supports_field()
.description(
N_("Number of levels of nested instances to realize for each top-level instance"));
b.add_output<decl::Geometry>(N_("Geometry")).propagate_all();
}
@ -34,12 +46,31 @@ static void node_geo_exec(GeoNodeExecParams params)
}
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
if (!geometry_set.has_instances()) {
params.set_output("Geometry", std::move(geometry_set));
return;
ab_stanton marked this conversation as resolved
Review

The whitespace here doesn't really help IMO. I'd suggest putting all of these in one block:

  const bke::InstancesFieldContext field_context{instances};
  fn::FieldEvaluator evaluator{field_context, instances.instances_num()};
  evaluator.set_selection(params.extract_input<Field<bool>>("Selection"));
  evaluator.add(params.extract_input<Field<int>>("Depth"));
  evaluator.evaluate();
  const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
  const VArray<int> depths = evaluator.get_evaluated<int>(0);

This is subjective, but generally blank lines are only helpful when used a bit more sparingly, to separate the code into meaningful sections. Just like paragraphs of prose I guess.

The whitespace here doesn't really help IMO. I'd suggest putting all of these in one block: ``` const bke::InstancesFieldContext field_context{instances}; fn::FieldEvaluator evaluator{field_context, instances.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<int>>("Depth")); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); const VArray<int> depths = evaluator.get_evaluated<int>(0); ``` This is subjective, but generally blank lines are only helpful when used a bit more sparingly, to separate the code into meaningful sections. Just like paragraphs of prose I guess.
}
GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
geometry::RealizeInstancesOptions options;
options.keep_original_ids = legacy_behavior;
options.realize_instance_attributes = !legacy_behavior;
options.propagation_info = params.get_output_propagation_info("Geometry");
geometry_set = geometry::realize_instances(geometry_set, options);
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
Field<int> depth_field = params.extract_input<Field<int>>("Depth");
const bke::Instances &instances = *geometry_set.get_instances_for_read();
const bke::InstancesFieldContext field_context{instances};
fn::FieldEvaluator evaluator{field_context, instances.instances_num()};
evaluator.set_selection(selection_field);
evaluator.add(depth_field);
evaluator.evaluate();
const VArray<int> depths = evaluator.get_evaluated<int>(0);
Review

It looks like later you expect there to be a depth only per selected instance, but here you get an array that contains a depth for every instance (not only selected ones, although the unselected instances might be uninitialized).

It looks like later you expect there to be a depth only per selected instance, but here you get an array that contains a depth for every instance (not only selected ones, although the unselected instances might be uninitialized).
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
bke::AnonymousAttributePropagationInfo propagation_info = params.get_output_propagation_info(
"Geometry");
geometry_set = geometry::realize_instances(
geometry_set, {legacy_behavior, !legacy_behavior, selection, depths, propagation_info});
Review

I think it would be better to keep the explicit initialization of RealizeInstancesOptions.

I think it would be better to keep the explicit initialization of `RealizeInstancesOptions`.
params.set_output("Geometry", std::move(geometry_set));
}