Blender V4.3
BKE_attribute_math.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#pragma once
6
7#include "BLI_array.hh"
8#include "BLI_color.hh"
9#include "BLI_cpp_type.hh"
10#include "BLI_generic_span.hh"
13#include "BLI_math_color.hh"
15#include "BLI_math_vector.h"
16#include "BLI_math_vector.hh"
17#include "BLI_offset_indices.hh"
18
19#include "BKE_attribute.hh"
20
22
26template<typename Func>
27inline void convert_to_static_type(const CPPType &cpp_type, const Func &func)
28{
30 float2,
31 float3,
32 int,
33 int2,
34 bool,
35 int8_t,
39 float4x4>([&](auto type_tag) {
40 using T = typename decltype(type_tag)::type;
41 if constexpr (std::is_same_v<T, void>) {
42 /* It's expected that the given cpp type is one of the supported ones. */
44 }
45 else {
46 func(T());
47 }
48 });
49}
50
51template<typename Func>
52inline void convert_to_static_type(const eCustomDataType data_type, const Func &func)
53{
54 const CPPType &cpp_type = *bke::custom_data_type_to_cpp_type(data_type);
55 convert_to_static_type(cpp_type, func);
56}
57
58/* -------------------------------------------------------------------- */
64template<typename T> T mix2(float factor, const T &a, const T &b);
65
66template<> inline bool mix2(const float factor, const bool &a, const bool &b)
67{
68 return ((1.0f - factor) * a + factor * b) >= 0.5f;
69}
70
71template<> inline int8_t mix2(const float factor, const int8_t &a, const int8_t &b)
72{
73 return int8_t(std::round((1.0f - factor) * a + factor * b));
74}
75
76template<> inline int mix2(const float factor, const int &a, const int &b)
77{
78 return int(std::round((1.0f - factor) * a + factor * b));
79}
80
81template<> inline int2 mix2(const float factor, const int2 &a, const int2 &b)
82{
83 return math::interpolate(a, b, factor);
84}
85
86template<> inline float mix2(const float factor, const float &a, const float &b)
87{
88 return (1.0f - factor) * a + factor * b;
89}
90
91template<> inline float2 mix2(const float factor, const float2 &a, const float2 &b)
92{
93 return math::interpolate(a, b, factor);
94}
95
96template<> inline float3 mix2(const float factor, const float3 &a, const float3 &b)
97{
98 return math::interpolate(a, b, factor);
99}
100
101template<>
102inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b)
103{
104 return math::interpolate(a, b, factor);
105}
106
107template<>
108inline ColorGeometry4b mix2(const float factor, const ColorGeometry4b &a, const ColorGeometry4b &b)
109{
110 return math::interpolate(a, b, factor);
111}
112
115/* -------------------------------------------------------------------- */
121template<typename T> T mix3(const float3 &weights, const T &v0, const T &v1, const T &v2);
122
123template<>
124inline int8_t mix3(const float3 &weights, const int8_t &v0, const int8_t &v1, const int8_t &v2)
125{
126 return int8_t(std::round(weights.x * v0 + weights.y * v1 + weights.z * v2));
127}
128
129template<> inline bool mix3(const float3 &weights, const bool &v0, const bool &v1, const bool &v2)
130{
131 return (weights.x * v0 + weights.y * v1 + weights.z * v2) >= 0.5f;
132}
133
134template<> inline int mix3(const float3 &weights, const int &v0, const int &v1, const int &v2)
135{
136 return int(std::round(weights.x * v0 + weights.y * v1 + weights.z * v2));
137}
138
139template<> inline int2 mix3(const float3 &weights, const int2 &v0, const int2 &v1, const int2 &v2)
140{
141 return int2(weights.x * float2(v0) + weights.y * float2(v1) + weights.z * float2(v2));
142}
143
144template<>
145inline float mix3(const float3 &weights, const float &v0, const float &v1, const float &v2)
146{
147 return weights.x * v0 + weights.y * v1 + weights.z * v2;
148}
149
150template<>
151inline float2 mix3(const float3 &weights, const float2 &v0, const float2 &v1, const float2 &v2)
152{
153 return weights.x * v0 + weights.y * v1 + weights.z * v2;
154}
155
156template<>
157inline float3 mix3(const float3 &weights, const float3 &v0, const float3 &v1, const float3 &v2)
158{
159 return weights.x * v0 + weights.y * v1 + weights.z * v2;
160}
161
162template<>
163inline ColorGeometry4f mix3(const float3 &weights,
164 const ColorGeometry4f &v0,
165 const ColorGeometry4f &v1,
166 const ColorGeometry4f &v2)
167{
169 interp_v4_v4v4v4(result, v0, v1, v2, weights);
170 return result;
171}
172
173template<>
174inline ColorGeometry4b mix3(const float3 &weights,
175 const ColorGeometry4b &v0,
176 const ColorGeometry4b &v1,
177 const ColorGeometry4b &v2)
178{
179 const float4 v0_f{&v0.r};
180 const float4 v1_f{&v1.r};
181 const float4 v2_f{&v2.r};
182 const float4 mixed = v0_f * weights[0] + v1_f * weights[1] + v2_f * weights[2];
183 return ColorGeometry4b{
184 uint8_t(mixed[0]), uint8_t(mixed[1]), uint8_t(mixed[2]), uint8_t(mixed[3])};
185}
186
189/* -------------------------------------------------------------------- */
194template<typename T>
195T mix4(const float4 &weights, const T &v0, const T &v1, const T &v2, const T &v3);
196
197template<>
199 const float4 &weights, const int8_t &v0, const int8_t &v1, const int8_t &v2, const int8_t &v3)
200{
201 return int8_t(std::round(weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3));
202}
203
204template<>
205inline bool mix4(
206 const float4 &weights, const bool &v0, const bool &v1, const bool &v2, const bool &v3)
207{
208 return (weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3) >= 0.5f;
209}
210
211template<>
212inline int mix4(const float4 &weights, const int &v0, const int &v1, const int &v2, const int &v3)
213{
214 return int(std::round(weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3));
215}
216
217template<>
218inline int2 mix4(
219 const float4 &weights, const int2 &v0, const int2 &v1, const int2 &v2, const int2 &v3)
220{
221 return int2(weights.x * float2(v0) + weights.y * float2(v1) + weights.z * float2(v2) +
222 weights.w * float2(v3));
223}
224
225template<>
226inline float mix4(
227 const float4 &weights, const float &v0, const float &v1, const float &v2, const float &v3)
228{
229 return weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3;
230}
231
232template<>
234 const float4 &weights, const float2 &v0, const float2 &v1, const float2 &v2, const float2 &v3)
235{
236 return weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3;
237}
238
239template<>
241 const float4 &weights, const float3 &v0, const float3 &v1, const float3 &v2, const float3 &v3)
242{
243 return weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3;
244}
245
246template<>
247inline ColorGeometry4f mix4(const float4 &weights,
248 const ColorGeometry4f &v0,
249 const ColorGeometry4f &v1,
250 const ColorGeometry4f &v2,
251 const ColorGeometry4f &v3)
252{
254 interp_v4_v4v4v4v4(result, v0, v1, v2, v3, weights);
255 return result;
256}
257
258template<>
259inline ColorGeometry4b mix4(const float4 &weights,
260 const ColorGeometry4b &v0,
261 const ColorGeometry4b &v1,
262 const ColorGeometry4b &v2,
263 const ColorGeometry4b &v3)
264{
265 const float4 v0_f{&v0.r};
266 const float4 v1_f{&v1.r};
267 const float4 v2_f{&v2.r};
268 const float4 v3_f{&v3.r};
269 float4 mixed;
270 interp_v4_v4v4v4v4(mixed, v0_f, v1_f, v2_f, v3_f, weights);
271 return ColorGeometry4b{
272 uint8_t(mixed[0]), uint8_t(mixed[1]), uint8_t(mixed[2]), uint8_t(mixed[3])};
273}
274
277/* -------------------------------------------------------------------- */
285template<typename T> class SimpleMixer {
286 private:
287 MutableSpan<T> buffer_;
288 T default_value_;
289 Array<float> total_weights_;
290
291 public:
296 SimpleMixer(MutableSpan<T> buffer, T default_value = {})
297 : SimpleMixer(buffer, buffer.index_range(), default_value)
298 {
299 }
300
304 SimpleMixer(MutableSpan<T> buffer, const IndexMask &mask, T default_value = {})
305 : buffer_(buffer), default_value_(default_value), total_weights_(buffer.size(), 0.0f)
306 {
307 BLI_STATIC_ASSERT(std::is_trivial_v<T>, "");
308 mask.foreach_index([&](const int64_t i) { buffer_[i] = default_value_; });
309 }
310
314 void set(const int64_t index, const T &value, const float weight = 1.0f)
315 {
316 buffer_[index] = value * weight;
317 total_weights_[index] = weight;
318 }
319
323 void mix_in(const int64_t index, const T &value, const float weight = 1.0f)
324 {
325 buffer_[index] += value * weight;
326 total_weights_[index] += weight;
327 }
328
332 void finalize()
333 {
334 this->finalize(IndexMask(buffer_.size()));
335 }
336
337 void finalize(const IndexMask &mask)
338 {
339 mask.foreach_index([&](const int64_t i) {
340 const float weight = total_weights_[i];
341 if (weight > 0.0f) {
342 buffer_[i] *= 1.0f / weight;
343 }
344 else {
345 buffer_[i] = default_value_;
346 }
347 });
348 }
349};
350
360 private:
361 MutableSpan<bool> buffer_;
362
363 public:
368 : BooleanPropagationMixer(buffer, buffer.index_range())
369 {
370 }
371
375 BooleanPropagationMixer(MutableSpan<bool> buffer, const IndexMask &mask) : buffer_(buffer)
376 {
377 mask.foreach_index([&](const int64_t i) { buffer_[i] = false; });
378 }
379
383 void set(const int64_t index, const bool value, [[maybe_unused]] const float weight = 1.0f)
384 {
385 buffer_[index] = value;
386 }
387
391 void mix_in(const int64_t index, const bool value, [[maybe_unused]] const float weight = 1.0f)
392 {
393 buffer_[index] |= value;
394 }
395
399 void finalize() {}
400
401 void finalize(const IndexMask & /*mask*/) {}
402};
403
408template<typename T,
409 typename AccumulationT,
410 AccumulationT (*ValueToAccumulate)(const T &value),
411 T (*AccumulateToValue)(const AccumulationT &value)>
413 private:
414 struct Item {
415 /* Store both values together, because they are accessed together. */
416 AccumulationT value = AccumulationT(0);
417 float weight = 0.0f;
418 };
419
420 MutableSpan<T> buffer_;
421 T default_value_;
422 Array<Item> accumulation_buffer_;
423
424 public:
426 : SimpleMixerWithAccumulationType(buffer, buffer.index_range(), default_value)
427 {
428 }
429
434 const IndexMask &mask,
435 T default_value = {})
436 : buffer_(buffer), default_value_(default_value), accumulation_buffer_(buffer.size())
437 {
438 mask.foreach_index([&](const int64_t index) { buffer_[index] = default_value_; });
439 }
440
441 void set(const int64_t index, const T &value, const float weight = 1.0f)
442 {
443 const AccumulationT converted_value = ValueToAccumulate(value);
444 Item &item = accumulation_buffer_[index];
445 item.value = converted_value * weight;
446 item.weight = weight;
447 }
448
449 void mix_in(const int64_t index, const T &value, const float weight = 1.0f)
450 {
451 const AccumulationT converted_value = ValueToAccumulate(value);
452 Item &item = accumulation_buffer_[index];
453 item.value += converted_value * weight;
454 item.weight += weight;
455 }
456
457 void finalize()
458 {
459 this->finalize(buffer_.index_range());
460 }
461
462 void finalize(const IndexMask &mask)
463 {
464 mask.foreach_index([&](const int64_t i) {
465 const Item &item = accumulation_buffer_[i];
466 if (item.weight > 0.0f) {
467 const float weight_inv = 1.0f / item.weight;
468 const T converted_value = AccumulateToValue(item.value * weight_inv);
469 buffer_[i] = converted_value;
470 }
471 else {
472 buffer_[i] = default_value_;
473 }
474 });
475 }
476};
477
479 private:
481 ColorGeometry4f default_color_;
482 Array<float> total_weights_;
483
484 public:
486 ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
491 const IndexMask &mask,
492 ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
493 void set(int64_t index, const ColorGeometry4f &color, float weight = 1.0f);
494 void mix_in(int64_t index, const ColorGeometry4f &color, float weight = 1.0f);
495 void finalize();
496 void finalize(const IndexMask &mask);
497};
498
500 private:
502 ColorGeometry4b default_color_;
503 Array<float> total_weights_;
504 Array<float4> accumulation_buffer_;
505
506 public:
508 ColorGeometry4b default_color = ColorGeometry4b(0, 0, 0, 255));
513 const IndexMask &mask,
514 ColorGeometry4b default_color = ColorGeometry4b(0, 0, 0, 255));
515 void set(int64_t index, const ColorGeometry4b &color, float weight = 1.0f);
516 void mix_in(int64_t index, const ColorGeometry4b &color, float weight = 1.0f);
517 void finalize();
518 void finalize(const IndexMask &mask);
519};
520
522 private:
523 MutableSpan<float4x4> buffer_;
524 Array<float> total_weights_;
525 Array<float3> location_buffer_;
526 Array<float3> expmap_buffer_;
527 Array<float3> scale_buffer_;
528
529 public:
534 float4x4Mixer(MutableSpan<float4x4> buffer, const IndexMask &mask);
535 void set(int64_t index, const float4x4 &value, float weight = 1.0f);
536 void mix_in(int64_t index, const float4x4 &value, float weight = 1.0f);
537 void finalize();
538 void finalize(const IndexMask &mask);
539};
540
541template<typename T> struct DefaultMixerStruct {
542 /* Use void by default. This can be checked for in `if constexpr` statements. */
543 using type = void;
544};
545template<> struct DefaultMixerStruct<float> {
547};
548template<> struct DefaultMixerStruct<float2> {
550};
551template<> struct DefaultMixerStruct<float3> {
553};
555 /* Use a special mixer for colors. ColorGeometry4f can't be added/multiplied, because this is not
556 * something one should usually do with colors. */
558};
564};
565template<> struct DefaultMixerStruct<int> {
566 static double int_to_double(const int &value)
567 {
568 return double(value);
569 }
570 static int double_to_int(const double &value)
571 {
572 return int(std::round(value));
573 }
574 /* Store interpolated ints in a double temporarily, so that weights are handled correctly. It
575 * uses double instead of float so that it is accurate for all 32 bit integers. */
577};
578template<> struct DefaultMixerStruct<int2> {
579 static double2 int_to_double(const int2 &value)
580 {
581 return double2(value);
582 }
583 static int2 double_to_int(const double2 &value)
584 {
585 return int2(math::round(value));
586 }
587 /* Store interpolated ints in a double temporarily, so that weights are handled correctly. It
588 * uses double instead of float so that it is accurate for all 32 bit integers. */
590};
591template<> struct DefaultMixerStruct<bool> {
592 static float bool_to_float(const bool &value)
593 {
594 return value ? 1.0f : 0.0f;
595 }
596 static bool float_to_bool(const float &value)
597 {
598 return value >= 0.5f;
599 }
600 /* Store interpolated booleans in a float temporary.
601 * Otherwise information provided by weights is easily rounded away. */
603};
604
605template<> struct DefaultMixerStruct<int8_t> {
606 static float int8_t_to_float(const int8_t &value)
607 {
608 return float(value);
609 }
610 static int8_t float_to_int8_t(const float &value)
611 {
612 return int8_t(std::round(value));
613 }
614 /* Store interpolated 8 bit integers in a float temporarily to increase accuracy. */
616};
617template<> struct DefaultMixerStruct<math::Quaternion> {
619 {
620 return value.expmap();
621 }
623 {
624 return math::Quaternion::expmap(value);
625 }
626 using type =
628};
629
630template<typename T> struct DefaultPropagationMixerStruct {
631 /* Use void by default. This can be checked for in `if constexpr` statements. */
633};
634
635template<> struct DefaultPropagationMixerStruct<bool> {
637};
638
644template<typename T>
646
647/* Utility to get a good default mixer for a given type. This is `void` when there is no default
648 * mixer for the given type. */
649template<typename T> using DefaultMixer = typename DefaultMixerStruct<T>::type;
650
653/* -------------------------------------------------------------------- */
660void gather(GSpan src, Span<int> map, GMutableSpan dst);
661void gather(const GVArray &src, Span<int> map, GMutableSpan dst);
663 OffsetIndices<int> dst_offsets,
664 const IndexMask &selection,
665 GSpan src,
666 GMutableSpan dst);
667void gather_to_groups(OffsetIndices<int> dst_offsets,
668 const IndexMask &src_selection,
669 GSpan src,
670 GMutableSpan dst);
671
674} // namespace blender::bke::attribute_math
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:87
void interp_v4_v4v4v4v4(float p[4], const float v1[4], const float v2[4], const float v3[4], const float v4[4], const float w[4])
void interp_v4_v4v4v4(float p[4], const float v1[4], const float v2[4], const float v3[4], const float w[3])
typedef double(DMatrix)[4][4]
ATTR_WARN_UNUSED_RESULT const BMVert * v2
void to_static_type_tag(const Fn &fn) const
ChannelStorageType r
Definition BLI_color.hh:88
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
BooleanPropagationMixer(MutableSpan< bool > buffer, const IndexMask &mask)
void set(const int64_t index, const bool value, const float weight=1.0f)
void mix_in(const int64_t index, const bool value, const float weight=1.0f)
void set(int64_t index, const ColorGeometry4b &color, float weight=1.0f)
SimpleMixerWithAccumulationType(MutableSpan< T > buffer, const IndexMask &mask, T default_value={})
void mix_in(const int64_t index, const T &value, const float weight=1.0f)
SimpleMixerWithAccumulationType(MutableSpan< T > buffer, T default_value={})
void set(const int64_t index, const T &value, const float weight=1.0f)
SimpleMixer(MutableSpan< T > buffer, const IndexMask &mask, T default_value={})
void mix_in(const int64_t index, const T &value, const float weight=1.0f)
void set(const int64_t index, const T &value, const float weight=1.0f)
SimpleMixer(MutableSpan< T > buffer, T default_value={})
void set(int64_t index, const float4x4 &value, float weight=1.0f)
local_group_size(16, 16) .push_constant(Type b
node_ attributes set("label", ss.str())
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define T
void gather(GSpan src, Span< int > map, GMutableSpan dst)
void gather_to_groups(OffsetIndices< int > dst_offsets, const IndexMask &src_selection, GSpan src, GMutableSpan dst)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
T mix3(const float3 &weights, const T &v0, const T &v1, const T &v2)
typename DefaultMixerStruct< T >::type DefaultMixer
T mix4(const float4 &weights, const T &v0, const T &v1, const T &v2, const T &v3)
void gather_group_to_group(OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, GSpan src, GMutableSpan dst)
typename DefaultPropagationMixerStruct< T >::type DefaultPropagationMixer
T mix2(float factor, const T &a, const T &b)
const CPPType * custom_data_type_to_cpp_type(eCustomDataType type)
QuaternionBase< float > Quaternion
T interpolate(const T &a, const T &b, const FactorT &t)
T round(const T &a)
VecBase< double, 2 > double2
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:337
VecBase< float, 3 > float3
ColorSceneLinearByteEncoded4b< eAlpha::Premultiplied > ColorGeometry4b
Definition BLI_color.hh:338
__int64 int64_t
Definition stdint.h:89
unsigned char uint8_t
Definition stdint.h:78
signed char int8_t
Definition stdint.h:75