davidhaver-WIP-realize-depth #2

Merged
Arye Ramaty merged 11 commits from David-Haver/blender-old:davidhaver-WIP-realize-depth into WIP-realize-depth 2024-03-21 14:55:23 +01:00
5 changed files with 325 additions and 265 deletions

View File

@ -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<bke::GeometrySet> geometries,
const bke::AnonymousAttributePropagationInfo &propagation_info);
void join_attributes(const Span<const bke::GeometryComponent *> src_components,
bke::GeometryComponent &result,
const Span<StringRef> ignored_attributes);
void join_attributes(Span<const bke::GeometryComponent *> src_components,
bke::GeometryComponent &r_result,
Span<StringRef> ignored_attributes = {});
} // namespace blender::geometry

View File

@ -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<int> 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

View File

@ -74,8 +74,8 @@ static void fill_new_attribute(const Span<const GeometryComponent *> src_compone
}
aryeramaty marked this conversation as resolved
Review

I would rather not make clean-up or style changes to files I didn't modify in the PR. It just makes it a little bit harder to see what has changed.

I would rather not make clean-up or style changes to files I didn't modify in the PR. It just makes it a little bit harder to see what has changed.
Review

the changes were done because you moved the declaration to the .hh file so it needed to follow the convention of public functions

the changes were done because you moved the declaration to the .hh file so it needed to follow the convention of public functions
Review

OK

