diff --git a/source/blender/geometry/GEO_join_geometries.hh b/source/blender/geometry/GEO_join_geometries.hh index 9e45bd5ab2b..8003cf5028b 100644 --- a/source/blender/geometry/GEO_join_geometries.hh +++ b/source/blender/geometry/GEO_join_geometries.hh @@ -3,16 +3,16 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once + #include "BKE_anonymous_attribute_id.hh" #include "BKE_geometry_set.hh" -#include "BLI_math_matrix_types.hh" namespace blender::geometry { bke::GeometrySet join_geometries(Span geometries, const bke::AnonymousAttributePropagationInfo &propagation_info); -void join_attributes(const Span src_components, - bke::GeometryComponent &result, - const Span ignored_attributes); +void join_attributes(Span src_components, + bke::GeometryComponent &r_result, + Span ignored_attributes = {}); } // namespace blender::geometry diff --git a/source/blender/geometry/GEO_realize_instances.hh b/source/blender/geometry/GEO_realize_instances.hh index 42532655b36..08a0d8a6da1 100644 --- a/source/blender/geometry/GEO_realize_instances.hh +++ b/source/blender/geometry/GEO_realize_instances.hh @@ -8,6 +8,9 @@ namespace blender::geometry { +/** + * General options for realize_instances. + */ struct RealizeInstancesOptions { /** * The default is to generate new ids for every element (when there was any id attribute in the @@ -22,6 +25,13 @@ struct RealizeInstancesOptions { */ bool realize_instance_attributes = true; + bke::AnonymousAttributePropagationInfo propagation_info; +}; + +/** + * Allow the user to choice which instances to realize and to what depth. + */ +struct VariedDepthOption { /** * Selection of top-level instances to realize. */ @@ -32,7 +42,10 @@ struct RealizeInstancesOptions { */ VArray depths; - bke::AnonymousAttributePropagationInfo propagation_info; + /** + * Use this value to realize the instance completely + */ + static const int MAX_DEPTH = -1; }; /** @@ -48,4 +61,20 @@ struct RealizeInstancesOptions { bke::GeometrySet realize_instances(bke::GeometrySet geometry_set, const RealizeInstancesOptions &options); +/** + * Join all instances into a single geometry component for each geometry type. For example, all + * mesh instances (including the already realized mesh) are joined into a single mesh. The output + * geometry set does not contain any instances. If the input did not contain any instances, it is + * returned directly. + * + * The `id` attribute has special handling. If there is an id attribute on any component, the + * output will contain an `id` attribute as well. The output id is generated by mixing/hashing ids + * of instances and of the instanced geometry data. + * + * Will realize only the instances chosen by varied_depth_option to there chosen depth. + */ +bke::GeometrySet realize_instances(bke::GeometrySet geometry_set, + const RealizeInstancesOptions &options, + const VariedDepthOption &varied_depth_option); + } // namespace blender::geometry diff --git a/source/blender/geometry/intern/join_geometries.cc b/source/blender/geometry/intern/join_geometries.cc index 21c8303365d..92912e9fa1a 100644 --- a/source/blender/geometry/intern/join_geometries.cc +++ b/source/blender/geometry/intern/join_geometries.cc @@ -74,8 +74,8 @@ static void fill_new_attribute(const Span src_compone } void join_attributes(const Span src_components, - GeometryComponent &result, - const Span ignored_attributes = {}) + GeometryComponent &r_result, + const Span ignored_attributes) { const Map info = get_final_attribute_info(src_components, ignored_attributes); @@ -85,7 +85,7 @@ void join_attributes(const Span src_components, const AttributeMetaData &meta_data = item.value; bke::GSpanAttributeWriter write_attribute = - result.attributes_for_write()->lookup_or_add_for_write_only_span( + r_result.attributes_for_write()->lookup_or_add_for_write_only_span( attribute_id, meta_data.domain, meta_data.data_type); if (!write_attribute) { continue; @@ -97,7 +97,7 @@ void join_attributes(const Span src_components, } static void join_instances(const Span src_components, - GeometrySet &result) + GeometrySet &r_result) { Array offsets_data(src_components.size() + 1); for (const int i : src_components.index_range()) { @@ -127,8 +127,8 @@ static void join_instances(const Span src_components, array_utils::gather(handle_map.as_span(), src_handles, all_handles.slice(dst_range)); } - result.replace_instances(dst_instances.release()); - auto &dst_component = result.get_component_for_write(); + r_result.replace_instances(dst_instances.release()); + auto &dst_component = r_result.get_component_for_write(); join_attributes(src_components, dst_component, {".reference_index"}); } @@ -142,7 +142,7 @@ static void join_volumes(const Span /*src_components* static void join_component_type(const bke::GeometryComponent::Type component_type, const Span src_geometry_sets, const bke::AnonymousAttributePropagationInfo &propagation_info, - GeometrySet &result) + GeometrySet &r_result) { Vector components; for (const GeometrySet &geometry_set : src_geometry_sets) { @@ -156,16 +156,16 @@ static void join_component_type(const bke::GeometryComponent::Type component_typ return; } if (components.size() == 1) { - result.add(*components.first()); + r_result.add(*components.first()); return; } switch (component_type) { case bke::GeometryComponent::Type::Instance: - join_instances(components, result); + join_instances(components, r_result); return; case bke::GeometryComponent::Type::Volume: - join_volumes(components, result); + join_volumes(components, r_result); return; default: break; @@ -185,13 +185,10 @@ static void join_component_type(const bke::GeometryComponent::Type component_typ options.keep_original_ids = true; options.realize_instance_attributes = false; options.propagation_info = propagation_info; - options.depths = VArray::ForSingle(-1, instances.get()->instances_num()); - IndexMaskMemory memory; - options.selection = IndexMask::from_bools( - VArray::ForSingle(true, instances.get()->instances_num()), memory); + GeometrySet joined_components = realize_instances( GeometrySet::from_instances(instances.release()), options); - result.add(joined_components.get_component_for_write(component_type)); + r_result.add(joined_components.get_component_for_write(component_type)); } GeometrySet join_geometries(const Span geometries, diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 5024a4bd6ee..7679f979507 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -2,23 +2,14 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_geometry_set.hh" #include "GEO_join_geometries.hh" #include "GEO_realize_instances.hh" #include "DNA_collection_types.h" -#include "DNA_layer_types.h" -#include "DNA_object_types.h" -#include "DNA_pointcloud_types.h" #include "BLI_array_utils.hh" -#include "BLI_listbase.h" -#include "BLI_math_matrix.hh" -#include "BLI_math_rotation.hh" #include "BLI_noise.hh" -#include "BLI_task.hh" -#include "BKE_collection.hh" #include "BKE_curves.hh" #include "BKE_customdata.hh" #include "BKE_geometry_set_instances.hh" @@ -227,6 +218,7 @@ struct AllCurvesInfo { bool create_nurbs_weight_attribute = false; bool create_custom_normal_attribute = false; }; + struct AllInstancesInfo { /** store an array of void pointer to attributes for each component. */ Vector attribute_fallback; @@ -235,6 +227,7 @@ struct AllInstancesInfo { /** Base transform for each instance component. */ Vector instances_components_transforms; }; + /** Collects all tasks that need to be executed to realize all instances. */ struct GatherTasks { Vector pointcloud_tasks; @@ -305,33 +298,25 @@ struct InstanceContext { curves(gather_info.curves.attributes.size()), instances(gather_info.instances_attriubutes.size()) { + //empty } }; static void realize_collections(Collection *collection, bke::Instances *instances) { - Vector children_collections; LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) { - children_collections.append(collection_child->collection); - } - Vector children_objects; - LISTBASE_FOREACH (CollectionObject *, collection_object, &collection->gobject) { - children_objects.append(collection_object->ob); + float4x4 transform = float4x4::identity(); + transform.location() += float3((collection_child->collection)->instance_offset); + transform.location() -= float3(collection->instance_offset); + const int handle = instances->add_reference(*(collection_child->collection)); + instances->add_instance(handle, transform); } - for (Collection *child_collection : children_collections) { - float4x4 transform = float4x4::identity(); - transform.location() += float3(child_collection->instance_offset); - transform.location() -= float3(collection->instance_offset); - const int handle = instances->add_reference(*child_collection); - instances->add_instance(handle, transform); - // entries.append({handle, &(child_collection->id.name[2]), transform}); - } - for (Object *child_object : children_objects) { - const int handle = instances->add_reference(*child_object); + LISTBASE_FOREACH (CollectionObject *, collection_object, &collection->gobject) { float4x4 transform = float4x4::identity(); transform.location() -= float3(collection->instance_offset); - transform *= child_object->object_to_world(); + transform *= (collection_object->ob)->object_to_world(); + const int handle = instances->add_reference(*(collection_object->ob)); instances->add_instance(handle, transform); } } @@ -409,12 +394,12 @@ static void copy_generic_attributes_to_result( }); } -static void create_result_ids(const RealizeInstancesOptions &options, +static void create_result_ids(bool keep_original_ids, Span stored_ids, const int task_id, MutableSpan dst_ids) { - if (options.keep_original_ids) { + if (keep_original_ids) { if (stored_ids.is_empty()) { dst_ids.fill(0); } @@ -497,10 +482,6 @@ static Vector> prepare_attribute_fallbacks( return attributes_to_override; } -/** - * Calls #fn for every geometry in the given #InstanceReference. Also passes on the transformation - * that is applied to every instance. - */ static bke::GeometrySet &geometry_set_from_reference(const InstanceReference &reference, bke::GeometrySet &r_geometry_set) { @@ -514,7 +495,7 @@ static bke::GeometrySet &geometry_set_from_reference(const InstanceReference &re Collection *collection_ptr = &reference.collection(); std::unique_ptr instances = std::make_unique(); realize_collections(collection_ptr, instances.get()); - r_geometry_set = bke::GeometrySet::from_instances(instances.release()); + r_geometry_set.replace_instances(instances.release()); break; } case InstanceReference::Type::GeometrySet: { @@ -533,6 +514,10 @@ static bke::GeometrySet &geometry_set_from_reference(const InstanceReference &re return r_geometry_set; } +/** + * Calls #fn for every geometry in the given #InstanceReference. Also passes on the transformation + * that is applied to every instance. + */ static void foreach_geometry_in_reference( const InstanceReference &reference, const float4x4 &base_transform, @@ -742,7 +727,18 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, } } -bool static attribute_foreach(const bke::GeometrySet &geometry_set, +/** + * This function iterates through a set of geometries, applying a callback to each attribute of + * eligible children based on specified conditions. Attributes should not be removed or added + * by the callback. Relevant children are determined by three criteria: the component type + * (e.g., mesh, curve), a depth value greater than 0 and a selection. If the primary component + * is an instance, the condition is true only when the depth is exactly 0. Additionally, the + * function extends its operation to instances if any of their nested children meet the first + * condition. + * + * Based on bke::GeometrySet::attribute_foreach + */ +static bool attribute_foreach(const bke::GeometrySet &geometry_set, const Span component_types, const int current_depth, const int depth_target, @@ -750,44 +746,32 @@ bool static attribute_foreach(const bke::GeometrySet &geometry_set, const IndexMask selection, const bke::GeometrySet::AttributeForeachCallback callback) { - /** - * This function iterates through a set of geometries, applying a callback to each attribute of - * eligible children based on specified conditions. Relevant children are determined by three - * criteria: the component type (e.g., mesh, curve), a depth value greater than 0 and a - * selection. If the primary component is an instance, the condition is true only when the depth - * is exactly 0. Additionally, the function extends its operation to instances if any of their - * nested children meet the first condition. Also, an initial depth of 0 is equal to infinity for - * easier use. - */ /*Initialize flag to track if child instances have the specified components.*/ - bool is_child_has_component = true; + bool child_has_component = true; if (geometry_set.has_instances()) { - is_child_has_component = false; + child_has_component = false; const Instances &instances = *geometry_set.get_instances(); - /*ensure objects and collection are included.*/ - Instances ensure_instances = instances; - const IndexMask indices = (current_depth == 0) ? + const IndexMask indices = (0 == current_depth) ? selection : IndexMask(IndexRange(instances.instances_num())); for (const int index : indices.index_range()) { const int i = indices[index]; - const int depth_target_tmp = (current_depth == 0) ? instance_depth[i] : depth_target; + const int depth_target_tmp = (0 == current_depth) ? instance_depth[i] : depth_target; bke::GeometrySet instance_geometry_set; geometry_set_from_reference(instances.references()[instances.reference_handles()[i]], instance_geometry_set); /*Process child instances with a recursive call.*/ if (current_depth != depth_target_tmp) { - is_child_has_component = attribute_foreach(instance_geometry_set, - component_types, - current_depth + 1, - depth_target_tmp, - instance_depth, - selection, - callback) || - is_child_has_component; + child_has_component = child_has_component | attribute_foreach(instance_geometry_set, + component_types, + current_depth + 1, + depth_target_tmp, + instance_depth, + selection, + callback); } } } @@ -796,33 +780,37 @@ bool static attribute_foreach(const bke::GeometrySet &geometry_set, bool is_relevant = false; for (const bke::GeometryComponent::Type component_type : component_types) { - if (!geometry_set.has(component_type)) { - continue; - } + if (geometry_set.has(component_type)) { + /*Check if the current instance components is the main one*/ + const bool is_special_instance = (bke::GeometryComponent::Type::Instance == + component_type) && + (component_types.size() > 1); + if (!is_special_instance || child_has_component) { + /*Process attributes for the current component.*/ + const bke::GeometryComponent &component = *geometry_set.get_component(component_type); + const std::optional attributes = component.attributes(); + if (attributes.has_value()) { + attributes->for_all( + [&](const AttributeIDRef &attributeId, const AttributeMetaData &metaData) { + callback(attributeId, metaData, component); + return true; + }); - /*Check if the current instance components is the main one*/ - const bool is_special_instance = (component_type == bke::GeometryComponent::Type::Instance) && - (component_types.size() > 1); - if (is_special_instance && !is_child_has_component) { - continue; - } - /*Process attributes for the current component.*/ - const bke::GeometryComponent &component = *geometry_set.get_component(component_type); - const std::optional attributes = component.attributes(); - if (attributes.has_value()) { - attributes->for_all( - [&](const AttributeIDRef &attributeId, const AttributeMetaData &metaData) { - callback(attributeId, metaData, component); - return true; - }); - - is_relevant = true; + is_relevant = true; + } + } } } return is_relevant; } + +/** + * Based on bke::GeometrySet::gather_attributes_for_propagation. + * Specialized for Specialized attribute_foreach to get: + * current_depth, depth_target, instance_depth and selection. +*/ void static gather_attributes_for_propagation( bke::GeometrySet re_geometry_set, const Span component_types, @@ -839,7 +827,7 @@ void static gather_attributes_for_propagation( attribute_foreach(re_geometry_set, component_types, 0, - -1, + VariedDepthOption::MAX_DEPTH, instance_depth, selection, [&](const AttributeIDRef &attribute_id, @@ -863,8 +851,7 @@ void static gather_attributes_for_propagation( AttrDomain domain = meta_data.domain; if (dst_component_type != bke::GeometryComponent::Type::Instance && - domain == AttrDomain::Instance) - { + domain == AttrDomain::Instance) { domain = AttrDomain::Point; } @@ -881,16 +868,17 @@ void static gather_attributes_for_propagation( r_attributes.add_or_modify(attribute_id, add_info, modify_info); }); } + /** \} */ /* -------------------------------------------------------------------- */ -/** \name Point Cloud +/** \name Instance * \{ */ static OrderedAttributes gather_generic_instance_attributes_to_propagate( const bke::GeometrySet &in_geometry_set, const RealizeInstancesOptions &options, - bool &r_create_id) + const VariedDepthOption &varied_depth_option) { Vector src_component_types; src_component_types.append(bke::GeometryComponent::Type::Instance); @@ -899,13 +887,13 @@ static OrderedAttributes gather_generic_instance_attributes_to_propagate( gather_attributes_for_propagation(in_geometry_set, src_component_types, bke::GeometryComponent::Type::Instance, - options.depths, - options.selection, + varied_depth_option.depths, + varied_depth_option.selection, options.propagation_info, attributes_to_propagate); attributes_to_propagate.remove("position"); attributes_to_propagate.remove("radius"); - r_create_id = attributes_to_propagate.pop_try("id").has_value(); + attributes_to_propagate.pop_try("id").has_value(); OrderedAttributes ordered_attributes; for (const auto item : attributes_to_propagate.items()) { ordered_attributes.ids.add_new(item.key); @@ -914,9 +902,113 @@ static OrderedAttributes gather_generic_instance_attributes_to_propagate( return ordered_attributes; } +static void execute_instances_tasks( + const Span src_components, + Span src_base_transforms, + OrderedAttributes all_instances_attributes, + Span attribute_fallback, + bke::GeometrySet &r_realized_geometry) +{ + BLI_assert(src_components.size() == src_base_transforms.size() && + src_components.size() == attribute_fallback.size()); + if (src_components.is_empty()) { + return; + } + + VArray::ForSpan(src_base_transforms); + Array offsets_data(src_components.size() + 1); + for (const int component_index : src_components.index_range()) { + const bke::InstancesComponent &src_component = static_cast( + *src_components[component_index]); + offsets_data[component_index] = src_component.get()->instances_num(); + } + const OffsetIndices offsets = offset_indices::accumulate_counts_to_offsets(offsets_data); + + std::unique_ptr dst_instances = std::make_unique(); + dst_instances->resize(offsets.total_size()); + + /* Prepare generic output attributes. */ + for (const int attribute_index : all_instances_attributes.index_range()) { + bke::AttrDomain domain = bke::AttrDomain::Instance; + bke::AttributeIDRef id = all_instances_attributes.ids[attribute_index]; + eCustomDataType type = all_instances_attributes.kinds[attribute_index].data_type; + blender::bke::MutableAttributeAccessor attr = dst_instances->attributes_for_write(); + attr.lookup_or_add_for_write_only_span(id, domain, type).finish(); + } + + MutableSpan all_transforms = dst_instances->transforms_for_write(); + MutableSpan all_handles = dst_instances->reference_handles_for_write(); + + for (const int component_index : src_components.index_range()) { + const bke::InstancesComponent &src_component = static_cast( + *src_components[component_index]); + const bke::Instances &src_instances = *src_component.get(); + const blender::float4x4 src_base_transform = src_base_transforms[component_index]; + const Array attribute_fallback_array = attribute_fallback[component_index].array; + const Span src_references = src_instances.references(); + Array 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]); + } + const IndexRange dst_range = offsets[component_index]; + for (const int attribute_index : all_instances_attributes.index_range()) { + bke::AttributeIDRef id = all_instances_attributes.ids[attribute_index]; + eCustomDataType type = all_instances_attributes.kinds[attribute_index].data_type; + const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(type); + BLI_assert(cpp_type != nullptr); + bke::GSpanAttributeWriter write_attribute = + dst_instances->attributes_for_write().lookup_for_write_span(id); + GMutableSpan dst_span = write_attribute.span; + + if (!write_attribute) { // do not override existing attributes + continue; + } + + const void *attribute_ptr; + if (attribute_fallback_array[attribute_index] != nullptr) { + attribute_ptr = attribute_fallback_array[attribute_index]; + } + else { + attribute_ptr = cpp_type->default_value(); + } + + GVArray src_span = GVArray::ForSingle(*cpp_type, dst_range.size(), attribute_ptr); + array_utils::copy(src_span, dst_span.slice(dst_range)); + write_attribute.finish(); + } + + const Span src_handles = src_instances.reference_handles(); + array_utils::gather(handle_map.as_span(), src_handles, all_handles.slice(dst_range)); + array_utils::copy(src_instances.transforms(), all_transforms.slice(dst_range)); + + for (blender::float4x4 &transfrom : all_transforms.slice(dst_range)) { + transfrom *= src_base_transform; + } + } + + r_realized_geometry.replace_instances(dst_instances.release()); + auto &dst_component = r_realized_geometry.get_component_for_write(); + + Vector for_join_attributes; + for (bke::GeometryComponentPtr compent : src_components) { + for_join_attributes.append(compent.get()); + } + + join_attributes( + for_join_attributes, dst_component, {"position", ".reference_index", "instance_transform"}); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Point Cloud + * \{ */ + static OrderedAttributes gather_generic_pointcloud_attributes_to_propagate( const bke::GeometrySet &in_geometry_set, const RealizeInstancesOptions &options, + const VariedDepthOption &varied_depth_option, bool &r_create_radii, bool &r_create_id) { @@ -925,20 +1017,16 @@ static OrderedAttributes gather_generic_pointcloud_attributes_to_propagate( if (options.realize_instance_attributes) { src_component_types.append(bke::GeometryComponent::Type::Instance); } + Map attributes_to_propagate; - // in_geometry_set.gather_attributes_for_propagation(src_component_types, - // bke::GeometryComponent::Type::PointCloud, - // options.depths, - // options.selection, - // options.propagation_info, - // attributes_to_propagate); gather_attributes_for_propagation(in_geometry_set, src_component_types, bke::GeometryComponent::Type::PointCloud, - options.depths, - options.selection, + varied_depth_option.depths, + varied_depth_option.selection, options.propagation_info, attributes_to_propagate); + attributes_to_propagate.remove("position"); r_create_id = attributes_to_propagate.pop_try("id").has_value(); r_create_radii = attributes_to_propagate.pop_try("radius").has_value(); @@ -966,11 +1054,15 @@ static void gather_pointclouds_to_realize(const bke::GeometrySet &geometry_set, } static AllPointCloudsInfo preprocess_pointclouds(const bke::GeometrySet &geometry_set, - const RealizeInstancesOptions &options) + const RealizeInstancesOptions &options, + const VariedDepthOption &varied_depth_option) { AllPointCloudsInfo info; - info.attributes = gather_generic_pointcloud_attributes_to_propagate( - geometry_set, options, info.create_radius_attribute, info.create_id_attribute); + info.attributes = gather_generic_pointcloud_attributes_to_propagate(geometry_set, + options, + varied_depth_option, + info.create_radius_attribute, + info.create_id_attribute); gather_pointclouds_to_realize(geometry_set, info.order); info.realize_info.reinitialize(info.order.size()); @@ -1009,7 +1101,7 @@ static AllPointCloudsInfo preprocess_pointclouds(const bke::GeometrySet &geometr } static void execute_realize_pointcloud_task( - const RealizeInstancesOptions &options, + bool keep_original_ids, const RealizePointCloudTask &task, const OrderedAttributes &ordered_attributes, MutableSpan dst_attribute_writers, @@ -1027,7 +1119,7 @@ static void execute_realize_pointcloud_task( /* Create point ids. */ if (!all_dst_ids.is_empty()) { create_result_ids( - options, pointcloud_info.stored_ids, task.id, all_dst_ids.slice(point_slice)); + keep_original_ids, pointcloud_info.stored_ids, task.id, all_dst_ids.slice(point_slice)); } if (!all_dst_radii.is_empty()) { pointcloud_info.radii.materialize(all_dst_radii.slice(point_slice)); @@ -1044,100 +1136,8 @@ static void execute_realize_pointcloud_task( }, dst_attribute_writers); } -static void execute_instances_tasks( - const Span src_components, - Span src_base_transforms, - OrderedAttributes all_instances_attributes, - Span attribute_fallback, - bke::GeometrySet &result) -{ - BLI_assert(src_components.size() == src_base_transforms.size() && - src_components.size() == attribute_fallback.size()); - if (src_components.is_empty()) { - return; - } - VArray::ForSpan(src_base_transforms); - Array offsets_data(src_components.size() + 1); - for (const int component_index : src_components.index_range()) { - const bke::InstancesComponent &src_component = static_cast( - *src_components[component_index]); - offsets_data[component_index] = src_component.get()->instances_num(); - } - const OffsetIndices offsets = offset_indices::accumulate_counts_to_offsets(offsets_data); - - std::unique_ptr dst_instances = std::make_unique(); - dst_instances->resize(offsets.total_size()); - - /* Prepare generic output attributes. */ - for (const int attribute_index : all_instances_attributes.index_range()) { - bke::AttrDomain domain = bke::AttrDomain::Instance; - bke::AttributeIDRef id = all_instances_attributes.ids[attribute_index]; - eCustomDataType type = all_instances_attributes.kinds[attribute_index].data_type; - blender::bke::MutableAttributeAccessor attr = dst_instances->attributes_for_write(); - attr.lookup_or_add_for_write_only_span(id, domain, type).finish(); - } - - MutableSpan all_transforms = dst_instances->transforms_for_write(); - MutableSpan all_handles = dst_instances->reference_handles_for_write(); - - for (const int component_index : src_components.index_range()) { - const auto &src_component = static_cast( - *src_components[component_index]); - const bke::Instances &src_instances = *src_component.get(); - const blender::float4x4 src_base_transform = src_base_transforms[component_index]; - const Array attribute_fallback_array = attribute_fallback[component_index].array; - - const Span src_references = src_instances.references(); - Array 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]); - } - const IndexRange dst_range = offsets[component_index]; - for (const int attribute_index : all_instances_attributes.index_range()) { - bke::AttributeIDRef id = all_instances_attributes.ids[attribute_index]; - eCustomDataType type = all_instances_attributes.kinds[attribute_index].data_type; - - const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(type); - BLI_assert(cpp_type != nullptr); - bke::GSpanAttributeWriter write_attribute = - dst_instances->attributes_for_write().lookup_for_write_span(id); - GMutableSpan dst_span = write_attribute.span; - if (!write_attribute) { // do not override existing attributes - continue; - } - - const void *attribute_ptr; // Declare a pointer to an integer - if (attribute_fallback_array[attribute_index] != nullptr) { - attribute_ptr = attribute_fallback_array[attribute_index]; - } - else { - attribute_ptr = cpp_type->default_value(); - } - GVArray src_span = GVArray::ForSingle(*cpp_type, dst_range.size(), attribute_ptr); - array_utils::copy(src_span, dst_span.slice(dst_range)); - write_attribute.finish(); - } - const Span src_handles = src_instances.reference_handles(); - array_utils::gather(handle_map.as_span(), src_handles, all_handles.slice(dst_range)); - array_utils::copy(src_instances.transforms(), all_transforms.slice(dst_range)); - for (blender::float4x4 &transfrom : all_transforms.slice(dst_range)) { - transfrom *= src_base_transform; - } - } - result.replace_instances(dst_instances.release()); - auto &dst_component = result.get_component_for_write(); - Vector for_join_attributes; - for (bke::GeometryComponentPtr compent : src_components) - { - for_join_attributes.append(compent.get()); - } - - join_attributes( - for_join_attributes, dst_component, {"position", ".reference_index", "instance_transform"}); -} - -static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &options, +static void execute_realize_pointcloud_tasks(bool keep_original_ids, const AllPointCloudsInfo &all_pointclouds_info, const Span tasks, const OrderedAttributes &ordered_attributes, @@ -1189,7 +1189,7 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) { for (const int task_index : task_range) { const RealizePointCloudTask &task = tasks[task_index]; - execute_realize_pointcloud_task(options, + execute_realize_pointcloud_task(keep_original_ids, task, ordered_attributes, dst_attribute_writers, @@ -1217,6 +1217,7 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti static OrderedAttributes gather_generic_mesh_attributes_to_propagate( const bke::GeometrySet &in_geometry_set, const RealizeInstancesOptions &options, + const VariedDepthOption &varied_depth_option, bool &r_create_id, bool &r_create_material_index) { @@ -1230,8 +1231,8 @@ static OrderedAttributes gather_generic_mesh_attributes_to_propagate( gather_attributes_for_propagation(in_geometry_set, src_component_types, bke::GeometryComponent::Type::Mesh, - options.depths, - options.selection, + varied_depth_option.depths, + varied_depth_option.selection, options.propagation_info, attributes_to_propagate); attributes_to_propagate.remove("position"); @@ -1264,11 +1265,16 @@ static void gather_meshes_to_realize(const bke::GeometrySet &geometry_set, } static AllMeshesInfo preprocess_meshes(const bke::GeometrySet &geometry_set, - const RealizeInstancesOptions &options) + const RealizeInstancesOptions &options, + const VariedDepthOption &varied_depth_option) { AllMeshesInfo info; info.attributes = gather_generic_mesh_attributes_to_propagate( - geometry_set, options, info.create_id_attribute, info.create_material_index_attribute); + geometry_set, + options, + varied_depth_option, + info.create_id_attribute, + info.create_material_index_attribute); gather_meshes_to_realize(geometry_set, info.order); for (const Mesh *mesh : info.order) { @@ -1346,7 +1352,7 @@ static AllMeshesInfo preprocess_meshes(const bke::GeometrySet &geometry_set, return info; } -static void execute_realize_mesh_task(const RealizeInstancesOptions &options, +static void execute_realize_mesh_task(bool keep_original_ids, const RealizeMeshTask &task, const OrderedAttributes &ordered_attributes, MutableSpan dst_attribute_writers, @@ -1430,7 +1436,7 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options, } if (!all_dst_vertex_ids.is_empty()) { - create_result_ids(options, + create_result_ids(keep_original_ids, mesh_info.stored_vertex_ids, task.id, all_dst_vertex_ids.slice(task.start_indices.vertex, mesh.verts_num)); @@ -1458,7 +1464,7 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options, dst_attribute_writers); } -static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options, +static void execute_realize_mesh_tasks(bool keep_original_ids, const AllMeshesInfo &all_meshes_info, const Span tasks, const OrderedAttributes &ordered_attributes, @@ -1541,7 +1547,7 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options, threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) { for (const int task_index : task_range) { const RealizeMeshTask &task = tasks[task_index]; - execute_realize_mesh_task(options, + execute_realize_mesh_task(keep_original_ids, task, ordered_attributes, dst_attribute_writers, @@ -1582,6 +1588,7 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options, static OrderedAttributes gather_generic_curve_attributes_to_propagate( const bke::GeometrySet &in_geometry_set, const RealizeInstancesOptions &options, + const VariedDepthOption &varied_depth_option, bool &r_create_id) { Vector src_component_types; @@ -1594,8 +1601,8 @@ static OrderedAttributes gather_generic_curve_attributes_to_propagate( gather_attributes_for_propagation(in_geometry_set, src_component_types, bke::GeometryComponent::Type::Curve, - options.depths, - options.selection, + varied_depth_option.depths, + varied_depth_option.selection, options.propagation_info, attributes_to_propagate); attributes_to_propagate.remove("position"); @@ -1630,11 +1637,12 @@ static void gather_curves_to_realize(const bke::GeometrySet &geometry_set, } static AllCurvesInfo preprocess_curves(const bke::GeometrySet &geometry_set, - const RealizeInstancesOptions &options) + const RealizeInstancesOptions &options, + const VariedDepthOption &varied_depth_option) { AllCurvesInfo info; info.attributes = gather_generic_curve_attributes_to_propagate( - geometry_set, options, info.create_id_attribute); + geometry_set, options, varied_depth_option, info.create_id_attribute); gather_curves_to_realize(geometry_set, info.order); info.realize_info.reinitialize(info.order.size()); @@ -1693,7 +1701,7 @@ static AllCurvesInfo preprocess_curves(const bke::GeometrySet &geometry_set, return info; } -static void execute_realize_curve_task(const RealizeInstancesOptions &options, +static void execute_realize_curve_task(bool keep_original_ids, const AllCurvesInfo &all_curves_info, const RealizeCurveTask &task, const OrderedAttributes &ordered_attributes, @@ -1776,7 +1784,7 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options, if (!all_dst_ids.is_empty()) { create_result_ids( - options, curves_info.stored_ids, task.id, all_dst_ids.slice(dst_point_range)); + keep_original_ids, curves_info.stored_ids, task.id, all_dst_ids.slice(dst_point_range)); } copy_generic_attributes_to_result( @@ -1797,7 +1805,7 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options, dst_attribute_writers); } -static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, +static void execute_realize_curve_tasks(bool keep_original_ids, const AllCurvesInfo &all_curves_info, const Span tasks, const OrderedAttributes &ordered_attributes, @@ -1876,7 +1884,7 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) { for (const int task_index : task_range) { const RealizeCurveTask &task = tasks[task_index]; - execute_realize_curve_task(options, + execute_realize_curve_task(keep_original_ids, all_curves_info, task, ordered_attributes, @@ -1946,16 +1954,33 @@ static void propagate_instances_to_keep( return; } - bke::InstancesComponent &new_instances_components = - new_geometry_set.get_component_for_write(); - std::unique_ptr new_instances = std::make_unique(instances); new_instances->remove(inverse_selection, propagation_info); + + bke::InstancesComponent &new_instances_components = + new_geometry_set.get_component_for_write(); new_instances_components.replace(new_instances.release(), bke::GeometryOwnershipType::Owned); } bke::GeometrySet realize_instances(bke::GeometrySet geometry_set, const RealizeInstancesOptions &options) +{ + if (!geometry_set.has_instances()) { + return geometry_set; + } + + VariedDepthOption all_instances; + all_instances.depths = VArray::ForSingle(VariedDepthOption::MAX_DEPTH, + geometry_set.get_instances()->instances_num()); + IndexMaskMemory memory; + all_instances.selection = IndexMask::from_bools( + VArray::ForSingle(true, geometry_set.get_instances()->instances_num()), memory); + return realize_instances(geometry_set, options, all_instances); +} + +bke::GeometrySet realize_instances(bke::GeometrySet geometry_set, + const RealizeInstancesOptions &options, + const VariedDepthOption &varied_depth_option) { /* The algorithm works in three steps: * 1. Preprocess each unique geometry that is instanced (e.g. each `Mesh`). @@ -1970,37 +1995,32 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set, bke::GeometrySet not_to_realize_set; propagate_instances_to_keep( - geometry_set, options.selection, not_to_realize_set, options.propagation_info); + geometry_set, varied_depth_option.selection, not_to_realize_set, options.propagation_info); if (options.keep_original_ids) { remove_id_attribute_from_instances(geometry_set); } - AllPointCloudsInfo all_pointclouds_info = preprocess_pointclouds(geometry_set, options); - AllMeshesInfo all_meshes_info = preprocess_meshes(geometry_set, options); - AllCurvesInfo all_curves_info = preprocess_curves(geometry_set, options); - bool tmp = true; + AllPointCloudsInfo all_pointclouds_info = preprocess_pointclouds( + geometry_set, options, varied_depth_option); + AllMeshesInfo all_meshes_info = preprocess_meshes(geometry_set, options, varied_depth_option); + AllCurvesInfo all_curves_info = preprocess_curves(geometry_set, options, varied_depth_option); OrderedAttributes all_instance_attributes = gather_generic_instance_attributes_to_propagate( - geometry_set, options, tmp); + geometry_set, options, varied_depth_option); - Vector>> temporary_arrays; const bool create_id_attribute = all_pointclouds_info.create_id_attribute || all_meshes_info.create_id_attribute || all_curves_info.create_id_attribute; + Vector>> temporary_arrays; GatherTasksInfo gather_info = {all_pointclouds_info, all_meshes_info, all_curves_info, all_instance_attributes, create_id_attribute, - options.selection, - options.depths, + varied_depth_option.selection, + varied_depth_option.depths, temporary_arrays}; - bke::GeometrySet new_geometry_set; - - const float4x4 transform = float4x4::identity(); - InstanceContext attribute_fallbacks(gather_info); - if (not_to_realize_set.has_instances()) { gather_info.instances.instances_components_to_merge.append( (not_to_realize_set.get_component_for_write()).copy()); @@ -2008,25 +2028,38 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set, gather_info.instances.attribute_fallback.append((gather_info.instances_attriubutes.size())); } - gather_realize_tasks_recursive(gather_info, 0, -1, geometry_set, transform, attribute_fallbacks); + const float4x4 transform = float4x4::identity(); + InstanceContext attribute_fallbacks(gather_info); + + gather_realize_tasks_recursive(gather_info, + 0, + VariedDepthOption::MAX_DEPTH, + geometry_set, + transform, + attribute_fallbacks); + + bke::GeometrySet new_geometry_set; execute_instances_tasks(gather_info.instances.instances_components_to_merge, gather_info.instances.instances_components_transforms, all_instance_attributes, gather_info.instances.attribute_fallback, new_geometry_set); - execute_realize_pointcloud_tasks(options, + + execute_realize_pointcloud_tasks(options.keep_original_ids, all_pointclouds_info, gather_info.r_tasks.pointcloud_tasks, all_pointclouds_info.attributes, new_geometry_set); - execute_realize_mesh_tasks(options, + + execute_realize_mesh_tasks(options.keep_original_ids, all_meshes_info, gather_info.r_tasks.mesh_tasks, all_meshes_info.attributes, all_meshes_info.materials, new_geometry_set); - execute_realize_curve_tasks(options, + + execute_realize_curve_tasks(options.keep_original_ids, all_curves_info, gather_info.r_tasks.curve_tasks, all_curves_info.attributes, diff --git a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc index 34c1b8f11c7..2fd19b7b9df 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc @@ -19,13 +19,13 @@ static void node_declare(NodeDeclarationBuilder &b) .default_value(true) .hide_value() .supports_field() - .description(("Which top-level instances to realize")); + .description("Which top-level instances to realize"); b.add_input("Realize All") .default_value(true) .supports_field() - .description(("Determine wether to realize nested instances completly")); + .description("Determine wether to realize nested instances completly"); b.add_input("Depth").default_value(0).min(0).supports_field().description( - ("Number of levels of nested instances to realize for each top-level instance")); + "Number of levels of nested instances to realize for each top-level instance"); b.add_output("Geometry").propagate_all(); } @@ -36,6 +36,7 @@ static void node_geo_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); return; } + GeometryComponentEditData::remember_deformed_positions_if_necessary(geometry_set); Field selection_field = params.extract_input>("Selection"); Field realize_all_filed = params.extract_input>("Realize All"); @@ -46,33 +47,33 @@ static void node_geo_exec(GeoNodeExecParams params) [](int value, bool realize) { return realize ? -1 : std::max(value, 0); }, mf::build::exec_presets::AllSpanOrSingle()); - static auto selction_override = mf::build::SI2_SO( - "selction_override", + static auto selection_override = mf::build::SI2_SO( + "selection_override", [](int value, bool selection) { return value == 0 ? false : selection; }, mf::build::exec_presets::AllSpanOrSingle()); - const bke::Instances &instances = *geometry_set.get_instances(); - const bke::InstancesFieldContext field_context{instances}; - fn::FieldEvaluator evaluator{field_context, instances.instances_num()}; - Field depth_field_overrided( FieldOperation::Create(depth_override, {depth_field, realize_all_filed})); Field selection_field_overrided( - FieldOperation::Create(selction_override, {depth_field_overrided, selection_field})); + FieldOperation::Create(selection_override, {depth_field_overrided, selection_field})); - evaluator.add(depth_field_overrided); + const bke::Instances &instances = *geometry_set.get_instances(); + const bke::InstancesFieldContext field_context(instances); + fn::FieldEvaluator evaluator(field_context, instances.instances_num()); + + const int evaluated_depth_index = evaluator.add(depth_field_overrided); evaluator.set_selection(selection_field_overrided); evaluator.evaluate(); - const VArray depths = evaluator.get_evaluated(0); - const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + geometry::VariedDepthOption varied_depth_option; + varied_depth_option.depths = evaluator.get_evaluated(evaluated_depth_index); + varied_depth_option.selection = evaluator.get_evaluated_selection_as_mask(); geometry::RealizeInstancesOptions options; options.keep_original_ids = false; options.realize_instance_attributes = true; options.propagation_info = params.get_output_propagation_info("Geometry"); - options.depths = depths; - options.selection = selection; - geometry_set = geometry::realize_instances(geometry_set, options); + geometry_set = geometry::realize_instances(geometry_set, options, varied_depth_option); params.set_output("Geometry", std::move(geometry_set)); }