Fix: Attribute Transfer node does not work with a single index

Differential Revision: https://developer.blender.org/D13194
This commit is contained in:
Jacques Lucke 2021-11-11 19:49:20 +01:00
parent f3bdabbe24
commit bd734cc441
Notes: blender-bot 2023-02-14 03:44:41 +01:00
Referenced by issue #93256, Geometry Nodes: Instances to points doesn't generate any points
Referenced by issue #93114, Blender 3.0b becomes laggy after being open for some time
Referenced by issue #93095, EEVEE motion blur in cryptomatte
Referenced by issue #91358, Evee crashes when rendering
1 changed files with 55 additions and 65 deletions

View File

@ -305,7 +305,7 @@ void copy_with_indices(const VArray<T> &src,
template<typename T>
void copy_with_indices_clamped(const VArray<T> &src,
const IndexMask mask,
const Span<int> indices,
const VArray<int> &indices,
const MutableSpan<T> dst)
{
if (src.is_empty()) {
@ -587,18 +587,11 @@ class NearestTransferFunction : public fn::MultiFunction {
}
};
static const GeometryComponent *find_best_match_component(const GeometrySet &geometry,
const GeometryComponentType type,
const AttributeDomain domain)
static const GeometryComponent *find_target_component(const GeometrySet &geometry,
const AttributeDomain domain)
{
/* Prefer transferring from the same component type, if it exists. */
if (component_is_available(geometry, type, domain)) {
return geometry.get_component_for_read(type);
}
/* If there is no component of the same type, choose the other component based on a consistent
* order, rather than some more complicated heuristic. This is the same order visible in the
* spreadsheet and used in the ray-cast node. */
/* Choose the other component based on a consistent order, rather than some more complicated
* heuristic. This is the same order visible in the spreadsheet and used in the ray-cast node. */
static const Array<GeometryComponentType> supported_types = {
GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
for (const GeometryComponentType src_type : supported_types) {
@ -611,76 +604,71 @@ static const GeometryComponent *find_best_match_component(const GeometrySet &geo
}
/**
* Use a #FieldInput because it's necessary to know the field context in order to choose the
* corresponding component type from the input geometry, and only a #FieldInput receives the
* evaluation context to provide its data.
*
* The index-based transfer theoretically does not need realized data when there is only one
* instance geometry set in the target. A future optimization could be removing that limitation
* internally.
*/
class IndexTransferFieldInput : public FieldInput {
class IndexTransferFunction : public fn::MultiFunction {
GeometrySet src_geometry_;
GField src_field_;
Field<int> index_field_;
AttributeDomain domain_;
fn::MFSignature signature_;
std::optional<GeometryComponentFieldContext> geometry_context_;
std::unique_ptr<FieldEvaluator> evaluator_;
const GVArray *src_data_ = nullptr;
public:
IndexTransferFieldInput(GeometrySet geometry,
GField src_field,
Field<int> index_field,
const AttributeDomain domain)
: FieldInput(src_field.cpp_type(), "Attribute Transfer node"),
src_geometry_(std::move(geometry)),
src_field_(std::move(src_field)),
index_field_(std::move(index_field)),
domain_(domain)
IndexTransferFunction(GeometrySet geometry, GField src_field, const AttributeDomain domain)
: src_geometry_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain)
{
src_geometry_.ensure_owns_direct_data();
category_ = Category::Generated;
signature_ = this->create_signature();
this->set_signature(&signature_);
this->evaluate_field();
}
const GVArray *get_varray_for_context(const FieldContext &context,
const IndexMask mask,
ResourceScope &scope) const final
fn::MFSignature create_signature()
{
const GeometryComponentFieldContext *geometry_context =
dynamic_cast<const GeometryComponentFieldContext *>(&context);
if (geometry_context == nullptr) {
return nullptr;
}
fn::MFSignatureBuilder signature{"Attribute Transfer Index"};
signature.single_input<int>("Index");
signature.single_output("Attribute", src_field_.cpp_type());
return signature.build();
}
FieldEvaluator index_evaluator{*geometry_context, &mask};
index_evaluator.add(index_field_);
index_evaluator.evaluate();
const VArray<int> &index_varray = index_evaluator.get_evaluated<int>(0);
/* The index virtual array is expected to be a span, since transferring the same index for
* every element is not very useful. */
VArray_Span<int> indices{index_varray};
const GeometryComponent *component = find_best_match_component(
src_geometry_, geometry_context->geometry_component().type(), domain_);
void evaluate_field()
{
const GeometryComponent *component = find_target_component(src_geometry_, domain_);
if (component == nullptr) {
return nullptr;
return;
}
const int domain_size = component->attribute_domain_size(domain_);
geometry_context_.emplace(GeometryComponentFieldContext(*component, domain_));
evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_size);
evaluator_->add(src_field_);
evaluator_->evaluate();
src_data_ = &evaluator_->get_evaluated(0);
}
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
{
const VArray<int> &indices = params.readonly_single_input<int>(0, "Index");
GMutableSpan dst = params.uninitialized_single_output(1, "Attribute");
const CPPType &type = dst.type();
if (src_data_ == nullptr) {
type.fill_construct_indices(type.default_value(), dst.data(), mask);
return;
}
GeometryComponentFieldContext target_context{*component, domain_};
/* A potential improvement is to only copy the necessary values
* based on the indices retrieved from the index input field. */
FieldEvaluator target_evaluator{target_context, component->attribute_domain_size(domain_)};
target_evaluator.add(src_field_);
target_evaluator.evaluate();
const GVArray &src_data = target_evaluator.get_evaluated(0);
GArray dst(src_field_.cpp_type(), mask.min_array_size());
attribute_math::convert_to_static_type(src_data.type(), [&](auto dummy) {
attribute_math::convert_to_static_type(type, [&](auto dummy) {
using T = decltype(dummy);
GVArray_Typed<T> src{src_data};
copy_with_indices_clamped(*src, mask, indices, dst.as_mutable_span().typed<T>());
GVArray_Typed<T> src{*src_data_};
copy_with_indices_clamped(*src, mask, indices, dst.typed<T>());
});
return &scope.construct<fn::GVArray_For_GArray>(std::move(dst));
}
};
@ -792,9 +780,11 @@ static void geo_node_transfer_attribute_exec(GeoNodeExecParams params)
}
case GEO_NODE_ATTRIBUTE_TRANSFER_INDEX: {
Field<int> indices = params.extract_input<Field<int>>("Index");
std::shared_ptr<FieldInput> input = std::make_shared<IndexTransferFieldInput>(
std::move(geometry), std::move(field), std::move(indices), domain);
output_field = GField(std::move(input));
auto fn = std::make_unique<IndexTransferFunction>(
std::move(geometry), std::move(field), domain);
auto op = std::make_shared<FieldOperation>(
FieldOperation(std::move(fn), {std::move(indices)}));
output_field = GField(std::move(op));
break;
}
}