Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2022-01-22 02:57:27 +0300
committerHans Goudey <h.goudey@me.com>2022-01-22 02:57:27 +0300
commitae1649e905701420afe7cf1566fdafa438b3472a (patch)
tree1436cb09dc088e7aa67b67fe81ad387d398c73ff /source
parent32832197adc1550b63d9ff90d0eaa76f8c136e46 (diff)
Mix boolean attributes with a new "or" mixer
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenkernel/BKE_attribute_math.hh53
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc18
2 files changed, 64 insertions, 7 deletions
diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh
index a7bdca06790..36a08b18963 100644
--- a/source/blender/blenkernel/BKE_attribute_math.hh
+++ b/source/blender/blenkernel/BKE_attribute_math.hh
@@ -231,6 +231,43 @@ template<typename T> class SimpleMixer {
};
/**
+ * Mixes together booleans with "or" while fitting the same interface as the other mixers in order
+ * to make using it simpler. This mixing method has a few benefits:
+ * - An "average" for selections is relatively meaningless.
+ * - Predictable selection propagation is very super important.
+ * - It's generally easier to remove an element from a selection that is slightly too large than
+ * the opposite.
+ */
+class BooleanPropagationMixer {
+ private:
+ MutableSpan<bool> buffer_;
+
+ public:
+ /**
+ * \param buffer: Span where the interpolated values should be stored.
+ */
+ BooleanPropagationMixer(MutableSpan<bool> buffer) : buffer_(buffer)
+ {
+ buffer_.fill(false);
+ }
+
+ /**
+ * Mix a #value into the element with the given #index.
+ */
+ void mix_in(const int64_t index, const bool value, [[maybe_unused]] const float weight = 1.0f)
+ {
+ buffer_[index] |= value;
+ }
+
+ /**
+ * Does not do anything, since the mixing is trivial.
+ */
+ void finalize()
+ {
+ }
+};
+
+/**
* This mixer accumulates values in a type that is different from the one that is mixed.
* Some types cannot encode the floating point weights in their values (e.g. int and bool).
*/
@@ -291,7 +328,7 @@ class ColorGeometryMixer {
};
template<typename T> struct DefaultMixerStruct {
- /* Use void by default. This can be check for in `if constexpr` statements. */
+ /* Use void by default. This can be checked for in `if constexpr` statements. */
using type = void;
};
template<> struct DefaultMixerStruct<float> {
@@ -327,6 +364,20 @@ template<> struct DefaultMixerStruct<bool> {
using type = SimpleMixerWithAccumulationType<bool, float, float_to_bool>;
};
+template<typename T> struct DefaultPropatationMixerStruct {
+ /* Use void by default. This can be checked for in `if constexpr` statements. */
+ using type = typename DefaultMixerStruct<T>::type;
+};
+
+template<> struct DefaultPropatationMixerStruct<bool> {
+ using type = BooleanPropagationMixer;
+};
+
+/* This mixer is meant for propagating attributes when creating new geometry. A key difference
+ * with the default mixer is that booleans are mixed with "or" instead of "at least half". */
+template<typename T>
+using DefaultPropatationMixer = typename DefaultPropatationMixerStruct<T>::type;
+
/* Utility to get a good default mixer for a given type. This is `void` when there is no default
* mixer for the given type. */
template<typename T> using DefaultMixer = typename DefaultMixerStruct<T>::type;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
index 7ff688377a8..8d311906527 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
@@ -216,7 +216,7 @@ template<typename T, typename GetMixIndicesFn>
void copy_with_mixing(MutableSpan<T> dst, Span<T> src, GetMixIndicesFn get_mix_indices_fn)
{
threading::parallel_for(dst.index_range(), 512, [&](const IndexRange range) {
- attribute_math::DefaultMixer<T> mixer{dst.slice(range)};
+ attribute_math::DefaultPropatationMixer<T> mixer{dst.slice(range)};
for (const int i_dst : IndexRange(range.size())) {
for (const int i_src : get_mix_indices_fn(range[i_dst])) {
mixer.mix_in(i_dst, src[i_src]);
@@ -424,7 +424,7 @@ static void extrude_mesh_edges(MeshComponent &component,
Array<float3> vert_offsets;
if (!edge_offsets.is_single()) {
vert_offsets.reinitialize(orig_vert_size);
- attribute_math::DefaultMixer<float3> mixer(vert_offsets);
+ attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets);
for (const int i_edge : edge_selection) {
const MEdge &edge = orig_edges[i_edge];
const float3 offset = edge_offsets[i_edge];
@@ -568,7 +568,7 @@ static void extrude_mesh_edges(MeshComponent &component,
/* Both corners on each vertical edge of the side polygon get the same value,
* so there are only two unique values to mix. */
Array<T> side_poly_corner_data(2);
- attribute_math::DefaultMixer<T> mixer{side_poly_corner_data};
+ attribute_math::DefaultPropatationMixer<T> mixer{side_poly_corner_data};
const MEdge &duplicate_edge = duplicate_edges[i_edge_selection];
const int new_vert_1 = duplicate_edge.v1;
@@ -684,7 +684,7 @@ static void extrude_mesh_face_regions(MeshComponent &component,
Array<float3> vert_offsets;
if (!poly_offsets.is_single()) {
vert_offsets.reinitialize(orig_vert_size);
- attribute_math::DefaultMixer<float3> mixer(vert_offsets);
+ attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets);
for (const int i_poly : poly_selection) {
const MPoly &poly = orig_polys[i_poly];
const float3 offset = poly_offsets[i_poly];
@@ -1177,8 +1177,14 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
const int i_loop_prev = (i == 0) ? poly.totloop - 1 : i - 1;
const int orig_index = poly_loops[i].e;
const int orig_index_prev = poly_loops[i_loop_prev].e;
- connect_data[poly_corner_range[i]] = attribute_math::mix2(
- 0.5f, data[orig_index], data[orig_index_prev]);
+ if constexpr (std::is_same_v<T, bool>) {
+ /* Propagate selections with "or" instead of "at least half". */
+ connect_data[poly_corner_range[i]] = data[orig_index] || data[orig_index_prev];
+ }
+ else {
+ connect_data[poly_corner_range[i]] = attribute_math::mix2(
+ 0.5f, data[orig_index], data[orig_index_prev]);
+ }
}
}
});