Blender V5.0
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{
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{
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 bke::AttrType type = bke::cpp_type_to_attribute_type(attribute_a.varray.type());
92 if (ELEM(type, bke::AttrType::String, bke::AttrType::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 if (!index_map.is_empty()) {
101 bke::GSpanAttributeWriter dst = attributes_a.lookup_for_write_span(id);
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 dst.finish();
105 }
106 else if (attributes_a.domain_size(domain) == b_attributes.domain_size(domain)) {
107 bke::GSpanAttributeWriter dst = attributes_a.lookup_for_write_span(id);
108 /* With no ID attribute to find matching elements, we can only support mixing when the domain
109 * size (topology) is the same. Other options like mixing just the start of arrays might work
110 * too, but give bad results too. */
111 mix(dst.span, attribute_b.varray, factor);
112 dst.finish();
113 }
114 }
115}
116
118{
119 Map<int, int> map;
120 map.reserve(values.size());
121 for (const int i : values.index_range()) {
122 map.add(values[i], i);
123 }
124 return map;
125}
126
128 const bke::AttributeAccessor b_attributes,
129 const bke::AttrDomain id_domain)
130{
131 const bke::GAttributeReader ids_a = attributes_a.lookup("id");
132 const bke::GAttributeReader ids_b = b_attributes.lookup("id");
133 if (!ids_a || !ids_b) {
134 return {};
135 }
136 if (!ids_a.varray.type().is<int>() || !ids_b.varray.type().is<int>()) {
137 return {};
138 }
139 if (ids_a.domain != id_domain || ids_b.domain != id_domain) {
140 return {};
141 }
142 if (sharing_info_equal(ids_a.sharing_info, ids_b.sharing_info)) {
143 return {};
144 }
145
146 const VArraySpan ids_span_a(ids_a.varray.typed<int>());
147 const VArraySpan ids_span_b(ids_b.varray.typed<int>());
148
149 const Map<int, int> id_map_b = create_value_to_first_index_map(ids_span_b);
150 Array<int> index_map(ids_span_a.size());
151 threading::parallel_for(ids_span_a.index_range(), 1024, [&](const IndexRange range) {
152 for (const int i : range) {
153 index_map[i] = id_map_b.lookup_default(ids_span_a[i], -1);
154 }
155 });
156 return index_map;
157}
158
160{
161 if (Mesh *mesh_a = a.get_mesh_for_write()) {
162 if (const Mesh *mesh_b = b.get_mesh()) {
164 mesh_a->attributes(), mesh_b->attributes(), bke::AttrDomain::Point);
165 mix_attributes(mesh_a->attributes_for_write(),
166 mesh_b->attributes(),
167 vert_map,
169 factor,
170 {});
171 }
172 }
173 if (PointCloud *points_a = a.get_pointcloud_for_write()) {
174 if (const PointCloud *points_b = b.get_pointcloud()) {
175 const Array<int> index_map = create_id_index_map(
176 points_a->attributes(), points_b->attributes(), bke::AttrDomain::Point);
177 mix_attributes(points_a->attributes_for_write(),
178 points_b->attributes(),
179 index_map,
181 factor);
182 }
183 }
184 if (Curves *curves_a = a.get_curves_for_write()) {
185 if (const Curves *curves_b = b.get_curves()) {
186 bke::MutableAttributeAccessor a = curves_a->geometry.wrap().attributes_for_write();
187 const bke::AttributeAccessor b = curves_b->geometry.wrap().attributes();
190 a,
191 b,
192 index_map,
194 factor,
195 {"curve_type", "nurbs_order", "knots_mode", "handle_type_left", "handle_type_right"});
196 }
197 }
198 if (bke::Instances *instances_a = a.get_instances_for_write()) {
199 if (const bke::Instances *instances_b = b.get_instances()) {
200 const Array<int> index_map = create_id_index_map(
201 instances_a->attributes(), instances_b->attributes(), bke::AttrDomain::Instance);
202 mix_attributes(instances_a->attributes_for_write(),
203 instances_b->attributes(),
204 index_map,
206 factor,
207 {".reference_index"});
208 }
209 }
210 return a;
211}
212
213} // namespace blender::geometry
Low-level operations for curves.
#define ELEM(...)
bool is() const
const CPPType & type() const
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
void reserve(int64_t n)
Definition BLI_map.hh:1028
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
bool remove_as(const ForwardKey &key)
Definition BLI_set.hh:389
bool remove(const Key &key)
Definition BLI_set.hh:385
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
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)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
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 Array< int > create_id_index_map(const bke::AttributeAccessor attributes_a, const bke::AttributeAccessor b_attributes, const bke::AttrDomain id_domain)
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)
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:93
const char * name
#define mix
const ImplicitSharingInfo * sharing_info
PointCloud * get_pointcloud_for_write()
Instances * get_instances_for_write()
i
Definition text_draw.cc:230