OK
void join_attributes(const Span<const GeometryComponent *> src_components,
GeometryComponent &result,
const Span<StringRef> ignored_attributes = {})
GeometryComponent &r_result,
const Span<StringRef> ignored_attributes)
{
const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(src_components,
ignored_attributes);
@ -85,7 +85,7 @@ void join_attributes(const Span<const GeometryComponent *> 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<const GeometryComponent *> src_components,
}
static void join_instances(const Span<const GeometryComponent *> src_components,
GeometrySet &result)
GeometrySet &r_result)
{
Array<int> offsets_data(src_components.size() + 1);
for (const int i : src_components.index_range()) {
@ -127,8 +127,8 @@ static void join_instances(const Span<const GeometryComponent *> 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<bke::InstancesComponent>();
r_result.replace_instances(dst_instances.release());
auto &dst_component = r_result.get_component_for_write<bke::InstancesComponent>();
join_attributes(src_components, dst_component, {".reference_index"});
}
@ -142,7 +142,7 @@ static void join_volumes(const Span<const GeometryComponent *> /*src_components*
static void join_component_type(const bke::GeometryComponent::Type component_type,
const Span<GeometrySet> src_geometry_sets,
const bke::AnonymousAttributePropagationInfo &propagation_info,
GeometrySet &result)
GeometrySet &r_result)
{
Vector<const GeometryComponent *> 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<int>::ForSingle(-1, instances.get()->instances_num());
IndexMaskMemory memory;
options.selection = IndexMask::from_bools(
VArray<bool>::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<GeometrySet> geometries,

View File

@ -2,23 +2,14 @@
*
aryeramaty marked this conversation as resolved
Review

As a general rule, I'll try to minimize the length of the PR, aiming to stay as close as possible to origin/main. I believe things like 'include clean-up' can wait for a later PR.

As a general rule, I'll try to minimize the length of the PR, aiming to stay as close as possible to origin/main. I believe things like 'include clean-up' can wait for a later PR.
Review

The changes were done mainly to the code you add and to thing that changed because of it.
New included made some other unnecessary.

The changes were done mainly to the code you add and to thing that changed because of it. New included made some other unnecessary.
Review

Fair enough

Fair enough
* 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<AttributeFallbacksArray> attribute_fallback;
@ -235,6 +227,7 @@ struct AllInstancesInfo {
/** Base transform for each instance component. */
Vector<float4x4> instances_components_transforms;
};
/** Collects all tasks that need to be executed to realize all instances. */
struct GatherTasks {
Vector<RealizePointCloudTask> 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<Collection *> children_collections;
LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) {
children_collections.append(collection_child->collection);
}
Vector<Object *> 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<int> stored_ids,
const int task_id,
MutableSpan<int> 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<std::pair<int, GSpan>> 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<bke::Instances> instances = std::make_unique<bke::Instances>();
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<bke::GeometryComponent::Type> 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,
child_has_component = child_has_component | attribute_foreach(instance_geometry_set,
component_types,
current_depth + 1,
depth_target_tmp,
instance_depth,
selection,
callback) ||
is_child_has_component;
callback);
}
}
}
@ -796,16 +780,12 @@ 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 = (component_type == bke::GeometryComponent::Type::Instance) &&
const bool is_special_instance = (bke::GeometryComponent::Type::Instance ==
component_type) &&
(component_types.size() > 1);
if (is_special_instance && !is_child_has_component) {
continue;
}
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<bke::AttributeAccessor> attributes = component.attributes();
@ -819,10 +799,18 @@ bool static attribute_foreach(const bke::GeometrySet &geometry_set,
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<bke::GeometryComponent::Type> 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<bke::GeometryComponent::Type> 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<bke::GeometryComponentPtr> src_components,
Span<blender::float4x4> src_base_transforms,
OrderedAttributes all_instances_attributes,
Span<blender::geometry::AttributeFallbacksArray> 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<blender::float4x4>::ForSpan(src_base_transforms);
Array<int> offsets_data(src_components.size() + 1);
for (const int component_index : src_components.index_range()) {
const bke::InstancesComponent &src_component = static_cast<const bke::InstancesComponent &>(
*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<bke::Instances> dst_instances = std::make_unique<bke::Instances>();
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<float4x4> all_transforms = dst_instances->transforms_for_write();
MutableSpan<int> all_handles = dst_instances->reference_handles_for_write();
for (const int component_index : src_components.index_range()) {
const bke::InstancesComponent &src_component = static_cast<const bke::InstancesComponent &>(
*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<const void *> attribute_fallback_array = attribute_fallback[component_index].array;
const 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]);
}
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<int> 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<bke::InstancesComponent>();
Vector<const bke::GeometryComponent *> 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<AttributeIDRef, AttributeKind> 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<GSpanAttributeWriter> 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<bke::GeometryComponentPtr> src_components,
Span<blender::float4x4> src_base_transforms,
OrderedAttributes all_instances_attributes,
Span<blender::geometry::AttributeFallbacksArray> 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<blender::float4x4>::ForSpan(src_base_transforms);
Array<int> offsets_data(src_components.size() + 1);
for (const int component_index : src_components.index_range()) {
const bke::InstancesComponent &src_component = static_cast<const bke::InstancesComponent &>(
*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<bke::Instances> dst_instances = std::make_unique<bke::Instances>();
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<float4x4> all_transforms = dst_instances->transforms_for_write();
MutableSpan<int> all_handles = dst_instances->reference_handles_for_write();
for (const int component_index : src_components.index_range()) {
const auto &src_component = static_cast<const bke::InstancesComponent &>(
*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<const void *> attribute_fallback_array = attribute_fallback[component_index].array;
const 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]);
}
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<int> 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<bke::InstancesComponent>();
Vector<const bke::GeometryComponent *> 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<RealizePointCloudTask> 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<GSpanAttributeWriter> 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<RealizeMeshTask> 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<bke::GeometryComponent::Type> 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<RealizeCurveTask> 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<bke::InstancesComponent>();
std::unique_ptr<Instances> new_instances = std::make_unique<Instances>(instances);
new_instances->remove(inverse_selection, propagation_info);
bke::InstancesComponent &new_instances_components =
new_geometry_set.get_component_for_write<bke::InstancesComponent>();
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<int>::ForSingle(VariedDepthOption::MAX_DEPTH,
geometry_set.get_instances()->instances_num());
IndexMaskMemory memory;
all_instances.selection = IndexMask::from_bools(
VArray<bool>::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<std::unique_ptr<GArray<>>> 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<std::unique_ptr<GArray<>>> 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<bke::InstancesComponent>()).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,

View File

@ -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<decl::Bool>("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<decl::Int>("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<decl::Geometry>("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<bool> selection_field = params.extract_input<Field<bool>>("Selection");
Field<bool> realize_all_filed = params.extract_input<Field<bool>>("Realize All");
@ -46,33 +47,33 @@ static void node_geo_exec(GeoNodeExecParams params)
[](int value, bool realize) { return realize ? -1 : std::max(value, 0); },
aryeramaty marked this conversation as resolved Outdated

The use of std::max was intentional. We aim to prevent users from entering -1 into the field. Instead, we provide the 'realize all' option for that purpose.
This was suggested in the chat

The use of std::max was intentional. We aim to prevent users from entering -1 into the field. Instead, we provide the 'realize all' option for that purpose. This was suggested in the chat

From my understanding the users can't enter -1 because of .min(0) in line 27.
If it don't work like this I will change this back.

From my understanding the users can't enter -1 because of .min(0) in line 27. If it don't work like this I will change this back.

The user can enter -1; the .min(0) serves as a 'soft limit,' implying that the user can still set the value lower than 0 by either typing a specific value (instead of scrubbing) or connecting the socket to a link.

The user can enter -1; the .min(0) serves as a 'soft limit,' implying that the user can still set the value lower than 0 by either typing a specific value (instead of scrubbing) or connecting the socket to a link.

ok returned it

ok returned it
mf::build::exec_presets::AllSpanOrSingle());
static auto selction_override = mf::build::SI2_SO<int, bool, bool>(
"selction_override",
static auto selection_override = mf::build::SI2_SO<int, bool, bool>(
"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<int> depth_field_overrided(
FieldOperation::Create(depth_override, {depth_field, realize_all_filed}));
Field<bool> 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<int> depths = evaluator.get_evaluated<int>(0);
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
geometry::VariedDepthOption varied_depth_option;
David-Haver marked this conversation as resolved Outdated

This is fine, but I'm not sure about the name 'SpecificInstancesChoice'. I think something like 'SelectionAndDepthOption' may be clearer. Alternatively, 'VariedDepthOption' could also work.

This is fine, but I'm not sure about the name 'SpecificInstancesChoice'. I think something like 'SelectionAndDepthOption' may be clearer. Alternatively, 'VariedDepthOption' could also work.

I couldn't think of a better name VariedDepthOption sound great, I will change it to that.

I couldn't think of a better name VariedDepthOption sound great, I will change it to that.
varied_depth_option.depths = evaluator.get_evaluated<int>(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));
}