diff options
Diffstat (limited to 'source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc')
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc | 201 |
1 files changed, 130 insertions, 71 deletions
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 8c0c0763be8..71256a7f781 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -22,72 +22,80 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "BKE_attribute_math.hh" + #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_instance_on_points_cc { -static void geo_node_instance_on_points_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Points").description("Points to instance on"); - b.add_input<decl::Geometry>("Instance").description("Geometry that is instanced on the points"); - b.add_input<decl::Bool>("Pick Instance") + b.add_input<decl::Geometry>(N_("Points")).description(N_("Points to instance on")); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_input<decl::Geometry>(N_("Instance")) + .description(N_("Geometry that is instanced on the points")); + b.add_input<decl::Bool>(N_("Pick Instance")) .supports_field() - .description("Place different instances on different points"); - b.add_input<decl::Int>("Instance Index") + .description(N_("Place different instances on different points")); + b.add_input<decl::Int>(N_("Instance Index")) .implicit_field() - .description( + .description(N_( "Index of the instance that used for each point. This is only used when Pick Instances " - "is on. By default the point index is used"); - b.add_input<decl::Vector>("Rotation") + "is on. By default the point index is used")); + b.add_input<decl::Vector>(N_("Rotation")) .subtype(PROP_EULER) .supports_field() - .description("Rotation of the instances"); - b.add_input<decl::Vector>("Scale") + .description(N_("Rotation of the instances")); + b.add_input<decl::Vector>(N_("Scale")) .default_value({1.0f, 1.0f, 1.0f}) + .subtype(PROP_XYZ) .supports_field() - .description("Scale of the instances"); - b.add_input<decl::Int>("Stable ID") - .supports_field() - .description( - "ID for every instance that is used to identify it over time even when the number of " - "instances changes. Used for example for motion blur"); + .description(N_("Scale of the instances")); - b.add_output<decl::Geometry>("Instances"); + b.add_output<decl::Geometry>(N_("Instances")); } -static void add_instances_from_component(InstancesComponent &dst_component, - const GeometryComponent &src_component, - const GeometrySet &instance, - const GeoNodeExecParams ¶ms) +static void add_instances_from_component( + InstancesComponent &dst_component, + const GeometryComponent &src_component, + const GeometrySet &instance, + const GeoNodeExecParams ¶ms, + const Map<AttributeIDRef, AttributeKind> &attributes_to_propagate) { const AttributeDomain domain = ATTR_DOMAIN_POINT; const int domain_size = src_component.attribute_domain_size(domain); + VArray<bool> pick_instance; + VArray<int> indices; + VArray<float3> rotations; + VArray<float3> scales; + + GeometryComponentFieldContext field_context{src_component, domain}; + const Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(selection_field); + /* The evaluator could use the component's stable IDs as a destination directly, but only the + * selected indices should be copied. */ + evaluator.add(params.get_input<Field<bool>>("Pick Instance"), &pick_instance); + evaluator.add(params.get_input<Field<int>>("Instance Index"), &indices); + evaluator.add(params.get_input<Field<float3>>("Rotation"), &rotations); + evaluator.add(params.get_input<Field<float3>>("Scale"), &scales); + evaluator.evaluate(); + + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + /* The initial size of the component might be non-zero when this function is called for multiple * component types. */ const int start_len = dst_component.instances_amount(); - dst_component.resize(start_len + domain_size); + const int select_len = selection.index_range().size(); + dst_component.resize(start_len + select_len); + MutableSpan<int> dst_handles = dst_component.instance_reference_handles().slice(start_len, - domain_size); + select_len); MutableSpan<float4x4> dst_transforms = dst_component.instance_transforms().slice(start_len, - domain_size); - MutableSpan<int> dst_stable_ids = dst_component.instance_ids().slice(start_len, domain_size); + select_len); - GeometryComponentFieldContext field_context{src_component, domain}; - FieldEvaluator field_evaluator{field_context, domain_size}; - - const VArray<bool> *pick_instance = nullptr; - const VArray<int> *indices = nullptr; - const VArray<float3> *rotations = nullptr; - const VArray<float3> *scales = nullptr; - field_evaluator.add(params.get_input<Field<bool>>("Pick Instance"), &pick_instance); - field_evaluator.add(params.get_input<Field<int>>("Instance Index"), &indices); - field_evaluator.add(params.get_input<Field<float3>>("Rotation"), &rotations); - field_evaluator.add(params.get_input<Field<float3>>("Scale"), &scales); - field_evaluator.add_with_destination(params.get_input<Field<int>>("Stable ID"), dst_stable_ids); - field_evaluator.evaluate(); - - GVArray_Typed<float3> positions = src_component.attribute_get_for_read<float3>( + VArray<float3> positions = src_component.attribute_get_for_read<float3>( "position", domain, {0, 0, 0}); const InstancesComponent *src_instances = instance.get_component_for_read<InstancesComponent>(); @@ -96,7 +104,7 @@ static void add_instances_from_component(InstancesComponent &dst_component, Array<int> handle_mapping; /* Only fill #handle_mapping when it may be used below. */ if (src_instances != nullptr && - (!pick_instance->is_single() || pick_instance->get_internal_single())) { + (!pick_instance.is_single() || pick_instance.get_internal_single())) { Span<InstanceReference> src_references = src_instances->references(); handle_mapping.reinitialize(src_references.size()); for (const int src_instance_handle : src_references.index_range()) { @@ -110,23 +118,24 @@ static void add_instances_from_component(InstancesComponent &dst_component, /* Add this reference last, because it is the most likely one to be removed later on. */ const int empty_reference_handle = dst_component.add_reference(InstanceReference()); - threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) { - for (const int i : range) { + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) { + for (const int range_i : selection_range) { + const int64_t i = selection[range_i]; + /* Compute base transform for every instances. */ - float4x4 &dst_transform = dst_transforms[i]; - dst_transform = float4x4::from_loc_eul_scale( - positions[i], rotations->get(i), scales->get(i)); + float4x4 &dst_transform = dst_transforms[range_i]; + dst_transform = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]); /* Reference that will be used by this new instance. */ int dst_handle = empty_reference_handle; - const bool use_individual_instance = pick_instance->get(i); + const bool use_individual_instance = pick_instance[i]; if (use_individual_instance) { if (src_instances != nullptr) { const int src_instances_amount = src_instances->instances_amount(); - const int original_index = indices->get(i); - /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1 refers to - * the last element. */ + const int original_index = indices[i]; + /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1 + * refers to the last element. */ const int index = mod_i(original_index, std::max(src_instances_amount, 1)); if (index < src_instances_amount) { /* Get the reference to the source instance. */ @@ -144,12 +153,12 @@ static void add_instances_from_component(InstancesComponent &dst_component, dst_handle = full_instance_handle; } /* Set properties of new instance. */ - dst_handles[i] = dst_handle; + dst_handles[range_i] = dst_handle; } }); - if (pick_instance->is_single()) { - if (pick_instance->get_internal_single()) { + if (pick_instance.is_single()) { + if (pick_instance.get_internal_single()) { if (instance.has_realized_data()) { params.error_message_add( NodeWarningType::Info, @@ -157,9 +166,40 @@ static void add_instances_from_component(InstancesComponent &dst_component, } } } + + bke::CustomDataAttributes &instance_attributes = dst_component.attributes(); + for (const auto item : attributes_to_propagate.items()) { + const AttributeIDRef &attribute_id = item.key; + const AttributeKind attribute_kind = item.value; + + const GVArray src_attribute = src_component.attribute_get_for_read( + attribute_id, ATTR_DOMAIN_POINT, attribute_kind.data_type); + BLI_assert(src_attribute); + std::optional<GMutableSpan> dst_attribute_opt = instance_attributes.get_for_write( + attribute_id); + if (!dst_attribute_opt) { + if (!instance_attributes.create(attribute_id, attribute_kind.data_type)) { + continue; + } + dst_attribute_opt = instance_attributes.get_for_write(attribute_id); + } + BLI_assert(dst_attribute_opt); + const GMutableSpan dst_attribute = dst_attribute_opt->slice(start_len, select_len); + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) { + attribute_math::convert_to_static_type(attribute_kind.data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray<T> src = src_attribute.typed<T>(); + MutableSpan<T> dst = dst_attribute.typed<T>(); + for (const int range_i : selection_range) { + const int i = selection[range_i]; + dst[range_i] = src[i]; + } + }); + }); + } } -static void geo_node_instance_on_points_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Points"); GeometrySet instance = params.get_input<GeometrySet>("Instance"); @@ -168,40 +208,59 @@ static void geo_node_instance_on_points_exec(GeoNodeExecParams params) geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + Map<AttributeIDRef, AttributeKind> attributes_to_propagate; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}, + GEO_COMPONENT_TYPE_INSTANCES, + false, + attributes_to_propagate); + attributes_to_propagate.remove("position"); + if (geometry_set.has<MeshComponent>()) { - add_instances_from_component( - instances, *geometry_set.get_component_for_read<MeshComponent>(), instance, params); - geometry_set.remove(GEO_COMPONENT_TYPE_MESH); + add_instances_from_component(instances, + *geometry_set.get_component_for_read<MeshComponent>(), + instance, + params, + attributes_to_propagate); } if (geometry_set.has<PointCloudComponent>()) { add_instances_from_component(instances, *geometry_set.get_component_for_read<PointCloudComponent>(), instance, - params); - geometry_set.remove(GEO_COMPONENT_TYPE_POINT_CLOUD); + params, + attributes_to_propagate); } if (geometry_set.has<CurveComponent>()) { - add_instances_from_component( - instances, *geometry_set.get_component_for_read<CurveComponent>(), instance, params); - geometry_set.remove(GEO_COMPONENT_TYPE_CURVE); + add_instances_from_component(instances, + *geometry_set.get_component_for_read<CurveComponent>(), + instance, + params, + attributes_to_propagate); } - /* Unused references may have been added above. Remove those now so that other nodes don't - * process them needlessly. */ - instances.remove_unused_references(); + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); }); + /* Unused references may have been added above. Remove those now so that other nodes don't + * process them needlessly. + * This should eventually be moved into the loop above, but currently this is quite tricky + * because it might remove references that the loop still wants to iterate over. */ + InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + instances.remove_unused_references(); + params.set_output("Instances", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_instance_on_points_cc void register_node_type_geo_instance_on_points() { + namespace file_ns = blender::nodes::node_geo_instance_on_points_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_INSTANCE_ON_POINTS, "Instance on Points", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_instance_on_points_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_instance_on_points_exec; + &ntype, GEO_NODE_INSTANCE_ON_POINTS, "Instance on Points", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } |