[Bf-blender-cvs] [85908e9edf3] master: Geometry Nodes: new Interpolate Curves node

Jacques Lucke noreply at git.blender.org
Fri Jan 20 12:10:48 CET 2023


Commit: 85908e9edf3dfefdc36714f07a554f480ff5d230
Author: Jacques Lucke
Date:   Fri Jan 20 12:09:29 2023 +0100
Branches: master
https://developer.blender.org/rB85908e9edf3dfefdc36714f07a554f480ff5d230

Geometry Nodes: new Interpolate Curves node

This adds a new `Interpolate Curves` node. It allows generating new curves
between a set of existing guide curves. This is essential for procedural hair.

Usage:
- One has to provide a set of guide curves and a set of root positions for
  the generated curves. New curves are created starting from these root
  positions. The N closest guide curves are used for the interpolation.
- An additional up vector can be provided for every guide curve and
  root position. This is typically a surface normal or nothing. This allows
  generating child curves that are properly oriented based on the
  surface orientation.
- Sometimes a point should only be interpolated using a subset of the
  guides. This can be achieved using the `Guide Group ID` and
  `Point Group ID` inputs. The curve generated at a specific point will
  only take the guides with the same id into account. This allows e.g.
  for hair parting.
- The `Max Neighbors` input limits how many guide curves are taken
  into account for every interpolated curve.

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

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

M	release/scripts/startup/bl_ui/node_add_menu_geometry.py
M	source/blender/blenkernel/BKE_node.h
M	source/blender/blenlib/BLI_length_parameterize.hh
M	source/blender/blenlib/BLI_task.hh
M	source/blender/blenlib/intern/offset_indices.cc
M	source/blender/nodes/NOD_static_types.h
M	source/blender/nodes/geometry/CMakeLists.txt
M	source/blender/nodes/geometry/node_geometry_register.cc
M	source/blender/nodes/geometry/node_geometry_register.hh
A	source/blender/nodes/geometry/nodes/node_geo_interpolate_curves.cc
M	tests/python/CMakeLists.txt

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

diff --git a/release/scripts/startup/bl_ui/node_add_menu_geometry.py b/release/scripts/startup/bl_ui/node_add_menu_geometry.py
index 2554734d903..cdbd05b74a3 100644
--- a/release/scripts/startup/bl_ui/node_add_menu_geometry.py
+++ b/release/scripts/startup/bl_ui/node_add_menu_geometry.py
@@ -101,6 +101,7 @@ class NODE_MT_geometry_node_GEO_CURVE_OPERATIONS(Menu):
         node_add_menu.add_node_type(layout, "GeometryNodeDeformCurvesOnSurface")
         node_add_menu.add_node_type(layout, "GeometryNodeFillCurve")
         node_add_menu.add_node_type(layout, "GeometryNodeFilletCurve")
+        node_add_menu.add_node_type(layout, "GeometryNodeInterpolateCurves")
         node_add_menu.add_node_type(layout, "GeometryNodeResampleCurve")
         node_add_menu.add_node_type(layout, "GeometryNodeReverseCurve")
         node_add_menu.add_node_type(layout, "GeometryNodeSampleCurve")
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 915ca87621a..386fe7fc77f 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -1562,6 +1562,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
 
 /** \} */
 
+#define GEO_NODE_INTERPOLATE_CURVES 2000
+
 void BKE_node_system_init(void);
 void BKE_node_system_exit(void);
 
diff --git a/source/blender/blenlib/BLI_length_parameterize.hh b/source/blender/blenlib/BLI_length_parameterize.hh
index d81bcbe1e7a..df00e004060 100644
--- a/source/blender/blenlib/BLI_length_parameterize.hh
+++ b/source/blender/blenlib/BLI_length_parameterize.hh
@@ -105,7 +105,7 @@ inline void sample_at_length(const Span<float> accumulated_segment_lengths,
 
   BLI_assert(lengths.size() > 0);
   BLI_assert(sample_length >= 0.0f);
-  BLI_assert(sample_length <= lengths.last());
+  BLI_assert(sample_length <= lengths.last() + 0.00001f);
 
   if (hint != nullptr && hint->segment_index >= 0) {
     const float length_in_segment = sample_length - hint->segment_start;
diff --git a/source/blender/blenlib/BLI_task.hh b/source/blender/blenlib/BLI_task.hh
index e7d9a21439a..c726691ad46 100644
--- a/source/blender/blenlib/BLI_task.hh
+++ b/source/blender/blenlib/BLI_task.hh
@@ -37,7 +37,7 @@
 namespace blender::threading {
 
 template<typename Range, typename Function>
-void parallel_for_each(Range &range, const Function &function)
+void parallel_for_each(Range &&range, const Function &function)
 {
 #ifdef WITH_TBB
   tbb::parallel_for_each(range, function);
diff --git a/source/blender/blenlib/intern/offset_indices.cc b/source/blender/blenlib/intern/offset_indices.cc
index fee57e32ffa..2ac11fe631e 100644
--- a/source/blender/blenlib/intern/offset_indices.cc
+++ b/source/blender/blenlib/intern/offset_indices.cc
@@ -9,7 +9,7 @@ void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, const int
   int offset = start_offset;
   for (const int i : counts_to_offsets.index_range().drop_back(1)) {
     const int count = counts_to_offsets[i];
-    BLI_assert(count > 0);
+    BLI_assert(count >= 0);
     counts_to_offsets[i] = offset;
     offset += count;
   }
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index b9f747b2e4f..33fc7249fad 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -430,6 +430,8 @@ DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer
 DefNode(GeometryNode, GEO_NODE_VOLUME_CUBE, 0, "VOLUME_CUBE", VolumeCube, "Volume Cube", "Generate a dense volume with a field that controls the density at each grid voxel based on its position")
 DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "Generate a mesh on the \"surface\" of a volume")
 
+DefNode(GeometryNode, GEO_NODE_INTERPOLATE_CURVES, 0, "INTERPOLATE_CURVES", InterpolateCurves, "Interpolate Curves", "Generate new curves on points by interpolating between existing curves")
+
 /* undefine macros */
 #undef DefNode
 
diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt
index 8012b463a54..9fd3feff27e 100644
--- a/source/blender/nodes/geometry/CMakeLists.txt
+++ b/source/blender/nodes/geometry/CMakeLists.txt
@@ -106,6 +106,7 @@ set(SRC
   nodes/node_geo_input_tangent.cc
   nodes/node_geo_instance_on_points.cc
   nodes/node_geo_instances_to_points.cc
+  nodes/node_geo_interpolate_curves.cc
   nodes/node_geo_is_viewport.cc
   nodes/node_geo_join_geometry.cc
   nodes/node_geo_material_replace.cc
diff --git a/source/blender/nodes/geometry/node_geometry_register.cc b/source/blender/nodes/geometry/node_geometry_register.cc
index 8d24d9bd732..149fb6752ab 100644
--- a/source/blender/nodes/geometry/node_geometry_register.cc
+++ b/source/blender/nodes/geometry/node_geometry_register.cc
@@ -90,6 +90,7 @@ void register_geometry_nodes()
   register_node_type_geo_input_tangent();
   register_node_type_geo_instance_on_points();
   register_node_type_geo_instances_to_points();
+  register_node_type_geo_interpolate_curves();
   register_node_type_geo_is_viewport();
   register_node_type_geo_join_geometry();
   register_node_type_geo_material_replace();
diff --git a/source/blender/nodes/geometry/node_geometry_register.hh b/source/blender/nodes/geometry/node_geometry_register.hh
index ca64e7b9904..5984bfa83ce 100644
--- a/source/blender/nodes/geometry/node_geometry_register.hh
+++ b/source/blender/nodes/geometry/node_geometry_register.hh
@@ -87,6 +87,7 @@ void register_node_type_geo_input_spline_resolution();
 void register_node_type_geo_input_tangent();
 void register_node_type_geo_instance_on_points();
 void register_node_type_geo_instances_to_points();
+void register_node_type_geo_interpolate_curves();
 void register_node_type_geo_is_viewport();
 void register_node_type_geo_join_geometry();
 void register_node_type_geo_material_replace();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_interpolate_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_interpolate_curves.cc
new file mode 100644
index 00000000000..2142fa666cb
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_interpolate_curves.cc
@@ -0,0 +1,862 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "node_geometry_util.hh"
+
+#include "BLI_kdtree.h"
+#include "BLI_length_parameterize.hh"
+#include "BLI_task.hh"
+
+#include "BKE_curves.hh"
+#include "BKE_curves_utils.hh"
+
+#include "DNA_pointcloud_types.h"
+
+namespace blender::nodes::node_geo_interpolate_curves_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+  b.add_input<decl::Geometry>(N_("Guide Curves"))
+      .description(N_("Base curves that new curves are interpolated between"));
+  b.add_input<decl::Vector>(N_("Guide Up"))
+      .field_on({0})
+      .hide_value()
+      .description(N_("Optional up vector that is typically a surface normal"));
+  b.add_input<decl::Int>(N_("Guide Group ID"))
+      .field_on({0})
+      .hide_value()
+      .description(N_("Splits guides into separate groups. New curves interpolate existing curves "
+                      "from a single group"));
+  b.add_input<decl::Geometry>(N_("Points"))
+      .description(N_("First control point positions for new interpolated curves"));
+  b.add_input<decl::Vector>(N_("Point Up"))
+      .field_on({3})
+      .hide_value()
+      .description(N_("Optional up vector that is typically a surface normal"));
+  b.add_input<decl::Int>(N_("Point Group ID"))
+      .field_on({3})
+      .hide_value()
+      .description(N_("The curve group to interpolate in"));
+  b.add_input<decl::Int>(N_("Max Neighbors"))
+      .default_value(4)
+      .min(1)
+      .description(N_(
+          "Maximum amount of close guide curves that are taken into account for interpolation"));
+  b.add_output<decl::Geometry>(N_("Curves")).propagate_all();
+  b.add_output<decl::Int>(N_("Closest Index"))
+      .field_on_all()
+      .description(N_("Index of the closest guide curve for each generated curve"));
+  b.add_output<decl::Float>(N_("Closest Weight"))
+      .field_on_all()
+      .description(N_("Weight of the closest guide curve for each generated curve"));
+}
+
+/**
+ * Guides are split into groups. Every point will only interpolate between guides within the group
+ * with the same id.
+ */
+static MultiValueMap<int, int> separate_guides_by_group(const VArray<int> &guide_group_ids)
+{
+  MultiValueMap<int, int> guides_by_group;
+  for (const int curve_i : guide_group_ids.index_range()) {
+    const int group = guide_group_ids[curve_i];
+    guides_by_group.add(group, curve_i);
+  }
+  return guides_by_group;
+}
+
+/**
+ * Checks if all curves within a group have the same number of points. If yes, a better
+ * interpolation algorithm can be used, that does not require resampling curves.
+ */
+static Map<int, int> compute_points_per_curve_by_group(
+    const MultiValueMap<int, int> &guides_by_group, const bke::CurvesGeometry &guide_curves)
+{
+  const OffsetIndices points_by_curve = guide_curves.points_by_curve();
+  Map<int, int> points_per_curve_by_group;
+  for (const auto &[group, guide_curve_indices] : guides_by_group.items()) {
+    int group_control_points = points_by_curve.size(guide_curve_indices[0]);
+    for (const int guide_curve_i : guide_curve_indices.as_span().drop_front(1)) {
+      const int control_points = points_by_curve.size(guide_curve_i);
+      if (group_control_points != control_points) {
+        group_control_points = -1;
+        break;
+      }
+    }
+    if (group_control_points != -1) {
+      points_per_curve_by_group.add(group, group_control_points);
+    }
+  }
+  return points_per_curve_by_group;
+}
+
+/**
+ * Build a kdtree for every guide group.
+ */
+static Map<int, KDTree_3d *> build_kdtrees_for_root_positions(
+    const MultiValueMap<int, int> &guides_by_group, const bke::CurvesGeometry &guide_curves)
+{
+  Map<int, KDTree_3d *> kdtrees;
+  const Span<float3> positions = guide_curves.positions();
+  const Span<int> offsets = guide_curves.offsets();
+
+  for (const auto item : guides_by_group.items()) {
+    const int group = item.key;
+    const Span<int> guide_indices = item.value;
+
+    K

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list