Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2021-05-07 23:37:06 +0300
committerHans Goudey <h.goudey@me.com>2021-05-07 23:37:06 +0300
commit3185084efbe493bf232fb0de62b341ae2a2e2936 (patch)
treeb1c32e8646cfe9147801cdc1160cd4293db0ea97 /source/blender/nodes
parent960535ddf3446204dbfcc0f23040bdb74f7a9720 (diff)
Geometry Noes: Curve Resample Node
This node generates a naturally parametarized (even length edge) poly spline version of every spline in the input. There are two modes, "Count", and "Length". These are similar to the same options for the line primitive node in end points mode. I implemented this instead of a "Sample Points" node, because for this operation it's trivial to keep the result as a curve, which is nice since it increases flexibility, and because it can make instancing simpler, i.e. using the transforms of each evaluated point rather than requiring the construction of a "rotation" attribute. Differential Revision: https://developer.blender.org/D11173
Diffstat (limited to 'source/blender/nodes')
-rw-r--r--source/blender/nodes/CMakeLists.txt1
-rw-r--r--source/blender/nodes/NOD_geometry.h1
-rw-r--r--source/blender/nodes/NOD_static_types.h1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc201
4 files changed, 204 insertions, 0 deletions
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 2bf419b5ea2..73645d5eb27 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -162,6 +162,7 @@ set(SRC
geometry/nodes/node_geo_collection_info.cc
geometry/nodes/node_geo_common.cc
geometry/nodes/node_geo_curve_to_mesh.cc
+ geometry/nodes/node_geo_curve_resample.cc
geometry/nodes/node_geo_edge_split.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 7f77142b7e4..847e8ac4b00 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -50,6 +50,7 @@ void register_node_type_geo_boolean(void);
void register_node_type_geo_bounding_box(void);
void register_node_type_geo_collection_info(void);
void register_node_type_geo_curve_to_mesh(void);
+void register_node_type_geo_curve_resample(void);
void register_node_type_geo_edge_split(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 f445e520c95..f6546951748 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_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Attribute Transfer", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "ATTRIBUTE_CURVE_MAP", AttributeCurveMap, "Attribute Curve Map", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "")
/* undefine macros */
#undef DefNode
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
new file mode 100644
index 00000000000..9c7b036bda0
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -0,0 +1,201 @@
+/*
+ * 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_array.hh"
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+using blender::fn::GVArray_For_Span;
+using blender::fn::GVArray_Typed;
+
+static bNodeSocketTemplate geo_node_curve_resample_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_INT, N_("Count"), 10, 0, 0, 0, 1, 100000},
+ {SOCK_FLOAT, N_("Length"), 0.1f, 0.0f, 0.0f, 0.0f, 0.001f, FLT_MAX, PROP_DISTANCE},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_resample_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+}
+
+static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveResample *data = (NodeGeometryCurveResample *)MEM_callocN(
+ sizeof(NodeGeometryCurveResample), __func__);
+
+ data->mode = GEO_NODE_CURVE_SAMPLE_COUNT;
+ node->storage = data;
+}
+
+static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)node->storage;
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+
+ bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next;
+ bNodeSocket *length_socket = count_socket->next;
+
+ nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT);
+ nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
+}
+
+namespace blender::nodes {
+
+struct SampleModeParam {
+ GeometryNodeCurveSampleMode mode;
+ std::optional<float> length;
+ std::optional<int> count;
+};
+
+template<typename T>
+static void sample_span_to_output_spline(const Spline &input_spline,
+ Span<float> index_factors,
+ const VArray<T> &input_data,
+ MutableSpan<T> output_data)
+{
+ BLI_assert(input_data.size() == input_spline.evaluated_points_size());
+
+ parallel_for(output_data.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ const Spline::LookupResult interp = input_spline.lookup_data_from_index_factor(
+ index_factors[i]);
+ output_data[i] = blender::attribute_math::mix2(interp.factor,
+ input_data[interp.evaluated_index],
+ input_data[interp.next_evaluated_index]);
+ }
+ });
+}
+
+static SplinePtr resample_spline(const Spline &input_spline, const int count)
+{
+ std::unique_ptr<PolySpline> output_spline = std::make_unique<PolySpline>();
+ output_spline->set_cyclic(input_spline.is_cyclic());
+ output_spline->normal_mode = input_spline.normal_mode;
+ output_spline->resize(count);
+
+ Array<float> uniform_samples = input_spline.sample_uniform_index_factors(count);
+
+ {
+ GVArray_For_Span positions(input_spline.evaluated_positions());
+ GVArray_Typed<float3> positions_typed(positions);
+ sample_span_to_output_spline<float3>(
+ input_spline, uniform_samples, positions_typed, output_spline->positions());
+ }
+ {
+ GVArrayPtr interpolated_data = input_spline.interpolate_to_evaluated_points(
+ GVArray_For_Span(input_spline.radii()));
+ GVArray_Typed<float> interpolated_data_typed{*interpolated_data};
+ sample_span_to_output_spline<float>(
+ input_spline, uniform_samples, interpolated_data_typed, output_spline->radii());
+ }
+ {
+ GVArrayPtr interpolated_data = input_spline.interpolate_to_evaluated_points(
+ GVArray_For_Span(input_spline.tilts()));
+ GVArray_Typed<float> interpolated_data_typed{*interpolated_data};
+ sample_span_to_output_spline<float>(
+ input_spline, uniform_samples, interpolated_data_typed, output_spline->tilts());
+ }
+
+ return output_spline;
+}
+
+static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
+ const SampleModeParam &mode_param)
+{
+ std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
+
+ for (const SplinePtr &spline : input_curve.splines) {
+ if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_COUNT) {
+ BLI_assert(mode_param.count);
+ output_curve->splines.append(resample_spline(*spline, *mode_param.count));
+ }
+ else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
+ BLI_assert(mode_param.length);
+ const float length = spline->length();
+ const int count = length / *mode_param.length;
+ output_curve->splines.append(resample_spline(*spline, count));
+ }
+ }
+
+ return output_curve;
+}
+
+static void geo_node_resample_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ if (!geometry_set.has_curve()) {
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+
+ const CurveEval &input_curve = *geometry_set.get_curve_for_read();
+ NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage;
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+ SampleModeParam mode_param;
+ mode_param.mode = mode;
+ if (mode == GEO_NODE_CURVE_SAMPLE_COUNT) {
+ const int count = params.extract_input<int>("Count");
+ if (count < 1) {
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+ mode_param.count.emplace(count);
+ }
+ else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
+ /* Don't allow asymptotic count increase for low resolution values. */
+ const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f);
+ mode_param.length.emplace(resolution);
+ }
+
+ std::unique_ptr<CurveEval> output_curve = resample_curve(input_curve, mode_param);
+
+ params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release()));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_resample()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_RESAMPLE, "Resample Curve", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_resample_in, geo_node_curve_resample_out);
+ ntype.draw_buttons = geo_node_curve_resample_layout;
+ node_type_storage(
+ &ntype, "NodeGeometryCurveResample", node_free_standard_storage, node_copy_standard_storage);
+ node_type_init(&ntype, geo_node_curve_resample_init);
+ node_type_update(&ntype, geo_node_curve_resample_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_resample_exec;
+ nodeRegisterType(&ntype);
+}