forked from blender/blender
davidhaver-WIP-realize-depth #2
@ -8,6 +8,9 @@
|
|||||||
|
|
||||||
namespace blender::geometry {
|
namespace blender::geometry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General options for realize_instances.
|
||||||
|
*/
|
||||||
struct RealizeInstancesOptions {
|
struct RealizeInstancesOptions {
|
||||||
/**
|
/**
|
||||||
* The default is to generate new ids for every element (when there was any id attribute in the
|
* 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;
|
bool realize_instance_attributes = true;
|
||||||
|
|
||||||
|
bke::AnonymousAttributePropagationInfo propagation_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow the user to choice which instances to realize and to what depth.
|
||||||
|
*/
|
||||||
|
struct SpecificInstancesChoice {
|
||||||
/**
|
/**
|
||||||
* Selection of top-level instances to realize.
|
* Selection of top-level instances to realize.
|
||||||
*/
|
*/
|
||||||
@ -32,7 +42,7 @@ struct RealizeInstancesOptions {
|
|||||||
*/
|
*/
|
||||||
VArray<int> depths;
|
VArray<int> depths;
|
||||||
|
|
||||||
bke::AnonymousAttributePropagationInfo propagation_info;
|
static const int MAX_DEPTH = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,4 +58,20 @@ struct RealizeInstancesOptions {
|
|||||||
bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
||||||
const RealizeInstancesOptions &options);
|
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 instaces chosen by chosen_instaces to there chosen depth.
|
||||||
|
*/
|
||||||
|
bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
||||||
|
const RealizeInstancesOptions &options,
|
||||||
|
const SpecificInstancesChoice &chosen_instaces);
|
||||||
|
|
||||||
} // namespace blender::geometry
|
} // namespace blender::geometry
|
||||||
|
@ -185,10 +185,7 @@ static void join_component_type(const bke::GeometryComponent::Type component_typ
|
|||||||
options.keep_original_ids = true;
|
options.keep_original_ids = true;
|
||||||
options.realize_instance_attributes = false;
|
options.realize_instance_attributes = false;
|
||||||
options.propagation_info = propagation_info;
|
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 joined_components = realize_instances(
|
||||||
GeometrySet::from_instances(instances.release()), options);
|
GeometrySet::from_instances(instances.release()), options);
|
||||||
r_result.add(joined_components.get_component_for_write(component_type));
|
r_result.add(joined_components.get_component_for_write(component_type));
|
||||||
|
@ -49,13 +49,13 @@ struct OrderedAttributes {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AttributeFallbacksArray {
|
/**
|
||||||
/**
|
|
||||||
* Instance attribute values used as fallback when the geometry does not have the
|
* Instance attribute values used as fallback when the geometry does not have the
|
||||||
* corresponding attributes itself. The pointers point to attributes stored in the instances
|
* corresponding attributes itself. The pointers point to attributes stored in the instances
|
||||||
* component or in #r_temporary_arrays. The order depends on the corresponding #OrderedAttributes
|
* component or in #r_temporary_arrays. The order depends on the corresponding #OrderedAttributes
|
||||||
* instance.
|
* instance.
|
||||||
*/
|
*/
|
||||||
|
struct AttributeFallbacksArray {
|
||||||
Array<const void *> array;
|
Array<const void *> array;
|
||||||
|
|
||||||
AttributeFallbacksArray(int size) : array(size, nullptr) {}
|
AttributeFallbacksArray(int size) : array(size, nullptr) {}
|
||||||
@ -400,12 +400,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,
|
Span<int> stored_ids,
|
||||||
const int task_id,
|
const int task_id,
|
||||||
MutableSpan<int> dst_ids)
|
MutableSpan<int> dst_ids)
|
||||||
{
|
{
|
||||||
if (options.keep_original_ids) {
|
if (keep_original_ids) {
|
||||||
if (stored_ids.is_empty()) {
|
if (stored_ids.is_empty()) {
|
||||||
dst_ids.fill(0);
|
dst_ids.fill(0);
|
||||||
}
|
}
|
||||||
@ -733,6 +733,15 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 aselection. If the primary component
|
||||||
|
* is an instance, the condition is true only when the depthis exactly 0. Additionally, the
|
||||||
|
* function extends its operation to instances if any of theirnested children meet the first
|
||||||
|
* condition. Also, an initial depth of 0 is equal to infinity for iteration easier use.
|
||||||
|
*/
|
||||||
bool static attribute_foreach(const bke::GeometrySet &geometry_set,
|
bool static attribute_foreach(const bke::GeometrySet &geometry_set,
|
||||||
const Span<bke::GeometryComponent::Type> component_types,
|
const Span<bke::GeometryComponent::Type> component_types,
|
||||||
const int current_depth,
|
const int current_depth,
|
||||||
@ -741,44 +750,32 @@ bool static attribute_foreach(const bke::GeometrySet &geometry_set,
|
|||||||
const IndexMask selection,
|
const IndexMask selection,
|
||||||
const bke::GeometrySet::AttributeForeachCallback callback)
|
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.*/
|
/*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()) {
|
if (geometry_set.has_instances()) {
|
||||||
is_child_has_component = false;
|
child_has_component = false;
|
||||||
|
|
||||||
const Instances &instances = *geometry_set.get_instances();
|
const Instances &instances = *geometry_set.get_instances();
|
||||||
/*ensure objects and collection are included.*/
|
const IndexMask indices = (0 == current_depth) ?
|
||||||
Instances ensure_instances = instances;
|
|
||||||
const IndexMask indices = (current_depth == 0) ?
|
|
||||||
selection :
|
selection :
|
||||||
IndexMask(IndexRange(instances.instances_num()));
|
IndexMask(IndexRange(instances.instances_num()));
|
||||||
for (const int index : indices.index_range()) {
|
for (const int index : indices.index_range()) {
|
||||||
const int i = indices[index];
|
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;
|
bke::GeometrySet instance_geometry_set;
|
||||||
geometry_set_from_reference(instances.references()[instances.reference_handles()[i]],
|
geometry_set_from_reference(instances.references()[instances.reference_handles()[i]],
|
||||||
instance_geometry_set);
|
instance_geometry_set);
|
||||||
/*Process child instances with a recursive call.*/
|
/*Process child instances with a recursive call.*/
|
||||||
if (current_depth != depth_target_tmp) {
|
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,
|
component_types,
|
||||||
current_depth + 1,
|
current_depth + 1,
|
||||||
depth_target_tmp,
|
depth_target_tmp,
|
||||||
instance_depth,
|
instance_depth,
|
||||||
selection,
|
selection,
|
||||||
callback) ||
|
callback);
|
||||||
is_child_has_component;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -787,16 +784,12 @@ bool static attribute_foreach(const bke::GeometrySet &geometry_set,
|
|||||||
bool is_relevant = false;
|
bool is_relevant = false;
|
||||||
|
|
||||||
for (const bke::GeometryComponent::Type component_type : component_types) {
|
for (const bke::GeometryComponent::Type component_type : component_types) {
|
||||||
if (!geometry_set.has(component_type)) {
|
if (geometry_set.has(component_type)) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Check if the current instance components is the main one*/
|
/*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);
|
(component_types.size() > 1);
|
||||||
if (is_special_instance && !is_child_has_component) {
|
if (!is_special_instance || child_has_component) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/*Process attributes for the current component.*/
|
/*Process attributes for the current component.*/
|
||||||
const bke::GeometryComponent &component = *geometry_set.get_component(component_type);
|
const bke::GeometryComponent &component = *geometry_set.get_component(component_type);
|
||||||
const std::optional<bke::AttributeAccessor> attributes = component.attributes();
|
const std::optional<bke::AttributeAccessor> attributes = component.attributes();
|
||||||
@ -810,6 +803,8 @@ bool static attribute_foreach(const bke::GeometrySet &geometry_set,
|
|||||||
is_relevant = true;
|
is_relevant = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return is_relevant;
|
return is_relevant;
|
||||||
}
|
}
|
||||||
@ -830,7 +825,7 @@ void static gather_attributes_for_propagation(
|
|||||||
attribute_foreach(re_geometry_set,
|
attribute_foreach(re_geometry_set,
|
||||||
component_types,
|
component_types,
|
||||||
0,
|
0,
|
||||||
-1,
|
SpecificInstancesChoice::MAX_DEPTH,
|
||||||
instance_depth,
|
instance_depth,
|
||||||
selection,
|
selection,
|
||||||
[&](const AttributeIDRef &attribute_id,
|
[&](const AttributeIDRef &attribute_id,
|
||||||
@ -881,7 +876,7 @@ void static gather_attributes_for_propagation(
|
|||||||
static OrderedAttributes gather_generic_instance_attributes_to_propagate(
|
static OrderedAttributes gather_generic_instance_attributes_to_propagate(
|
||||||
const bke::GeometrySet &in_geometry_set,
|
const bke::GeometrySet &in_geometry_set,
|
||||||
const RealizeInstancesOptions &options,
|
const RealizeInstancesOptions &options,
|
||||||
bool &r_create_id)
|
const SpecificInstancesChoice &chosen_instances)
|
||||||
{
|
{
|
||||||
Vector<bke::GeometryComponent::Type> src_component_types;
|
Vector<bke::GeometryComponent::Type> src_component_types;
|
||||||
src_component_types.append(bke::GeometryComponent::Type::Instance);
|
src_component_types.append(bke::GeometryComponent::Type::Instance);
|
||||||
@ -890,13 +885,13 @@ static OrderedAttributes gather_generic_instance_attributes_to_propagate(
|
|||||||
gather_attributes_for_propagation(in_geometry_set,
|
gather_attributes_for_propagation(in_geometry_set,
|
||||||
src_component_types,
|
src_component_types,
|
||||||
bke::GeometryComponent::Type::Instance,
|
bke::GeometryComponent::Type::Instance,
|
||||||
options.depths,
|
chosen_instances.depths,
|
||||||
options.selection,
|
chosen_instances.selection,
|
||||||
options.propagation_info,
|
options.propagation_info,
|
||||||
attributes_to_propagate);
|
attributes_to_propagate);
|
||||||
attributes_to_propagate.remove("position");
|
attributes_to_propagate.remove("position");
|
||||||
attributes_to_propagate.remove("radius");
|
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;
|
OrderedAttributes ordered_attributes;
|
||||||
for (const auto item : attributes_to_propagate.items()) {
|
for (const auto item : attributes_to_propagate.items()) {
|
||||||
ordered_attributes.ids.add_new(item.key);
|
ordered_attributes.ids.add_new(item.key);
|
||||||
@ -908,6 +903,7 @@ static OrderedAttributes gather_generic_instance_attributes_to_propagate(
|
|||||||
static OrderedAttributes gather_generic_pointcloud_attributes_to_propagate(
|
static OrderedAttributes gather_generic_pointcloud_attributes_to_propagate(
|
||||||
const bke::GeometrySet &in_geometry_set,
|
const bke::GeometrySet &in_geometry_set,
|
||||||
const RealizeInstancesOptions &options,
|
const RealizeInstancesOptions &options,
|
||||||
|
const SpecificInstancesChoice &chosen_instances,
|
||||||
bool &r_create_radii,
|
bool &r_create_radii,
|
||||||
bool &r_create_id)
|
bool &r_create_id)
|
||||||
{
|
{
|
||||||
@ -919,15 +915,15 @@ static OrderedAttributes gather_generic_pointcloud_attributes_to_propagate(
|
|||||||
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
|
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
|
||||||
// in_geometry_set.gather_attributes_for_propagation(src_component_types,
|
// in_geometry_set.gather_attributes_for_propagation(src_component_types,
|
||||||
// bke::GeometryComponent::Type::PointCloud,
|
// bke::GeometryComponent::Type::PointCloud,
|
||||||
// options.depths,
|
// chosen_instances.depths,
|
||||||
// options.selection,
|
// chosen_instances.selection,
|
||||||
// options.propagation_info,
|
// options.propagation_info,
|
||||||
// attributes_to_propagate);
|
// attributes_to_propagate);
|
||||||
gather_attributes_for_propagation(in_geometry_set,
|
gather_attributes_for_propagation(in_geometry_set,
|
||||||
src_component_types,
|
src_component_types,
|
||||||
bke::GeometryComponent::Type::PointCloud,
|
bke::GeometryComponent::Type::PointCloud,
|
||||||
options.depths,
|
chosen_instances.depths,
|
||||||
options.selection,
|
chosen_instances.selection,
|
||||||
options.propagation_info,
|
options.propagation_info,
|
||||||
attributes_to_propagate);
|
attributes_to_propagate);
|
||||||
attributes_to_propagate.remove("position");
|
attributes_to_propagate.remove("position");
|
||||||
@ -957,11 +953,15 @@ static void gather_pointclouds_to_realize(const bke::GeometrySet &geometry_set,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static AllPointCloudsInfo preprocess_pointclouds(const bke::GeometrySet &geometry_set,
|
static AllPointCloudsInfo preprocess_pointclouds(const bke::GeometrySet &geometry_set,
|
||||||
const RealizeInstancesOptions &options)
|
const RealizeInstancesOptions &options,
|
||||||
|
const SpecificInstancesChoice &chosen_instances)
|
||||||
{
|
{
|
||||||
AllPointCloudsInfo info;
|
AllPointCloudsInfo info;
|
||||||
info.attributes = gather_generic_pointcloud_attributes_to_propagate(
|
info.attributes = gather_generic_pointcloud_attributes_to_propagate(geometry_set,
|
||||||
geometry_set, options, info.create_radius_attribute, info.create_id_attribute);
|
options,
|
||||||
|
chosen_instances,
|
||||||
|
info.create_radius_attribute,
|
||||||
|
info.create_id_attribute);
|
||||||
|
|
||||||
gather_pointclouds_to_realize(geometry_set, info.order);
|
gather_pointclouds_to_realize(geometry_set, info.order);
|
||||||
info.realize_info.reinitialize(info.order.size());
|
info.realize_info.reinitialize(info.order.size());
|
||||||
@ -1000,7 +1000,7 @@ static AllPointCloudsInfo preprocess_pointclouds(const bke::GeometrySet &geometr
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void execute_realize_pointcloud_task(
|
static void execute_realize_pointcloud_task(
|
||||||
const RealizeInstancesOptions &options,
|
bool keep_original_ids,
|
||||||
const RealizePointCloudTask &task,
|
const RealizePointCloudTask &task,
|
||||||
const OrderedAttributes &ordered_attributes,
|
const OrderedAttributes &ordered_attributes,
|
||||||
MutableSpan<GSpanAttributeWriter> dst_attribute_writers,
|
MutableSpan<GSpanAttributeWriter> dst_attribute_writers,
|
||||||
@ -1018,7 +1018,7 @@ static void execute_realize_pointcloud_task(
|
|||||||
/* Create point ids. */
|
/* Create point ids. */
|
||||||
if (!all_dst_ids.is_empty()) {
|
if (!all_dst_ids.is_empty()) {
|
||||||
create_result_ids(
|
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()) {
|
if (!all_dst_radii.is_empty()) {
|
||||||
pointcloud_info.radii.materialize(all_dst_radii.slice(point_slice));
|
pointcloud_info.radii.materialize(all_dst_radii.slice(point_slice));
|
||||||
@ -1035,6 +1035,7 @@ static void execute_realize_pointcloud_task(
|
|||||||
},
|
},
|
||||||
dst_attribute_writers);
|
dst_attribute_writers);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void execute_instances_tasks(
|
static void execute_instances_tasks(
|
||||||
const Span<bke::GeometryComponentPtr> src_components,
|
const Span<bke::GeometryComponentPtr> src_components,
|
||||||
Span<blender::float4x4> src_base_transforms,
|
Span<blender::float4x4> src_base_transforms,
|
||||||
@ -1119,8 +1120,7 @@ static void execute_instances_tasks(
|
|||||||
result.replace_instances(dst_instances.release());
|
result.replace_instances(dst_instances.release());
|
||||||
auto &dst_component = result.get_component_for_write<bke::InstancesComponent>();
|
auto &dst_component = result.get_component_for_write<bke::InstancesComponent>();
|
||||||
Vector<const bke::GeometryComponent *> for_join_attributes;
|
Vector<const bke::GeometryComponent *> for_join_attributes;
|
||||||
for (bke::GeometryComponentPtr compent : src_components)
|
for (bke::GeometryComponentPtr compent : src_components) {
|
||||||
{
|
|
||||||
for_join_attributes.append(compent.get());
|
for_join_attributes.append(compent.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1128,7 +1128,7 @@ static void execute_instances_tasks(
|
|||||||
for_join_attributes, dst_component, {"position", ".reference_index", "instance_transform"});
|
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 AllPointCloudsInfo &all_pointclouds_info,
|
||||||
const Span<RealizePointCloudTask> tasks,
|
const Span<RealizePointCloudTask> tasks,
|
||||||
const OrderedAttributes &ordered_attributes,
|
const OrderedAttributes &ordered_attributes,
|
||||||
@ -1180,7 +1180,7 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti
|
|||||||
threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) {
|
threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) {
|
||||||
for (const int task_index : task_range) {
|
for (const int task_index : task_range) {
|
||||||
const RealizePointCloudTask &task = tasks[task_index];
|
const RealizePointCloudTask &task = tasks[task_index];
|
||||||
execute_realize_pointcloud_task(options,
|
execute_realize_pointcloud_task(keep_original_ids,
|
||||||
task,
|
task,
|
||||||
ordered_attributes,
|
ordered_attributes,
|
||||||
dst_attribute_writers,
|
dst_attribute_writers,
|
||||||
@ -1208,6 +1208,7 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti
|
|||||||
static OrderedAttributes gather_generic_mesh_attributes_to_propagate(
|
static OrderedAttributes gather_generic_mesh_attributes_to_propagate(
|
||||||
const bke::GeometrySet &in_geometry_set,
|
const bke::GeometrySet &in_geometry_set,
|
||||||
const RealizeInstancesOptions &options,
|
const RealizeInstancesOptions &options,
|
||||||
|
const SpecificInstancesChoice &chosen_instances,
|
||||||
bool &r_create_id,
|
bool &r_create_id,
|
||||||
bool &r_create_material_index)
|
bool &r_create_material_index)
|
||||||
{
|
{
|
||||||
@ -1221,8 +1222,8 @@ static OrderedAttributes gather_generic_mesh_attributes_to_propagate(
|
|||||||
gather_attributes_for_propagation(in_geometry_set,
|
gather_attributes_for_propagation(in_geometry_set,
|
||||||
src_component_types,
|
src_component_types,
|
||||||
bke::GeometryComponent::Type::Mesh,
|
bke::GeometryComponent::Type::Mesh,
|
||||||
options.depths,
|
chosen_instances.depths,
|
||||||
options.selection,
|
chosen_instances.selection,
|
||||||
options.propagation_info,
|
options.propagation_info,
|
||||||
attributes_to_propagate);
|
attributes_to_propagate);
|
||||||
attributes_to_propagate.remove("position");
|
attributes_to_propagate.remove("position");
|
||||||
@ -1255,11 +1256,16 @@ static void gather_meshes_to_realize(const bke::GeometrySet &geometry_set,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static AllMeshesInfo preprocess_meshes(const bke::GeometrySet &geometry_set,
|
static AllMeshesInfo preprocess_meshes(const bke::GeometrySet &geometry_set,
|
||||||
const RealizeInstancesOptions &options)
|
const RealizeInstancesOptions &options,
|
||||||
|
const SpecificInstancesChoice &chosen_instances)
|
||||||
{
|
{
|
||||||
AllMeshesInfo info;
|
AllMeshesInfo info;
|
||||||
info.attributes = gather_generic_mesh_attributes_to_propagate(
|
info.attributes = gather_generic_mesh_attributes_to_propagate(
|
||||||
geometry_set, options, info.create_id_attribute, info.create_material_index_attribute);
|
geometry_set,
|
||||||
|
options,
|
||||||
|
chosen_instances,
|
||||||
|
info.create_id_attribute,
|
||||||
|
info.create_material_index_attribute);
|
||||||
|
|
||||||
gather_meshes_to_realize(geometry_set, info.order);
|
gather_meshes_to_realize(geometry_set, info.order);
|
||||||
for (const Mesh *mesh : info.order) {
|
for (const Mesh *mesh : info.order) {
|
||||||
@ -1337,7 +1343,7 @@ static AllMeshesInfo preprocess_meshes(const bke::GeometrySet &geometry_set,
|
|||||||
return info;
|
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 RealizeMeshTask &task,
|
||||||
const OrderedAttributes &ordered_attributes,
|
const OrderedAttributes &ordered_attributes,
|
||||||
MutableSpan<GSpanAttributeWriter> dst_attribute_writers,
|
MutableSpan<GSpanAttributeWriter> dst_attribute_writers,
|
||||||
@ -1421,7 +1427,7 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!all_dst_vertex_ids.is_empty()) {
|
if (!all_dst_vertex_ids.is_empty()) {
|
||||||
create_result_ids(options,
|
create_result_ids(keep_original_ids,
|
||||||
mesh_info.stored_vertex_ids,
|
mesh_info.stored_vertex_ids,
|
||||||
task.id,
|
task.id,
|
||||||
all_dst_vertex_ids.slice(task.start_indices.vertex, mesh.verts_num));
|
all_dst_vertex_ids.slice(task.start_indices.vertex, mesh.verts_num));
|
||||||
@ -1449,7 +1455,7 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options,
|
|||||||
dst_attribute_writers);
|
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 AllMeshesInfo &all_meshes_info,
|
||||||
const Span<RealizeMeshTask> tasks,
|
const Span<RealizeMeshTask> tasks,
|
||||||
const OrderedAttributes &ordered_attributes,
|
const OrderedAttributes &ordered_attributes,
|
||||||
@ -1532,7 +1538,7 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options,
|
|||||||
threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) {
|
threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) {
|
||||||
for (const int task_index : task_range) {
|
for (const int task_index : task_range) {
|
||||||
const RealizeMeshTask &task = tasks[task_index];
|
const RealizeMeshTask &task = tasks[task_index];
|
||||||
execute_realize_mesh_task(options,
|
execute_realize_mesh_task(keep_original_ids,
|
||||||
task,
|
task,
|
||||||
ordered_attributes,
|
ordered_attributes,
|
||||||
dst_attribute_writers,
|
dst_attribute_writers,
|
||||||
@ -1573,6 +1579,7 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options,
|
|||||||
static OrderedAttributes gather_generic_curve_attributes_to_propagate(
|
static OrderedAttributes gather_generic_curve_attributes_to_propagate(
|
||||||
const bke::GeometrySet &in_geometry_set,
|
const bke::GeometrySet &in_geometry_set,
|
||||||
const RealizeInstancesOptions &options,
|
const RealizeInstancesOptions &options,
|
||||||
|
const SpecificInstancesChoice &chosen_instances,
|
||||||
bool &r_create_id)
|
bool &r_create_id)
|
||||||
{
|
{
|
||||||
Vector<bke::GeometryComponent::Type> src_component_types;
|
Vector<bke::GeometryComponent::Type> src_component_types;
|
||||||
@ -1585,8 +1592,8 @@ static OrderedAttributes gather_generic_curve_attributes_to_propagate(
|
|||||||
gather_attributes_for_propagation(in_geometry_set,
|
gather_attributes_for_propagation(in_geometry_set,
|
||||||
src_component_types,
|
src_component_types,
|
||||||
bke::GeometryComponent::Type::Curve,
|
bke::GeometryComponent::Type::Curve,
|
||||||
options.depths,
|
chosen_instances.depths,
|
||||||
options.selection,
|
chosen_instances.selection,
|
||||||
options.propagation_info,
|
options.propagation_info,
|
||||||
attributes_to_propagate);
|
attributes_to_propagate);
|
||||||
attributes_to_propagate.remove("position");
|
attributes_to_propagate.remove("position");
|
||||||
@ -1621,11 +1628,12 @@ static void gather_curves_to_realize(const bke::GeometrySet &geometry_set,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static AllCurvesInfo preprocess_curves(const bke::GeometrySet &geometry_set,
|
static AllCurvesInfo preprocess_curves(const bke::GeometrySet &geometry_set,
|
||||||
const RealizeInstancesOptions &options)
|
const RealizeInstancesOptions &options,
|
||||||
|
const SpecificInstancesChoice &chosen_instances)
|
||||||
{
|
{
|
||||||
AllCurvesInfo info;
|
AllCurvesInfo info;
|
||||||
info.attributes = gather_generic_curve_attributes_to_propagate(
|
info.attributes = gather_generic_curve_attributes_to_propagate(
|
||||||
geometry_set, options, info.create_id_attribute);
|
geometry_set, options, chosen_instances, info.create_id_attribute);
|
||||||
|
|
||||||
gather_curves_to_realize(geometry_set, info.order);
|
gather_curves_to_realize(geometry_set, info.order);
|
||||||
info.realize_info.reinitialize(info.order.size());
|
info.realize_info.reinitialize(info.order.size());
|
||||||
@ -1684,7 +1692,7 @@ static AllCurvesInfo preprocess_curves(const bke::GeometrySet &geometry_set,
|
|||||||
return info;
|
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 AllCurvesInfo &all_curves_info,
|
||||||
const RealizeCurveTask &task,
|
const RealizeCurveTask &task,
|
||||||
const OrderedAttributes &ordered_attributes,
|
const OrderedAttributes &ordered_attributes,
|
||||||
@ -1767,7 +1775,7 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options,
|
|||||||
|
|
||||||
if (!all_dst_ids.is_empty()) {
|
if (!all_dst_ids.is_empty()) {
|
||||||
create_result_ids(
|
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(
|
copy_generic_attributes_to_result(
|
||||||
@ -1788,7 +1796,7 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options,
|
|||||||
dst_attribute_writers);
|
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 AllCurvesInfo &all_curves_info,
|
||||||
const Span<RealizeCurveTask> tasks,
|
const Span<RealizeCurveTask> tasks,
|
||||||
const OrderedAttributes &ordered_attributes,
|
const OrderedAttributes &ordered_attributes,
|
||||||
@ -1867,7 +1875,7 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
|
|||||||
threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) {
|
threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) {
|
||||||
for (const int task_index : task_range) {
|
for (const int task_index : task_range) {
|
||||||
const RealizeCurveTask &task = tasks[task_index];
|
const RealizeCurveTask &task = tasks[task_index];
|
||||||
execute_realize_curve_task(options,
|
execute_realize_curve_task(keep_original_ids,
|
||||||
all_curves_info,
|
all_curves_info,
|
||||||
task,
|
task,
|
||||||
ordered_attributes,
|
ordered_attributes,
|
||||||
@ -1947,6 +1955,23 @@ static void propagate_instances_to_keep(
|
|||||||
|
|
||||||
bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
||||||
const RealizeInstancesOptions &options)
|
const RealizeInstancesOptions &options)
|
||||||
|
{
|
||||||
|
if (!geometry_set.has_instances()) {
|
||||||
|
return geometry_set;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpecificInstancesChoice all_instances;
|
||||||
|
all_instances.depths = VArray<int>::ForSingle(SpecificInstancesChoice::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 SpecificInstancesChoice &chosen_instances)
|
||||||
{
|
{
|
||||||
/* The algorithm works in three steps:
|
/* The algorithm works in three steps:
|
||||||
* 1. Preprocess each unique geometry that is instanced (e.g. each `Mesh`).
|
* 1. Preprocess each unique geometry that is instanced (e.g. each `Mesh`).
|
||||||
@ -1961,18 +1986,18 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
|||||||
|
|
||||||
bke::GeometrySet not_to_realize_set;
|
bke::GeometrySet not_to_realize_set;
|
||||||
propagate_instances_to_keep(
|
propagate_instances_to_keep(
|
||||||
geometry_set, options.selection, not_to_realize_set, options.propagation_info);
|
geometry_set, chosen_instances.selection, not_to_realize_set, options.propagation_info);
|
||||||
|
|
||||||
if (options.keep_original_ids) {
|
if (options.keep_original_ids) {
|
||||||
remove_id_attribute_from_instances(geometry_set);
|
remove_id_attribute_from_instances(geometry_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
AllPointCloudsInfo all_pointclouds_info = preprocess_pointclouds(geometry_set, options);
|
AllPointCloudsInfo all_pointclouds_info = preprocess_pointclouds(
|
||||||
AllMeshesInfo all_meshes_info = preprocess_meshes(geometry_set, options);
|
geometry_set, options, chosen_instances);
|
||||||
AllCurvesInfo all_curves_info = preprocess_curves(geometry_set, options);
|
AllMeshesInfo all_meshes_info = preprocess_meshes(geometry_set, options, chosen_instances);
|
||||||
bool tmp = true;
|
AllCurvesInfo all_curves_info = preprocess_curves(geometry_set, options, chosen_instances);
|
||||||
OrderedAttributes all_instance_attributes = gather_generic_instance_attributes_to_propagate(
|
OrderedAttributes all_instance_attributes = gather_generic_instance_attributes_to_propagate(
|
||||||
geometry_set, options, tmp);
|
geometry_set, options, chosen_instances);
|
||||||
|
|
||||||
Vector<std::unique_ptr<GArray<>>> temporary_arrays;
|
Vector<std::unique_ptr<GArray<>>> temporary_arrays;
|
||||||
const bool create_id_attribute = all_pointclouds_info.create_id_attribute ||
|
const bool create_id_attribute = all_pointclouds_info.create_id_attribute ||
|
||||||
@ -1983,8 +2008,8 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
|||||||
all_curves_info,
|
all_curves_info,
|
||||||
all_instance_attributes,
|
all_instance_attributes,
|
||||||
create_id_attribute,
|
create_id_attribute,
|
||||||
options.selection,
|
chosen_instances.selection,
|
||||||
options.depths,
|
chosen_instances.depths,
|
||||||
temporary_arrays};
|
temporary_arrays};
|
||||||
|
|
||||||
bke::GeometrySet new_geometry_set;
|
bke::GeometrySet new_geometry_set;
|
||||||
@ -1999,25 +2024,33 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
|||||||
gather_info.instances.attribute_fallback.append((gather_info.instances_attriubutes.size()));
|
gather_info.instances.attribute_fallback.append((gather_info.instances_attriubutes.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
gather_realize_tasks_recursive(gather_info, 0, -1, geometry_set, transform, attribute_fallbacks);
|
gather_realize_tasks_recursive(gather_info,
|
||||||
|
0,
|
||||||
|
SpecificInstancesChoice::MAX_DEPTH,
|
||||||
|
geometry_set,
|
||||||
|
transform,
|
||||||
|
attribute_fallbacks);
|
||||||
|
|
||||||
execute_instances_tasks(gather_info.instances.instances_components_to_merge,
|
execute_instances_tasks(gather_info.instances.instances_components_to_merge,
|
||||||
gather_info.instances.instances_components_transforms,
|
gather_info.instances.instances_components_transforms,
|
||||||
all_instance_attributes,
|
all_instance_attributes,
|
||||||
gather_info.instances.attribute_fallback,
|
gather_info.instances.attribute_fallback,
|
||||||
new_geometry_set);
|
new_geometry_set);
|
||||||
execute_realize_pointcloud_tasks(options,
|
|
||||||
|
execute_realize_pointcloud_tasks(options.keep_original_ids,
|
||||||
all_pointclouds_info,
|
all_pointclouds_info,
|
||||||
gather_info.r_tasks.pointcloud_tasks,
|
gather_info.r_tasks.pointcloud_tasks,
|
||||||
all_pointclouds_info.attributes,
|
all_pointclouds_info.attributes,
|
||||||
new_geometry_set);
|
new_geometry_set);
|
||||||
execute_realize_mesh_tasks(options,
|
|
||||||
|
execute_realize_mesh_tasks(options.keep_original_ids,
|
||||||
all_meshes_info,
|
all_meshes_info,
|
||||||
gather_info.r_tasks.mesh_tasks,
|
gather_info.r_tasks.mesh_tasks,
|
||||||
all_meshes_info.attributes,
|
all_meshes_info.attributes,
|
||||||
all_meshes_info.materials,
|
all_meshes_info.materials,
|
||||||
new_geometry_set);
|
new_geometry_set);
|
||||||
execute_realize_curve_tasks(options,
|
|
||||||
|
execute_realize_curve_tasks(options.keep_original_ids,
|
||||||
all_curves_info,
|
all_curves_info,
|
||||||
gather_info.r_tasks.curve_tasks,
|
gather_info.r_tasks.curve_tasks,
|
||||||
all_curves_info.attributes,
|
all_curves_info.attributes,
|
||||||
|
@ -19,13 +19,13 @@ static void node_declare(NodeDeclarationBuilder &b)
|
|||||||
.default_value(true)
|
.default_value(true)
|
||||||
.hide_value()
|
.hide_value()
|
||||||
.supports_field()
|
.supports_field()
|
||||||
.description(("Which top-level instances to realize"));
|
.description("Which top-level instances to realize");
|
||||||
b.add_input<decl::Bool>("Realize All")
|
b.add_input<decl::Bool>("Realize All")
|
||||||
.default_value(true)
|
.default_value(true)
|
||||||
.supports_field()
|
.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(
|
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();
|
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));
|
params.set_output("Geometry", std::move(geometry_set));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GeometryComponentEditData::remember_deformed_positions_if_necessary(geometry_set);
|
GeometryComponentEditData::remember_deformed_positions_if_necessary(geometry_set);
|
||||||
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
|
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
|
||||||
Field<bool> realize_all_filed = params.extract_input<Field<bool>>("Realize All");
|
Field<bool> realize_all_filed = params.extract_input<Field<bool>>("Realize All");
|
||||||
@ -43,35 +44,35 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||||||
|
|
||||||
static auto depth_override = mf::build::SI2_SO<int, bool, int>(
|
static auto depth_override = mf::build::SI2_SO<int, bool, int>(
|
||||||
"depth_override",
|
"depth_override",
|
||||||
[](int value, bool realize) { return realize ? -1 : std::max(value, 0); },
|
[](int value, bool realize) { return realize ? -1 : value; },
|
||||||
aryeramaty marked this conversation as resolved
Outdated
|
|||||||
mf::build::exec_presets::AllSpanOrSingle());
|
mf::build::exec_presets::AllSpanOrSingle());
|
||||||
|
|
||||||
static auto selction_override = mf::build::SI2_SO<int, bool, bool>(
|
static auto selection_override = mf::build::SI2_SO<int, bool, bool>(
|
||||||
"selction_override",
|
"selection_override",
|
||||||
[](int value, bool selection) { return value == 0 ? false : selection; },
|
[](int value, bool selection) { return value == 0 ? false : selection; },
|
||||||
mf::build::exec_presets::AllSpanOrSingle());
|
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(
|
Field<int> depth_field_overrided(
|
||||||
FieldOperation::Create(depth_override, {depth_field, realize_all_filed}));
|
FieldOperation::Create(depth_override, {depth_field, realize_all_filed}));
|
||||||
Field<bool> selection_field_overrided(
|
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.set_selection(selection_field_overrided);
|
||||||
evaluator.evaluate();
|
evaluator.evaluate();
|
||||||
const VArray<int> depths = evaluator.get_evaluated<int>(0);
|
|
||||||
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
|
geometry::SpecificInstancesChoice chosen_instances;
|
||||||
David-Haver marked this conversation as resolved
Outdated
Arye Ramaty
commented
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.
David-Haver
commented
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.
|
|||||||
|
chosen_instances.depths = evaluator.get_evaluated<int>(evaluated_depth_index);
|
||||||
|
chosen_instances.selection = evaluator.get_evaluated_selection_as_mask();
|
||||||
|
|
||||||
geometry::RealizeInstancesOptions options;
|
geometry::RealizeInstancesOptions options;
|
||||||
options.keep_original_ids = false;
|
options.keep_original_ids = false;
|
||||||
options.realize_instance_attributes = true;
|
options.realize_instance_attributes = true;
|
||||||
options.propagation_info = params.get_output_propagation_info("Geometry");
|
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);
|
||||||
params.set_output("Geometry", std::move(geometry_set));
|
params.set_output("Geometry", std::move(geometry_set));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user
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.
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