Blender V4.3
mix_geometries.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "BKE_attribute.hh"
9#include "BKE_curves.hh"
10#include "BKE_instances.hh"
11
12#include "DNA_mesh_types.h"
14
15namespace blender::geometry {
16
18{
19 if (!a || !b) {
20 return false;
21 }
22 return a == b;
23}
24
25template<typename T>
27 const VArray<T> &b,
28 const Span<int> index_map,
29 const float factor)
30{
31 threading::parallel_for(a.index_range(), 1024, [&](const IndexRange range) {
32 devirtualize_varray(b, [&](const auto b) {
33 for (const int i : range) {
34 if (index_map[i] != -1) {
35 a[i] = bke::attribute_math::mix2(factor, a[i], b[index_map[i]]);
36 }
37 }
38 });
39 });
40}
41
43 const GVArray &b,
44 const Span<int> index_map,
45 const float factor)
46{
47 bke::attribute_math::convert_to_static_type(a.type(), [&](auto dummy) {
48 using T = decltype(dummy);
49 mix_with_indices(a.typed<T>(), b.typed<T>(), index_map, factor);
50 });
51}
52
53template<typename T> static void mix(MutableSpan<T> a, const VArray<T> &b, const float factor)
54{
55 threading::parallel_for(a.index_range(), 1024, [&](const IndexRange range) {
56 devirtualize_varray(b, [&](const auto b) {
57 for (const int i : range) {
58 a[i] = bke::attribute_math::mix2(factor, a[i], b[i]);
59 }
60 });
61 });
62}
63
64static void mix(GMutableSpan a, const GVArray &b, const float factor)
65{
66 bke::attribute_math::convert_to_static_type(a.type(), [&](auto dummy) {
67 using T = decltype(dummy);
68 mix(a.typed<T>(), b.typed<T>(), factor);
69 });
70}
71
73 const bke::AttributeAccessor b_attributes,
74 const Span<int> index_map,
75 const bke::AttrDomain mix_domain,
76 const float factor,
77 const Set<std::string> &names_to_skip = {})
78{
79 Set<StringRefNull> ids = attributes_a.all_ids();
80 ids.remove("id");
81 for (const StringRef name : names_to_skip) {
82 ids.remove_as(name);
83 }
84
85 for (const StringRef id : ids) {
86 const bke::GAttributeReader attribute_a = attributes_a.lookup(id);
87 const bke::AttrDomain domain = attribute_a.domain;
88 if (domain != mix_domain) {
89 continue;
90 }
91 const eCustomDataType type = bke::cpp_type_to_custom_data_type(attribute_a.varray.type());
92 if (ELEM(type, CD_PROP_STRING, CD_PROP_BOOL)) {
93 /* String attributes can't be mixed, and there's no point in mixing boolean attributes. */
94 continue;
95 }
96 const bke::GAttributeReader attribute_b = b_attributes.lookup(id, attribute_a.domain, type);
97 if (sharing_info_equal(attribute_a.sharing_info, attribute_b.sharing_info)) {
98 continue;
99 }
100 bke::GSpanAttributeWriter dst = attributes_a.lookup_for_write_span(id);
101 if (!index_map.is_empty()) {
102 /* If there's an ID attribute, use its values to mix with potentially changed indices. */
103 mix_with_indices(dst.span, *attribute_b, index_map, factor);
104 }
105 else if (attributes_a.domain_size(domain) == b_attributes.domain_size(domain)) {
106 /* With no ID attribute to find matching elements, we can only support mixing when the domain
107 * size (topology) is the same. Other options like mixing just the start of arrays might work
108 * too, but give bad results too. */
109 mix(dst.span, attribute_b.varray, factor);
110 }
111 dst.finish();
112 }
113}
114
116{
117 Map<int, int> map;
118 map.reserve(values.size());
119 for (const int i : values.index_range()) {
120 map.add(values[i], i);
121 }
122 return map;
123}
124
126 const bke::AttributeAccessor b_attributes)
127{
128 const bke::AttributeReader<int> ids_a = attributes_a.lookup<int>("id");
129 const bke::AttributeReader<int> ids_b = b_attributes.lookup<int>("id");
130 if (!ids_a || !ids_b) {
131 return {};
132 }
133 if (sharing_info_equal(ids_a.sharing_info, ids_b.sharing_info)) {
134 return {};
135 }
136
137 const VArraySpan ids_span_a(*ids_a);
138 const VArraySpan ids_span_b(*ids_b);
139
140 const Map<int, int> id_map_b = create_value_to_first_index_map(ids_span_b);
141 Array<int> index_map(ids_span_a.size());
142 threading::parallel_for(ids_span_a.index_range(), 1024, [&](const IndexRange range) {
143 for (const int i : range) {
144 index_map[i] = id_map_b.lookup_default(ids_span_a[i], -1);
145 }
146 });
147 return index_map;
148}
149
151{
152 if (Mesh *mesh_a = a.get_mesh_for_write()) {
153 if (const Mesh *mesh_b = b.get_mesh()) {
154 Array<int> vert_map = create_id_index_map(mesh_a->attributes(), mesh_b->attributes());
155 mix_attributes(mesh_a->attributes_for_write(),
156 mesh_b->attributes(),
157 vert_map,
158 bke::AttrDomain::Point,
159 factor,
160 {});
161 }
162 }
163 if (PointCloud *points_a = a.get_pointcloud_for_write()) {
164 if (const PointCloud *points_b = b.get_pointcloud()) {
165 const Array<int> index_map = create_id_index_map(points_a->attributes(),
166 points_b->attributes());
167 mix_attributes(points_a->attributes_for_write(),
168 points_b->attributes(),
169 index_map,
170 bke::AttrDomain::Point,
171 factor);
172 }
173 }
174 if (Curves *curves_a = a.get_curves_for_write()) {
175 if (const Curves *curves_b = b.get_curves()) {
176 bke::MutableAttributeAccessor a = curves_a->geometry.wrap().attributes_for_write();
177 const bke::AttributeAccessor b = curves_b->geometry.wrap().attributes();
178 const Array<int> index_map = create_id_index_map(a, b);
180 a,
181 b,
182 index_map,
183 bke::AttrDomain::Point,
184 factor,
185 {"curve_type", "nurbs_order", "knots_mode", "handle_type_left", "handle_type_right"});
186 }
187 }
188 if (bke::Instances *instances_a = a.get_instances_for_write()) {
189 if (const bke::Instances *instances_b = b.get_instances()) {
190 const Array<int> index_map = create_id_index_map(instances_a->attributes(),
191 instances_b->attributes());
192 mix_attributes(instances_a->attributes_for_write(),
193 instances_b->attributes(),
194 index_map,
195 bke::AttrDomain::Instance,
196 factor,
197 {".reference_index"});
198 }
199 }
200 return a;
201}
202
203} // namespace blender::geometry
Low-level operations for curves.
#define ELEM(...)
@ CD_PROP_STRING
void reserve(int64_t n)
Definition BLI_map.hh:979
bool remove_as(const ForwardKey &key)
Definition BLI_set.hh:370
bool remove(const Key &key)
Definition BLI_set.hh:366
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
GAttributeReader lookup(const StringRef attribute_id) const
Set< StringRefNull > all_ids() const
int domain_size(const AttrDomain domain) const
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
local_group_size(16, 16) .push_constant(Type b
#define mix(a, b, c)
Definition hash.h:36
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={})
void mix_with_indices(MutableSpan< T > a, const VArray< T > &b, const Span< int > index_map, const float factor)
static bool sharing_info_equal(const ImplicitSharingInfo *a, const ImplicitSharingInfo *b)
bke::GeometrySet mix_geometries(bke::GeometrySet a, const bke::GeometrySet &b, float factor)
static Map< int, int > create_value_to_first_index_map(const Span< int > values)
static Array< int > create_id_index_map(const bke::AttributeAccessor attributes_a, const bke::AttributeAccessor b_attributes)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
const ImplicitSharingInfo * sharing_info