Page MenuHome
Paste P2734

Duplicate Plan #2
ActivePublic

Authored by Johnny Matthews (guitargeek) on Jan 13 2022, 4:42 PM.
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BLI_map.hh"
#include "BLI_noise.hh"
#include "BLI_span.hh"
#include "BLI_task.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
#include "BKE_attribute_math.hh"
#include "BKE_mesh.h"
#include "BKE_pointcloud.h"
#include "BKE_spline.hh"
#include "node_geometry_util.hh"
#include "UI_interface.h"
#include "UI_resources.h"
namespace blender::nodes::node_geo_duplicate_elements_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Geometry"));
b.add_input<decl::Bool>(N_("Selection")).hide_value().default_value(true).supports_field();
b.add_input<decl::Int>(N_("Amount")).min(0).default_value(1).supports_field();
b.add_output<decl::Geometry>(N_("Geometry"));
b.add_output<decl::Int>(N_("Copy Index"))
.field_source()
.description(N_("The index of the duplicate of a specific element"));
}
static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryDuplicateElements *data = MEM_cnew<NodeGeometryDuplicateElements>(__func__);
data->domain = ATTR_DOMAIN_POINT;
node->storage = data;
}
static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
}
struct IndexAttributes {
StrongAnonymousAttributeID copy_index;
};
struct DomainOffsets {
Span<int> point;
Span<int> edge;
Span<int> face;
Span<int> corner;
Span<int> curve;
Span<int> instance;
};
static void gather_attributes_and_skip(GeometrySet &geometry_set,
const GeometryComponentType component_type,
Span<std::string> skip_attributes,
const bool include_instances,
Map<AttributeIDRef, AttributeKind> &r_gathered_attributes)
{
geometry_set.gather_attributes_for_propagation(
{component_type}, component_type, include_instances, r_gathered_attributes);
for (std::string id : skip_attributes) {
r_gathered_attributes.remove(id);
}
};
static void create_copy_id_attribute(GeometryComponent &component,
const AttributeDomain output_domain,
IndexAttributes attributes,
const VArray<int> counts,
const Span<int> offsets)
{
OutputAttribute_Typed<int> copy_attribute = component.attribute_try_get_for_output_only<int>(
attributes.copy_index.get(), output_domain);
MutableSpan<int> copy_indices = copy_attribute.as_span();
for (const int i_offset : offsets.index_range()) {
for (const int i_duplicate : IndexRange(counts[i_offset])) {
copy_indices[offsets[i_offset] + i_duplicate] = i_duplicate;
}
}
copy_attribute.save();
}
static void copy_attributes_without_id(GeometrySet &geometry_set,
const GeometryComponentType component_type,
const bool include_instances,
const IndexMask selection,
const VArray<int> counts,
const DomainOffsets &offsets,
Vector<std::string> attributes_to_ignore,
const GeometryComponent &src_component,
GeometryComponent &dst_component)
{
Map<AttributeIDRef, AttributeKind> gathered_attributes;
attributes_to_ignore.append("id");
gather_attributes_and_skip(geometry_set,
component_type,
attributes_to_ignore.as_span(),
include_instances,
gathered_attributes);
for (Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
if (!src_attribute) {
continue;
}
AttributeDomain out_domain = src_attribute.domain;
const CustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
attribute_id, out_domain, data_type);
if (!dst_attribute) {
continue;
}
const int output_size = dst_component.attribute_domain_size(out_domain);
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
VArray_Span<T> src{src_attribute.varray.typed<T>()};
MutableSpan<T> dst = dst_attribute.as_span<T>();
switch (out_domain) {
case ATTR_DOMAIN_POINT:
BLI_assert(output_size == offsets.point.last());
break;
case ATTR_DOMAIN_EDGE:
BLI_assert(output_size == offsets.edge.last());
break;
case ATTR_DOMAIN_FACE:
BLI_assert(output_size == offsets.face.last());
break;
case ATTR_DOMAIN_CORNER:
BLI_assert(output_size == offsets.corner.last());
break;
case ATTR_DOMAIN_CURVE:
BLI_assert(output_size == offsets.curve.last());
break;
case ATTR_DOMAIN_INSTANCE:
BLI_assert(output_size == offsets.instance.last());
for (const int i : IndexRange(offsets.point.size())) {
dst.slice(offsets.instance[i], counts[i]).fill(src[i]);
}
break;
default:
break;
}
});
dst_attribute.save();
}
}
static void duplicate_instances(GeometrySet &geometry_set,
const Field<int> &count_field,
const Field<bool> &selection_field,
IndexAttributes &attributes)
{
if (!geometry_set.has_instances()) {
geometry_set.clear();
return;
}
const InstancesComponent &src_instances =
*geometry_set.get_component_for_read<InstancesComponent>();
const int domain_size = src_instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE);
GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE};
FieldEvaluator evaluator{field_context, domain_size};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
evaluator.evaluate();
IndexMask selection = evaluator.get_evaluated_selection_as_mask();
const VArray<int> counts = evaluator.get_evaluated<int>(0);
Array<int> offsets(selection.size() + 1);
int dst_size = 0;
for (const int i_selection : selection.index_range()) {
offsets[i_selection] = dst_size;
dst_size += std::max(0, counts[selection[i_selection]]);
}
if (dst_size == 0) {
geometry_set.clear();
return;
}
GeometrySet instances_geometry;
InstancesComponent &dst_instances =
instances_geometry.get_component_for_write<InstancesComponent>();
dst_instances.resize(dst_size);
for (const int i_selection : selection.index_range()) {
const int count = offsets[i_selection + 1] - offsets[i_selection];
if (count == 0) {
continue;
}
const int old_handle = src_instances.instance_reference_handles()[i_selection];
const InstanceReference reference = src_instances.references()[old_handle];
const int new_handle = dst_instances.add_reference(reference);
const float4x4 transform = src_instances.instance_transforms()[i_selection];
dst_instances.instance_transforms().slice(offsets[i_selection], count).fill(transform);
dst_instances.instance_reference_handles().slice(offsets[i_selection], count).fill(new_handle);
}
DomainOffsets offset_bundle;
offset_bundle.instance = offsets.as_span();
copy_attributes_without_id(geometry_set,
GEO_COMPONENT_TYPE_INSTANCES,
true,
selection,
counts,
offset_bundle,
{},
src_instances,
dst_instances);
if (attributes.copy_index) {
create_copy_id_attribute(
dst_instances, ATTR_DOMAIN_INSTANCE, attributes, counts, offset_bundle.instance);
}
geometry_set.remove(GEO_COMPONENT_TYPE_INSTANCES);
geometry_set.add(dst_instances);
}
/** \} */
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
NodeGeometryDuplicateElements *data = static_cast<NodeGeometryDuplicateElements *>(
params.node().storage);
AttributeDomain duplicate_domain = AttributeDomain(data->domain);
Field<int> count_field = params.extract_input<Field<int>>("Amount");
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
IndexAttributes attributes;
if (params.output_is_required("Copy Index")) {
attributes.copy_index = StrongAnonymousAttributeID("copy_index");
}
if (duplicate_domain == ATTR_DOMAIN_INSTANCE) {
geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
duplicate_instances(geometry_set, count_field, selection_field, attributes);
}
if (geometry_set.is_empty()) {
params.set_default_remaining_outputs();
return;
}
if (attributes.copy_index) {
params.set_output("Copy Index",
AnonymousAttributeFieldInput::Create<int>(std::move(attributes.copy_index),
params.attribute_producer_name()));
}
params.set_output("Geometry", geometry_set);
}
} // namespace blender::nodes::node_geo_duplicate_elements_cc
void register_node_type_geo_duplicate_elements()
{
namespace file_ns = blender::nodes::node_geo_duplicate_elements_cc;
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_DUPLICATE_ELEMENTS, "Duplicate Elements", NODE_CLASS_GEOMETRY);
node_type_storage(&ntype,
"NodeGeometryDuplicateElements",
node_free_standard_storage,
node_copy_standard_storage);
node_type_init(&ntype, file_ns::node_init);
ntype.draw_buttons = file_ns::node_layout;
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}