[Bf-blender-cvs] [2b38770ebd4] temp-geometry-nodes-fields: Add initial normal node

Hans Goudey noreply at git.blender.org
Fri Sep 3 06:00:10 CEST 2021


Commit: 2b38770ebd47c958f357753fef46f18ed6c34cb5
Author: Hans Goudey
Date:   Thu Sep 2 23:00:00 2021 -0500
Branches: temp-geometry-nodes-fields
https://developer.blender.org/rB2b38770ebd47c958f357753fef46f18ed6c34cb5

Add initial normal node

In contrast with the read-only "normal" attribute on the face domain,
this node gives normalized values for every mesh domain, including
edges and corners. This is preferrable since it's much more predictable
to work with.

The implementation is a little more complicated than I would like,
but could be improved with const access to lazy calculation of normals
on meshes, which is something I've been planning to work on anyway.

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

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/CMakeLists.txt
M	source/blender/nodes/NOD_geometry.h
M	source/blender/nodes/NOD_static_types.h
A	source/blender/nodes/geometry/nodes/node_geo_input_normal.cc

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

diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index f03f86818e5..18e21e22102 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -546,6 +546,7 @@ geometry_node_categories = [
         NodeItem("GeometryNodeIsViewport"),
         NodeItem("GeometryNodePosition"),
         NodeItem("GeometryNodeIndex"),
+        NodeItem("GeometryNodeInputNormal"),
     ]),
     GeometryNodeCategory("GEO_MATERIAL", "Material", items=[
         NodeItem("GeometryNodeMaterialAssign"),
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 9beb80ec0f0..6b9a0134ce7 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -1486,6 +1486,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
 #define GEO_NODE_INPUT_POSITION 1076
 #define GEO_NODE_SET_POSITION 1077
 #define GEO_NODE_INPUT_INDEX 1078
+#define GEO_NODE_INPUT_NORMAL 1079
 
 /** \} */
 
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index 5cda79d8ee9..504cf0d71a1 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -5175,6 +5175,7 @@ static void registerGeometryNodes()
   register_node_type_geo_edge_split();
   register_node_type_geo_input_index();
   register_node_type_geo_input_material();
+  register_node_type_geo_input_normal();
   register_node_type_geo_input_position();
   register_node_type_geo_is_viewport();
   register_node_type_geo_join_geometry();
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 0bf796b48b4..4d52358687e 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -186,6 +186,7 @@ set(SRC
   geometry/nodes/node_geo_delete_geometry.cc
   geometry/nodes/node_geo_edge_split.cc
   geometry/nodes/node_geo_input_material.cc
+  geometry/nodes/node_geo_input_normal.cc
   geometry/nodes/node_geo_index.cc
   geometry/nodes/node_geo_is_viewport.cc
   geometry/nodes/node_geo_join_geometry.cc
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 5cd19cd2b81..c7705b4ef63 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -73,6 +73,7 @@ void register_node_type_geo_delete_geometry(void);
 void register_node_type_geo_edge_split(void);
 void register_node_type_geo_input_index(void);
 void register_node_type_geo_input_material(void);
+void register_node_type_geo_input_normal(void);
 void register_node_type_geo_input_position(void);
 void register_node_type_geo_is_viewport(void);
 void register_node_type_geo_join_geometry(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index f35886f2c01..5601ff1d6ef 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -314,6 +314,7 @@ DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeom
 DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
 DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", Index, "Index", "")
 DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "")
 DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", Position, "Position", "")
 DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
 DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
new file mode 100644
index 00000000000..564337394f8
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
@@ -0,0 +1,199 @@
+/*
+ * 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 "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_input_normal_declare(NodeDeclarationBuilder &b)
+{
+  b.add_output<decl::Vector>("Normal");
+}
+
+static GVArrayPtr mesh_face_normals(const Mesh &mesh,
+                                    const Span<MVert> verts,
+                                    const Span<MPoly> polys,
+                                    const Span<MLoop> loops,
+                                    const IndexMask mask)
+{
+  /* Use existing normals to avoid unnecessarily recalculating them, if possible. */
+  if (!(mesh.runtime.cd_dirty_poly & CD_MASK_NORMAL) &&
+      CustomData_has_layer(&mesh.pdata, CD_NORMAL)) {
+    const void *data = CustomData_get_layer(&mesh.pdata, CD_NORMAL);
+
+    return std::make_unique<fn::GVArray_For_Span<float3>>(
+        Span<float3>((const float3 *)data, mesh.totpoly));
+  }
+
+  auto normal_fn = [verts, polys, loops](const int i) -> float3 {
+    float3 normal;
+    const MPoly &poly = polys[i];
+    BKE_mesh_calc_poly_normal(&poly, &loops[poly.loopstart], verts.data(), normal);
+    return normal;
+  };
+
+  return std::make_unique<
+      fn::GVArray_For_EmbeddedVArray<float3, VArray_For_Func<float3, decltype(normal_fn)>>>(
+      mask.min_array_size(), mask.min_array_size(), normal_fn);
+}
+
+static GVArrayPtr mesh_vertex_normals(const Mesh &mesh,
+                                      const Span<MVert> verts,
+                                      const Span<MPoly> polys,
+                                      const Span<MLoop> loops,
+                                      const IndexMask mask)
+{
+  /* Use existing normals to avoid unnecessarily recalculating them, if possible. */
+  if (!(mesh.runtime.cd_dirty_vert & CD_MASK_NORMAL) &&
+      CustomData_has_layer(&mesh.vdata, CD_NORMAL)) {
+    const void *data = CustomData_get_layer(&mesh.pdata, CD_NORMAL);
+
+    return std::make_unique<fn::GVArray_For_Span<float3>>(
+        Span<float3>((const float3 *)data, mesh.totvert));
+  }
+
+  /* If the normals are dirty, they must be recalculated for the output of this node's field
+   * source. Ideally vertex normals could be calculated lazily on a const mesh, protected with a
+   * mutex. But that's not possible at the moment, so we take ownership of the results. Sadly we
+   * must also create a copy of MVert to use the mesh normals API.
+   *
+   * Use mask.min_array_size() to avoid calculating a final chunk of data if possible. */
+  Array<MVert> temp_verts(verts);
+  Array<float3> normals(mask.min_array_size());
+  BKE_mesh_calc_normals_poly_and_vertex(temp_verts.data(),
+                                        mask.min_array_size(),
+                                        loops.data(),
+                                        loops.size(),
+                                        polys.data(),
+                                        polys.size(),
+                                        nullptr,
+                                        (float(*)[3])normals.data());
+
+  return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals));
+}
+
+static const GVArray *construct_mesh_normals_gvarray(const MeshComponent &mesh_component,
+                                                     const Mesh &mesh,
+                                                     const IndexMask mask,
+                                                     const AttributeDomain domain,
+                                                     ResourceScope &scope)
+{
+  Span<MVert> verts{mesh.mvert, mesh.totvert};
+  Span<MEdge> edges{mesh.medge, mesh.totedge};
+  Span<MPoly> polys{mesh.mpoly, mesh.totpoly};
+  Span<MLoop> loops{mesh.mloop, mesh.totloop};
+
+  switch (domain) {
+    case ATTR_DOMAIN_FACE: {
+      return scope.add_value(mesh_face_normals(mesh, verts, polys, loops, mask), __func__).get();
+    }
+    case ATTR_DOMAIN_POINT: {
+      return scope.add_value(mesh_vertex_normals(mesh, verts, polys, loops, mask), __func__).get();
+    }
+    case ATTR_DOMAIN_EDGE: {
+      /* In this case, start with vertex normals and convert to the edge domain, since the
+       * conversion from edges to vertices is very simple. Use the full mask since the edges
+       * might use the vertex normal from any index. */
+      GVArrayPtr vert_normals = mesh_vertex_normals(
+          mesh, verts, polys, loops, IndexRange(verts.size()));
+      Span<float3> vert_normals_span = vert_normals->get_internal_span().typed<float3>();
+      Array<float3> edge_normals(mask.min_array_size());
+
+      /* Use "manual" domain interpolation instead of the GeometryComponent API to avoid
+       * calculating unnecessary values and to allow normalizing the result much more simply. */
+      for (const int i : mask) {
+        const MEdge &edge = edges[i];
+        edge_normals[i] = float3::interpolate(
+                              vert_normals_span[edge.v1], vert_normals_span[edge.v2], 0.5f)
+                              .normalized();
+      }
+
+      return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float3>>>(
+          __func__, std::move(edge_normals));
+    }
+    case ATTR_DOMAIN_CORNER: {
+      /* The normals on corners are just the mesh's face normals, so start with the face normal
+       * array and copy the face normal for each of its corners. */
+     

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list