[Bf-blender-cvs] [d54a08c8af1] master: Geometry Nodes: Dual Mesh Node

Wannes Malfait noreply at git.blender.org
Wed Dec 1 17:12:11 CET 2021


Commit: d54a08c8af12514a976be8996ebbad64682f54cc
Author: Wannes Malfait
Date:   Wed Dec 1 11:11:50 2021 -0500
Branches: master
https://developer.blender.org/rBd54a08c8af12514a976be8996ebbad64682f54cc

Geometry Nodes: Dual Mesh Node

This node calculates the dual of the input mesh. This means that faces
get replaced with vertices and vertices with faces. In principle this
only makes sense when the mesh in manifold, but there is an option to
keep the (non-manifold) boundaries of the mesh intact.

Attributes are propagated:
 - Point domain goes to face domain and vice versa
 - Edge domain and Face corner domain gets mapped to itself
Because of the duality, when the mesh is manifold, the attributes get
mapped to themselves when applying the node twice.

Thanks to Leul Mulugeta (@Leul) for help with the
ascii diagrams in the code comments.

Note that this does not work well with some non-manifold geometry,
like an edge connected to more than 2 faces, or a vertex connected to
only two faces, while not being in the boundary. This is because there
is no good way to define the dual at some of those points. This type
of non-manifold vertices are just removed for this reason.

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

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

M	release/scripts/startup/nodeitems_builtins.py
M	source/blender/blenkernel/BKE_node.h
M	source/blender/blenkernel/intern/node.cc
M	source/blender/nodes/NOD_geometry.h
M	source/blender/nodes/NOD_static_types.h
M	source/blender/nodes/geometry/CMakeLists.txt
A	source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc

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

diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 76050445023..4880a07115a 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -141,6 +141,7 @@ def mesh_node_items(context):
         yield NodeItem("GeometryNodeLegacySubdivisionSurface", poll=geometry_nodes_legacy_poll)
         yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
 
+    yield NodeItem("GeometryNodeDualMesh")
     yield NodeItem("GeometryNodeMeshBoolean")
     yield NodeItem("GeometryNodeMeshToCurve")
     yield NodeItem("GeometryNodeMeshToPoints")
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 9f6b413dcaa..ebbc149fceb 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -1555,6 +1555,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
 #define GEO_NODE_INPUT_ID 1134
 #define GEO_NODE_SET_ID 1135
 #define GEO_NODE_ATTRIBUTE_DOMAIN_SIZE 1136
+#define GEO_NODE_DUAL_MESH 1137
 
 /** \} */
 
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index bf18eb9951b..4178e7577d8 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -5891,6 +5891,7 @@ static void registerGeometryNodes()
   register_node_type_geo_curve_trim();
   register_node_type_geo_delete_geometry();
   register_node_type_geo_distribute_points_on_faces();
+  register_node_type_geo_dual_mesh();
   register_node_type_geo_edge_split();
   register_node_type_geo_image_texture();
   register_node_type_geo_input_curve_handles();
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index dab86350da8..a3d36788ee2 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -95,6 +95,7 @@ void register_node_type_geo_curve_to_points(void);
 void register_node_type_geo_curve_trim(void);
 void register_node_type_geo_delete_geometry(void);
 void register_node_type_geo_distribute_points_on_faces(void);
+void register_node_type_geo_dual_mesh(void);
 void register_node_type_geo_edge_split(void);
 void register_node_type_geo_image_texture(void);
 void register_node_type_geo_input_curve_handles(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 05985c4062b..7df670c5397 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -347,6 +347,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "
 DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "")
 DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "")
 DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "")
+DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "")
 DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "")
 DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "")
 DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "")
diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt
index 7e7bd7eaf07..4a34e1bd6d0 100644
--- a/source/blender/nodes/geometry/CMakeLists.txt
+++ b/source/blender/nodes/geometry/CMakeLists.txt
@@ -113,6 +113,7 @@ set(SRC
   nodes/node_geo_curve_trim.cc
   nodes/node_geo_delete_geometry.cc
   nodes/node_geo_distribute_points_on_faces.cc
+  nodes/node_geo_dual_mesh.cc
   nodes/node_geo_edge_split.cc
   nodes/node_geo_image_texture.cc
   nodes/node_geo_input_curve_handles.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
new file mode 100644
index 00000000000..25044e045f1
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
@@ -0,0 +1,843 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_task.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_dual_mesh_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+  b.add_input<decl::Geometry>("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH);
+  b.add_input<decl::Bool>("Keep Boundaries")
+      .default_value(false)
+      .description(
+          "Keep non-manifold boundaries of the input mesh in place by avoiding the dual "
+          "transformation there");
+  b.add_output<decl::Geometry>("Dual Mesh");
+}
+
+enum class EdgeType : int8_t {
+  Loose = 0,       /* No polygons connected to it. */
+  Boundary = 1,    /* An edge connected to exactly one polygon. */
+  Normal = 2,      /* A normal edge (connected to two polygons). */
+  NonManifold = 3, /* An edge connected to more than two polygons. */
+};
+
+static EdgeType get_edge_type_with_added_neighbor(EdgeType old_type)
+{
+  switch (old_type) {
+    case EdgeType::Loose:
+      return EdgeType::Boundary;
+    case EdgeType::Boundary:
+      return EdgeType::Normal;
+    case EdgeType::Normal:
+    case EdgeType::NonManifold:
+      return EdgeType::NonManifold;
+  }
+  BLI_assert_unreachable();
+  return EdgeType::Loose;
+}
+
+enum class VertexType : int8_t {
+  Loose = 0,       /* Either no edges connected or only loose edges connected. */
+  Normal = 1,      /* A normal vertex. */
+  Boundary = 2,    /* A vertex on a boundary edge. */
+  NonManifold = 3, /* A vertex on a non-manifold edge. */
+};
+
+static VertexType get_vertex_type_with_added_neighbor(VertexType old_type)
+{
+  switch (old_type) {
+    case VertexType::Loose:
+      return VertexType::Normal;
+    case VertexType::Normal:
+      return VertexType::Boundary;
+    case VertexType::Boundary:
+    case VertexType::NonManifold:
+      return VertexType::NonManifold;
+  }
+  BLI_assert_unreachable();
+  return VertexType::Loose;
+}
+
+/* Copy only where vertex_types is 'normal'. If keep boundaries is selected, also copy from
+ * boundary vertices. */
+template<typename T>
+static void copy_data_based_on_vertex_types(Span<T> data,
+                                            MutableSpan<T> r_data,
+                                            const Span<VertexType> vertex_types,
+                                            const bool keep_boundaries)
+{
+  if (keep_boundaries) {
+    int out_i = 0;
+    for (const int i : data.index_range()) {
+      if (ELEM(vertex_types[i], VertexType::Normal, VertexType::Boundary)) {
+        r_data[out_i] = data[i];
+        out_i++;
+      }
+    }
+  }
+  else {
+    int out_i = 0;
+    for (const int i : data.index_range()) {
+      if (vertex_types[i] == VertexType::Normal) {
+        r_data[out_i] = data[i];
+        out_i++;
+      }
+    }
+  }
+}
+
+template<typename T>
+static void copy_data_based_on_pairs(Span<T> data,
+                                     MutableSpan<T> r_data,
+                                     const Span<std::pair<int, int>> new_to_old_map)
+{
+  for (const std::pair<int, int> &pair : new_to_old_map) {
+    r_data[pair.first] = data[pair.second];
+  }
+}
+
+/* Copy using the map. */
+template<typename T>
+static void copy_data_based_on_new_to_old_map(Span<T> data,
+                                              MutableSpan<T> r_data,
+                                              const Span<int> new_to_old_map)
+{
+  for (const int i : r_data.index_range()) {
+    const int old_i = new_to_old_map[i];
+    r_data[i] = data[old_i];
+  }
+}
+
+/**
+ * Transfers the attributes from the original mesh to the new mesh using the following logic:
+ *  - If the attribute was on the face domain it is now on the point domain, and this is true
+ *    for all faces, so we can just copy these.
+ *  - If the attribute was on the vertex domain there are three cases:
+ *    - It was a 'bad' vertex so it is not in the dual mesh, and we can just ignore it
+ *    - It was a normal vertex so it has a corresponding face in the dual mesh to which we can
+ *      transfer.
+ *    - It was a boundary vertex so it has a corresponding face, if keep_boundaries is true.
+ *      Otherwise we can just ignore it.
+ *  - If the attribute was on the edge domain we lookup for the new edges which edge it originated
+ *    from using `new_to_old_edges_map`. We have to do it in this reverse order, because there can
+ *    be more edges in the new mesh if keep boundaries is on.
+ *  - We do the same thing for face corners as we do for edges.
+ *
+ * Some of the vertices (on the boundary) in the dual mesh don't come from faces, but from edges or
+ * vertices. For these the `boundary_vertex_to_relevant_face_map` is used, which maps them to the
+ * closest face.
+ */
+static void transfer_attributes(
+    const Map<AttributeIDRef, AttributeKind> &attributes,
+    const Span<VertexType> vertex_types,
+    const bool keep_boundaries,
+    const Span<int> new_to_old_edges_map,
+    const Span<int> new_to_old_face_corners_map,
+    const Span<std::pair<int, int>> boundary_ver

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list