diff options
author | Hans Goudey <h.goudey@me.com> | 2022-01-22 02:57:27 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-01-22 02:57:27 +0300 |
commit | ae1649e905701420afe7cf1566fdafa438b3472a (patch) | |
tree | 1436cb09dc088e7aa67b67fe81ad387d398c73ff /source | |
parent | 32832197adc1550b63d9ff90d0eaa76f8c136e46 (diff) |
Mix boolean attributes with a new "or" mixer
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/BKE_attribute_math.hh | 53 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc | 18 |
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]); + } } } }); |