[Bf-blender-cvs] [5841f8656d9] master: Geometry Nodes: Add special domain interpolation for selections

Hans Goudey noreply at git.blender.org
Wed Sep 15 17:28:16 CEST 2021


Commit: 5841f8656d9580d7b967d477b142f19364ec39b5
Author: Hans Goudey
Date:   Wed Sep 15 10:28:00 2021 -0500
Branches: master
https://developer.blender.org/rB5841f8656d9580d7b967d477b142f19364ec39b5

Geometry Nodes: Add special domain interpolation for selections

The generic domain interpolation algorithms didn't quite work for
selections. The interpolation would do unexpected things that
were different than the results in edit mode. The new behavior
is supposed to be the same as edit mode, although we also have
to handle face corner selections here.

Currently the code assumes that all boolean attributes should be
handled that way. I'm not sure of why that wouldn't be the case,
but if we ever need non-selection boolean attributes, that could
be supported too.

Differential Revision: https://developer.blender.org/D12488

===================================================================

M	source/blender/blenkernel/intern/geometry_component_curve.cc
M	source/blender/blenkernel/intern/geometry_component_mesh.cc

===================================================================

diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
index afafd766760..7d0537178ef 100644
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -222,6 +222,37 @@ static void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve,
   mixer.finalize();
 }
 
+/**
+ * A spline is selected if all of its control points were selected.
+ *
+ * \note Theoretically this interpolation does not need to compute all values at once.
+ * However, doing that makes the implementation simpler, and this can be optimized in the future if
+ * only some values are required.
+ */
+template<>
+void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve,
+                                             const VArray<bool> &old_values,
+                                             MutableSpan<bool> r_values)
+{
+  const int splines_len = curve.splines().size();
+  Array<int> offsets = curve.control_point_offsets();
+  BLI_assert(r_values.size() == splines_len);
+
+  r_values.fill(true);
+
+  for (const int i_spline : IndexRange(splines_len)) {
+    const int spline_offset = offsets[i_spline];
+    const int spline_point_len = offsets[i_spline + 1] - spline_offset;
+
+    for (const int i_point : IndexRange(spline_point_len)) {
+      if (!old_values[spline_offset + i_point]) {
+        r_values[i_spline] = false;
+        break;
+      }
+    }
+  }
+}
+
 static GVArrayPtr adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArrayPtr varray)
 {
   GVArrayPtr new_varray;
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index 9a4b8f4eb92..25ef4b6000f 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -175,6 +175,34 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
   mixer.finalize();
 }
 
+/* A vertex is selected if all connected face corners were selected and it is not loose. */
+template<>
+void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
+                                            const VArray<bool> &old_values,
+                                            MutableSpan<bool> r_values)
+{
+  BLI_assert(r_values.size() == mesh.totvert);
+  Array<bool> loose_verts(mesh.totvert, true);
+
+  r_values.fill(true);
+  for (const int loop_index : IndexRange(mesh.totloop)) {
+    const MLoop &loop = mesh.mloop[loop_index];
+    const int point_index = loop.v;
+
+    loose_verts[point_index] = false;
+    if (!old_values[loop_index]) {
+      r_values[point_index] = false;
+    }
+  }
+
+  /* Deselect loose vertices without corners that are still selected from the 'true' default. */
+  for (const int vert_index : IndexRange(mesh.totvert)) {
+    if (loose_verts[vert_index]) {
+      r_values[vert_index] = false;
+    }
+  }
+}
+
 static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray)
 {
   GVArrayPtr new_varray;
@@ -191,6 +219,13 @@ static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr
   return new_varray;
 }
 
+/**
+ * Each corner's value is simply a copy of the value at its vertex.
+ *
+ * \note Theoretically this interpolation does not need to compute all values at once.
+ * However, doing that makes the implementation simpler, and this can be optimized in the future if
+ * only some values are required.
+ */
 template<typename T>
 static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
                                                    const VArray<T> &old_values,
@@ -209,10 +244,6 @@ static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr
   GVArrayPtr new_varray;
   attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
     using T = decltype(dummy);
-    /* It is not strictly necessary to compute the value for all corners here. Instead one could
-     * lazily lookup the mesh topology when a specific index accessed. This can be more efficient
-     * when an algorithm only accesses very few of the corner values. However, for the algorithms
-     * we currently have, precomputing the array is fine. Also, it is easier to implement. */
     Array<T> values(mesh.totloop);
     adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray->typed<T>(), values);
     new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
@@ -244,6 +275,26 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
   mixer.finalize();
 }
 
