Files
test2/source/blender/geometry/intern/mix_geometries.cc
Hans Goudey 1f92fd7577 Refactor: Use AttrType instead of CustomData type in attribute API
Change `eCustomDataType` to `bke::AttrType` for uses of the attribute
API (the `AttributeAccessor` one anyway). I didn't touch any values that
might be saved in files; those should be handled on a case by case basis.

Part of #122398

Pull Request: https://projects.blender.org/blender/blender/pulls/141301
2025-07-01 22:14:26 +02:00

206 lines
7.1 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "GEO_mix_geometries.hh"
#include "BKE_attribute.hh"
#include "BKE_attribute_math.hh"
#include "BKE_curves.hh"
#include "BKE_instances.hh"
#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
namespace blender::geometry {
static bool sharing_info_equal(const ImplicitSharingInfo *a, const ImplicitSharingInfo *b)
{
if (!a || !b) {
return false;
}
return a == b;
}
template<typename T>
void mix_with_indices(MutableSpan<T> a,
const VArray<T> &b,
const Span<int> index_map,
const float factor)
{
threading::parallel_for(a.index_range(), 1024, [&](const IndexRange range) {
devirtualize_varray(b, [&](const auto b) {
for (const int i : range) {
if (index_map[i] != -1) {
a[i] = bke::attribute_math::mix2(factor, a[i], b[index_map[i]]);
}
}
});
});
}
static void mix_with_indices(GMutableSpan a,
const GVArray &b,
const Span<int> index_map,
const float factor)
{
bke::attribute_math::convert_to_static_type(a.type(), [&](auto dummy) {
using T = decltype(dummy);
mix_with_indices(a.typed<T>(), b.typed<T>(), index_map, factor);
});
}
template<typename T> static void mix(MutableSpan<T> a, const VArray<T> &b, const float factor)
{
threading::parallel_for(a.index_range(), 1024, [&](const IndexRange range) {
devirtualize_varray(b, [&](const auto b) {
for (const int i : range) {
a[i] = bke::attribute_math::mix2(factor, a[i], b[i]);
}
});
});
}
static void mix(GMutableSpan a, const GVArray &b, const float factor)
{
bke::attribute_math::convert_to_static_type(a.type(), [&](auto dummy) {
using T = decltype(dummy);
mix(a.typed<T>(), b.typed<T>(), factor);
});
}
static void mix_attributes(bke::MutableAttributeAccessor attributes_a,
const bke::AttributeAccessor b_attributes,
const Span<int> index_map,
const bke::AttrDomain mix_domain,
const float factor,
const Set<std::string> &names_to_skip = {})
{
Set<StringRefNull> ids = attributes_a.all_ids();
ids.remove("id");
for (const StringRef name : names_to_skip) {
ids.remove_as(name);
}
for (const StringRef id : ids) {
const bke::GAttributeReader attribute_a = attributes_a.lookup(id);
const bke::AttrDomain domain = attribute_a.domain;
if (domain != mix_domain) {
continue;
}
const bke::AttrType type = bke::cpp_type_to_attribute_type(attribute_a.varray.type());
if (ELEM(type, bke::AttrType::String, bke::AttrType::Bool)) {
/* String attributes can't be mixed, and there's no point in mixing boolean attributes. */
continue;
}
const bke::GAttributeReader attribute_b = b_attributes.lookup(id, attribute_a.domain, type);
if (sharing_info_equal(attribute_a.sharing_info, attribute_b.sharing_info)) {
continue;
}
if (!index_map.is_empty()) {
bke::GSpanAttributeWriter dst = attributes_a.lookup_for_write_span(id);
/* If there's an ID attribute, use its values to mix with potentially changed indices. */
mix_with_indices(dst.span, *attribute_b, index_map, factor);
dst.finish();
}
else if (attributes_a.domain_size(domain) == b_attributes.domain_size(domain)) {
bke::GSpanAttributeWriter dst = attributes_a.lookup_for_write_span(id);
/* With no ID attribute to find matching elements, we can only support mixing when the domain
* size (topology) is the same. Other options like mixing just the start of arrays might work
* too, but give bad results too. */
mix(dst.span, attribute_b.varray, factor);
dst.finish();
}
}
}
static Map<int, int> create_value_to_first_index_map(const Span<int> values)
{
Map<int, int> map;
map.reserve(values.size());
for (const int i : values.index_range()) {
map.add(values[i], i);
}
return map;
}
static Array<int> create_id_index_map(const bke::AttributeAccessor attributes_a,
const bke::AttributeAccessor b_attributes)
{
const bke::AttributeReader<int> ids_a = attributes_a.lookup<int>("id");
const bke::AttributeReader<int> ids_b = b_attributes.lookup<int>("id");
if (!ids_a || !ids_b) {
return {};
}
if (sharing_info_equal(ids_a.sharing_info, ids_b.sharing_info)) {
return {};
}
const VArraySpan ids_span_a(*ids_a);
const VArraySpan ids_span_b(*ids_b);
const Map<int, int> id_map_b = create_value_to_first_index_map(ids_span_b);
Array<int> index_map(ids_span_a.size());
threading::parallel_for(ids_span_a.index_range(), 1024, [&](const IndexRange range) {
for (const int i : range) {
index_map[i] = id_map_b.lookup_default(ids_span_a[i], -1);
}
});
return index_map;
}
bke::GeometrySet mix_geometries(bke::GeometrySet a, const bke::GeometrySet &b, const float factor)
{
if (Mesh *mesh_a = a.get_mesh_for_write()) {
if (const Mesh *mesh_b = b.get_mesh()) {
Array<int> vert_map = create_id_index_map(mesh_a->attributes(), mesh_b->attributes());
mix_attributes(mesh_a->attributes_for_write(),
mesh_b->attributes(),
vert_map,
bke::AttrDomain::Point,
factor,
{});
}
}
if (PointCloud *points_a = a.get_pointcloud_for_write()) {
if (const PointCloud *points_b = b.get_pointcloud()) {
const Array<int> index_map = create_id_index_map(points_a->attributes(),
points_b->attributes());
mix_attributes(points_a->attributes_for_write(),
points_b->attributes(),
index_map,
bke::AttrDomain::Point,
factor);
}
}
if (Curves *curves_a = a.get_curves_for_write()) {
if (const Curves *curves_b = b.get_curves()) {
bke::MutableAttributeAccessor a = curves_a->geometry.wrap().attributes_for_write();
const bke::AttributeAccessor b = curves_b->geometry.wrap().attributes();
const Array<int> index_map = create_id_index_map(a, b);
mix_attributes(
a,
b,
index_map,
bke::AttrDomain::Point,
factor,
{"curve_type", "nurbs_order", "knots_mode", "handle_type_left", "handle_type_right"});
}
}
if (bke::Instances *instances_a = a.get_instances_for_write()) {
if (const bke::Instances *instances_b = b.get_instances()) {
const Array<int> index_map = create_id_index_map(instances_a->attributes(),
instances_b->attributes());
mix_attributes(instances_a->attributes_for_write(),
instances_b->attributes(),
index_map,
bke::AttrDomain::Instance,
factor,
{".reference_index"});
}
}
return a;
}
} // namespace blender::geometry