[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