[Bf-blender-cvs] [f4af21038d8] master: Geometry Nodes: Move normal field input to be usable elsewhere

Hans Goudey noreply at git.blender.org
Mon Jan 10 23:41:14 CET 2022


Commit: f4af21038d82bba525dec4bfd291c43504607acf
Author: Hans Goudey
Date:   Mon Jan 10 16:41:05 2022 -0600
Branches: master
https://developer.blender.org/rBf4af21038d82bba525dec4bfd291c43504607acf

Geometry Nodes: Move normal field input to be usable elsewhere

This commit moves the normal field input to `BKE_geometry_set.hh`
from the node file so that normals can be used as an implicit input to
other nodes.

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

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

M	source/blender/blenkernel/BKE_geometry_set.hh
M	source/blender/blenkernel/intern/geometry_component_curve.cc
M	source/blender/blenkernel/intern/geometry_component_mesh.cc
M	source/blender/blenkernel/intern/geometry_set.cc
M	source/blender/nodes/geometry/nodes/node_geo_input_normal.cc

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

diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index a429aecd828..acb89637f20 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -1078,6 +1078,30 @@ class IDAttributeFieldInput : public GeometryFieldInput {
   bool is_equal_to(const fn::FieldNode &other) const override;
 };
 
+VArray<float3> curve_normals_varray(const CurveComponent &component, const AttributeDomain domain);
+
+VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component,
+                                   const Mesh &mesh,
+                                   const IndexMask mask,
+                                   const AttributeDomain domain);
+
+class NormalFieldInput : public GeometryFieldInput {
+ public:
+  NormalFieldInput() : GeometryFieldInput(CPPType::get<float3>())
+  {
+    category_ = Category::Generated;
+  }
+
+  GVArray get_varray_for_context(const GeometryComponent &component,
+                                 const AttributeDomain domain,
+                                 IndexMask mask) const override;
+
+  std::string socket_inspection_name() const override;
+
+  uint64_t hash() const override;
+  bool is_equal_to(const fn::FieldNode &other) const override;
+};
+
 class AnonymousAttributeFieldInput : public GeometryFieldInput {
  private:
   /**
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
index 1e24b29038d..16edbc36f9c 100644
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -14,6 +14,8 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+#include "BLI_task.hh"
+
 #include "DNA_ID_enums.h"
 #include "DNA_curve_types.h"
 
@@ -388,6 +390,98 @@ static const CurveEval *get_curve_from_component_for_read(const GeometryComponen
 
 namespace blender::bke {
 
+/* -------------------------------------------------------------------- */
+/** \name Curve Normals Access
+ * \{ */
+
+static void calculate_bezier_normals(const BezierSpline &spline, MutableSpan<float3> normals)
+{
+  Span<int> offsets = spline.control_point_offsets();
+  Span<float3> evaluated_normals = spline.evaluated_normals();
+  for (const int i : IndexRange(spline.size())) {
+    normals[i] = evaluated_normals[offsets[i]];
+  }
+}
+
+static void calculate_poly_normals(const PolySpline &spline, MutableSpan<float3> normals)
+{
+  normals.copy_from(spline.evaluated_normals());
+}
+
+/**
+ * Because NURBS control points are not necessarily on the path, the normal at the control points
+ * is not well defined, so create a temporary poly spline to find the normals. This requires extra
+ * copying currently, but may be more efficient in the future if attributes have some form of CoW.
+ */
+static void calculate_nurbs_normals(const NURBSpline &spline, MutableSpan<float3> normals)
+{
+  PolySpline poly_spline;
+  poly_spline.resize(spline.size());
+  poly_spline.positions().copy_from(spline.positions());
+  poly_spline.tilts().copy_from(spline.tilts());
+  normals.copy_from(poly_spline.evaluated_normals());
+}
+
+static Array<float3> curve_normal_point_domain(const CurveEval &curve)
+{
+  Span<SplinePtr> splines = curve.splines();
+  Array<int> offsets = curve.control_point_offsets();
+  const int total_size = offsets.last();
+  Array<float3> normals(total_size);
+
+  threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
+    for (const int i : range) {
+      const Spline &spline = *splines[i];
+      MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())};
+      switch (splines[i]->type()) {
+        case Spline::Type::Bezier:
+          calculate_bezier_normals(static_cast<const BezierSpline &>(spline), spline_normals);
+          break;
+        case Spline::Type::Poly:
+          calculate_poly_normals(static_cast<const PolySpline &>(spline), spline_normals);
+          break;
+        case Spline::Type::NURBS:
+          calculate_nurbs_normals(static_cast<const NURBSpline &>(spline), spline_normals);
+          break;
+      }
+    }
+  });
+  return normals;
+}
+
+VArray<float3> curve_normals_varray(const CurveComponent &component, const AttributeDomain domain)
+{
+  const CurveEval *curve = component.get_for_read();
+  if (curve == nullptr) {
+    return nullptr;
+  }
+
+  if (domain == ATTR_DOMAIN_POINT) {
+    const Span<SplinePtr> splines = curve->splines();
+
+    /* Use a reference to evaluated normals if possible to avoid an allocation and a copy.
+     * This is only possible when there is only one poly spline. */
+    if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) {
+      const PolySpline &spline = static_cast<PolySpline &>(*splines.first());
+      return VArray<float3>::ForSpan(spline.evaluated_normals());
+    }
+
+    Array<float3> normals = curve_normal_point_domain(*curve);
+    return VArray<float3>::ForContainer(std::move(normals));
+  }
+
+  if (domain == ATTR_DOMAIN_CURVE) {
+    Array<float3> point_normals = curve_normal_point_domain(*curve);
+    VArray<float3> varray = VArray<float3>::ForContainer(std::move(point_normals));
+    return component.attribute_try_adapt_domain<float3>(
+        std::move(varray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
+  }
+
+  return nullptr;
+}
+
+/** \} */
+
 /* -------------------------------------------------------------------- */
 /** \name Builtin Spline Attributes
  *
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index cc15e6d7b84..2b4238d26bb 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -122,6 +122,128 @@ void MeshComponent::ensure_owns_direct_data()
 
 /** \} */
 
+/* -------------------------------------------------------------------- */
+/** \name Mesh Normals Field Input
+ * \{ */
+
+namespace blender::bke {
+
+static VArray<float3> 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 VArray<float3>::ForSpan({(const float3 *)data, polys.size()});
+  }
+
+  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 VArray<float3>::ForFunc(mask.min_array_size(), normal_fn);
+}
+
+static VArray<float3> 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 VArray<float3>::ForSpan({(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, 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. This can be improved by adding mutex-protected lazy
+   * calculation of normals on meshes.
+   *
+   * Use mask.min_array_size() to avoid calculating a final chunk of data if possible. */
+  Array<MVert> temp_verts(verts);
+  Array<float3> normals(verts.size()); /* Use full size for accumulation from faces. */
+  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 VArray<float3>::ForContainer(std::move(normals));
+}
+
+VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component,
+                                   const Mesh &mesh,
+                                   const IndexMask mask,
+                                   const AttributeDomain domain)
+{
+  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 mesh_face_normals(mesh, verts, polys, loops, mask);
+    }
+    case ATTR_DOMAIN_POINT: {
+      return mesh_vertex_normals(mesh, verts, polys, loops, mask);
+    }
+    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. */
+      GVArray 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 i

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list