+/* A face is selected if all of its corners were selected. */
+template<>
+void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
+                                           const VArray<bool> &old_values,
+                                           MutableSpan<bool> r_values)
+{
+  BLI_assert(r_values.size() == mesh.totpoly);
+
+  r_values.fill(true);
+  for (const int poly_index : IndexRange(mesh.totpoly)) {
+    const MPoly &poly = mesh.mpoly[poly_index];
+    for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+      if (!old_values[loop_index]) {
+        r_values[poly_index] = false;
+        break;
+      }
+    }
+  }
+}
+
 static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray)
 {
   GVArrayPtr new_varray;
@@ -282,6 +333,41 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
   mixer.finalize();
 }
 
+/* An edge is selected if all corners on adjacent faces were selected. */
+template<>
+void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
+                                           const VArray<bool> &old_values,
+                                           MutableSpan<bool> r_values)
+{
+  BLI_assert(r_values.size() == mesh.totedge);
+
+  /* It may be possible to rely on the #ME_LOOSEEDGE flag, but that seems error-prone. */
+  Array<bool> loose_edges(mesh.totedge, true);
+
+  r_values.fill(true);
+  for (const int poly_index : IndexRange(mesh.totpoly)) {
+    const MPoly &poly = mesh.mpoly[poly_index];
+
+    for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+      const int loop_index_next = (loop_index == poly.totloop) ? poly.loopstart : (loop_index + 1);
+      const MLoop &loop = mesh.mloop[loop_index];
+      const int edge_index = loop.e;
+      loose_edges[edge_index] = false;
+
+      if (!old_values[loop_index] || !old_values[loop_index_next]) {
+        r_values[edge_index] = false;
+      }
+    }
+  }
+
+  /* Deselect loose edges without corners that are still selected from the 'true' default. */
+  for (const int edge_index : IndexRange(mesh.totedge)) {
+    if (loose_edges[edge_index]) {
+      r_values[edge_index] = false;
+    }
+  }
+}
+
 static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray)
 {
   GVArrayPtr new_varray;
@@ -317,6 +403,27 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
   mixer.finalize();
 }
 
+/* A vertex is selected if any of the connected faces were selected. */
+template<>
+void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
+                                          const VArray<bool> &old_values,
+                                          MutableSpan<bool> r_values)
+{
+  BLI_assert(r_values.size() == mesh.totvert);
+
+  r_values.fill(false);
+  for (const int poly_index : IndexRange(mesh.totpoly)) {
+    const MPoly &poly = mesh.mpoly[poly_index];
+    if (old_values[poly_index]) {
+      for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+        const MLoop &loop = mesh.mloop[loop_index];
+        const int vert_index = loop.v;
+        r_values[vert_index] = true;
+      }
+    }
+  }
+}
+
 static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray)
 {
   GVArrayPtr new_varray;
@@ -331,6 +438,7 @@ static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr v
   return new_varray;
 }
 
+/* Each corner's value is simply a copy of the value at its face. */
 template<typename T>
 void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
                                            const VArray<T> &old_values,
@@ -378,6 +486,27 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
   mixer.finalize();
 }
 
+/* An edge is selected if any connected face was selected. */
+template<>
+void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
+                                         const VArray<bool> &old_values,
+                                         MutableSpan<bool> r_values)
+{
+  BLI_assert(r_values.size() == mesh.totedge);
+
+  r_values.fill(false);
+  for (const int poly_index : IndexRange(mesh.totpoly)) {
+    const MPoly &poly = mesh.mpoly[poly_index];
+    if (old_values[poly_index]) {
+      for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+        const MLoop &loop = mesh.mloop[loop_index];
+        const int edge_index = loop.e;
+        r_values[edge_index] = true;
+      }
+    }
+  }
+}
+
 static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray)
 {
   GVArrayPtr new_varray;
@@ -416,6 +545,28 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
   mixer.finalize();
 }
 
+/* A face is selected if all of its vertices were selected too. */
+template<>
+void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
+                                          const VArray<bool> &old_values,
+                                          MutableSpan<bool> r_values)
+{
+  BLI_assert(r_values.size() == mesh.totpoly);
+
+  r_values.fill(true);
+  for (const int poly_index : IndexRange(mesh.totpoly)) {
+    const MPoly &poly = mesh.mpoly[poly_index];
+    for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+      MLoop &loop = mesh.mloop[loop_index];
+      const int vert_index = loop.v;
+      if (!old_values[vert_index]) {
+        r_values[poly_index] = false;
+        break;
+      }
+    }
+  }
+}
+
 static GVArrayPtr adapt_mesh_domain

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list