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
path: root/source
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2021-10-14 20:06:48 +0300
committerHans Goudey <h.goudey@me.com>2021-10-14 20:06:48 +0300
commit17b8da719606abfc9e3076555c626e6fc38dd7c5 (patch)
treec277e3ee63ce1c7b79509121f515c914b44c1826 /source
parent1996efe7aa2e0edc9887ad34bc59e2ab911e2d02 (diff)
Geometry Nodes: Field version of mesh to curve node
This commit adds a fields version of the mesh to curve node, with a field for the input selection. In order to reduce code duplication, it adds the mesh to curve conversion to the new geometry module and calls that implementation from both places. More details on the geometry module can be found here: T86869 Differential Revision: https://developer.blender.org/D12579
Diffstat (limited to 'source')
-rw-r--r--source/blender/CMakeLists.txt1
-rw-r--r--source/blender/blenkernel/BKE_node.h1
-rw-r--r--source/blender/blenkernel/intern/node.cc1
-rw-r--r--source/blender/geometry/CMakeLists.txt42
-rw-r--r--source/blender/geometry/GEO_mesh_to_curve.hh35
-rw-r--r--source/blender/geometry/intern/mesh_to_curve_convert.cc283
-rw-r--r--source/blender/nodes/CMakeLists.txt3
-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/legacy/node_geo_mesh_to_curve.cc273
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc69
11 files changed, 456 insertions, 254 deletions
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt
index fbc0ec440cf..84d31bccc53 100644
--- a/source/blender/CMakeLists.txt
+++ b/source/blender/CMakeLists.txt
@@ -119,6 +119,7 @@ add_subdirectory(blenloader)
add_subdirectory(depsgraph)
add_subdirectory(ikplugin)
add_subdirectory(simulation)
+add_subdirectory(geometry)
add_subdirectory(gpu)
add_subdirectory(imbuf)
add_subdirectory(nodes)
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 19215e75d95..9429da9d6a0 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -1536,6 +1536,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_SCALE_INSTANCES 1121
#define GEO_NODE_ROTATE_INSTANCES 1122
#define GEO_NODE_EDGE_SPLIT 1123
+#define GEO_NODE_MESH_TO_CURVE 1124
/** \} */
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index cb51249ac27..e526475fa95 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -5714,6 +5714,7 @@ static void registerGeometryNodes()
register_node_type_geo_legacy_attribute_randomize();
register_node_type_geo_legacy_delete_geometry();
register_node_type_geo_legacy_material_assign();
+ register_node_type_geo_legacy_mesh_to_curve();
register_node_type_geo_legacy_points_to_volume();
register_node_type_geo_legacy_select_by_material();
register_node_type_geo_legacy_curve_spline_type();
diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt
new file mode 100644
index 00000000000..4e7e0b1ea58
--- /dev/null
+++ b/source/blender/geometry/CMakeLists.txt
@@ -0,0 +1,42 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# 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.
+#
+# The Original Code is Copyright (C) 2006, Blender Foundation
+# All rights reserved.
+
+set(INC
+ .
+ ../blenkernel
+ ../blenlib
+ ../blentranslation
+ ../functions
+ ../makesdna
+ ../makesrna
+ ../../../intern/guardedalloc
+ ${CMAKE_BINARY_DIR}/source/blender/makesdna/intern
+)
+
+set(SRC
+ intern/mesh_to_curve_convert.cc
+ GEO_mesh_to_curve.hh
+)
+
+set(LIB
+ bf_blenkernel
+ bf_blenlib
+)
+
+blender_add_lib(bf_geometry "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/geometry/GEO_mesh_to_curve.hh b/source/blender/geometry/GEO_mesh_to_curve.hh
new file mode 100644
index 00000000000..66459ab79a9
--- /dev/null
+++ b/source/blender/geometry/GEO_mesh_to_curve.hh
@@ -0,0 +1,35 @@
+/*
+ * 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_index_mask.hh"
+
+#include "BKE_spline.hh"
+
+#pragma once
+
+struct Mesh;
+class MeshComponent;
+
+/** \file
+ * \ingroup geo
+ */
+
+namespace blender::geometry {
+
+std::unique_ptr<CurveEval> mesh_to_curve_convert(const MeshComponent &mesh_component,
+ const IndexMask selection);
+
+} // namespace blender::geometry
diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc
new file mode 100644
index 00000000000..64b9fa5c01d
--- /dev/null
+++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc
@@ -0,0 +1,283 @@
+/*
+ * 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_set.hh"
+#include "BLI_task.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_attribute_access.hh"
+#include "BKE_attribute_math.hh"
+#include "BKE_geometry_set.hh"
+#include "BKE_spline.hh"
+
+#include "GEO_mesh_to_curve.hh"
+
+namespace blender::geometry {
+
+template<typename T>
+static void copy_attribute_to_points(const VArray<T> &source_data,
+ Span<int> map,
+ MutableSpan<T> dest_data)
+{
+ for (const int point_index : map.index_range()) {
+ const int vert_index = map[point_index];
+ dest_data[point_index] = source_data[vert_index];
+ }
+}
+
+static void copy_attributes_to_points(CurveEval &curve,
+ const MeshComponent &mesh_component,
+ Span<Vector<int>> point_to_vert_maps)
+{
+ MutableSpan<SplinePtr> splines = curve.splines();
+ Set<bke::AttributeIDRef> source_attribute_ids = mesh_component.attribute_ids();
+
+ /* Copy builtin control point attributes. */
+ if (source_attribute_ids.contains("tilt")) {
+ const fn::GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>(
+ "tilt", ATTR_DOMAIN_POINT, 0.0f);
+ threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) {
+ for (const int i : range) {
+ copy_attribute_to_points<float>(
+ *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts());
+ }
+ });
+ source_attribute_ids.remove_contained("tilt");
+ }
+ if (source_attribute_ids.contains("radius")) {
+ const fn::GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>(
+ "radius", ATTR_DOMAIN_POINT, 1.0f);
+ threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) {
+ for (const int i : range) {
+ copy_attribute_to_points<float>(
+ *radius_attribute, point_to_vert_maps[i], splines[i]->radii());
+ }
+ });
+ source_attribute_ids.remove_contained("radius");
+ }
+
+ for (const bke::AttributeIDRef &attribute_id : source_attribute_ids) {
+ /* Don't copy attributes that are built-in on meshes but not on curves. */
+ if (mesh_component.attribute_is_builtin(attribute_id)) {
+ continue;
+ }
+
+ /* Don't copy anonymous attributes with no references anymore. */
+ if (attribute_id.is_anonymous()) {
+ const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id();
+ if (!BKE_anonymous_attribute_id_has_strong_references(&anonymous_id)) {
+ continue;
+ }
+ }
+
+ const fn::GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(
+ attribute_id, ATTR_DOMAIN_POINT);
+ /* Some attributes might not exist if they were builtin attribute on domains that don't
+ * have any elements, i.e. a face attribute on the output of the line primitive node. */
+ if (!mesh_attribute) {
+ continue;
+ }
+
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type());
+
+ threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ /* Create attribute on the spline points. */
+ splines[i]->attributes.create(attribute_id, data_type);
+ std::optional<fn::GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write(
+ attribute_id);
+ BLI_assert(spline_attribute);
+
+ /* Copy attribute based on the map for this spline. */
+ attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ copy_attribute_to_points<T>(
+ mesh_attribute->typed<T>(), point_to_vert_maps[i], spline_attribute->typed<T>());
+ });
+ }
+ });
+ }
+
+ curve.assert_valid_point_attributes();
+}
+
+struct CurveFromEdgesOutput {
+ std::unique_ptr<CurveEval> curve;
+ Vector<Vector<int>> point_to_vert_maps;
+};
+
+static CurveFromEdgesOutput edges_to_curve(Span<MVert> verts, Span<std::pair<int, int>> edges)
+{
+ std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
+ Vector<Vector<int>> point_to_vert_maps;
+
+ /* Compute the number of edges connecting to each vertex. */
+ Array<int> neighbor_count(verts.size(), 0);
+ for (const std::pair<int, int> &edge : edges) {
+ neighbor_count[edge.first]++;
+ neighbor_count[edge.second]++;
+ }
+
+ /* Compute an offset into the array of neighbor edges based on the counts. */
+ Array<int> neighbor_offsets(verts.size());
+ int start = 0;
+ for (const int i : verts.index_range()) {
+ neighbor_offsets[i] = start;
+ start += neighbor_count[i];
+ }
+
+ /* Use as an index into the "neighbor group" for each vertex. */
+ Array<int> used_slots(verts.size(), 0);
+ /* Calculate the indices of each vertex's neighboring edges. */
+ Array<int> neighbors(edges.size() * 2);
+ for (const int i : edges.index_range()) {
+ const int v1 = edges[i].first;
+ const int v2 = edges[i].second;
+ neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2;
+ neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1;
+ used_slots[v1]++;
+ used_slots[v2]++;
+ }
+
+ /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */
+ Array<int> unused_edges = std::move(used_slots);
+
+ for (const int start_vert : verts.index_range()) {
+ /* The vertex will be part of a cyclic spline. */
+ if (neighbor_count[start_vert] == 2) {
+ continue;
+ }
+
+ /* The vertex has no connected edges, or they were already used. */
+ if (unused_edges[start_vert] == 0) {
+ continue;
+ }
+
+ for (const int i : IndexRange(neighbor_count[start_vert])) {
+ int current_vert = start_vert;
+ int next_vert = neighbors[neighbor_offsets[current_vert] + i];
+
+ if (unused_edges[next_vert] == 0) {
+ continue;
+ }
+
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ Vector<int> point_to_vert_map;
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+
+ /* Follow connected edges until we read a vertex with more than two connected edges. */
+ while (true) {
+ int last_vert = current_vert;
+ current_vert = next_vert;
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+ unused_edges[current_vert]--;
+ unused_edges[last_vert]--;
+
+ if (neighbor_count[current_vert] != 2) {
+ break;
+ }
+
+ const int offset = neighbor_offsets[current_vert];
+ const int next_a = neighbors[offset];
+ const int next_b = neighbors[offset + 1];
+ next_vert = (last_vert == next_a) ? next_b : next_a;
+ }
+
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ point_to_vert_maps.append(std::move(point_to_vert_map));
+ }
+ }
+
+ /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */
+ for (const int start_vert : verts.index_range()) {
+ if (unused_edges[start_vert] != 2) {
+ continue;
+ }
+
+ int current_vert = start_vert;
+ int next_vert = neighbors[neighbor_offsets[current_vert]];
+
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ Vector<int> point_to_vert_map;
+ spline->set_cyclic(true);
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+
+ /* Follow connected edges until we loop back to the start vertex. */
+ while (next_vert != start_vert) {
+ const int last_vert = current_vert;
+ current_vert = next_vert;
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+ unused_edges[current_vert]--;
+ unused_edges[last_vert]--;
+
+ const int offset = neighbor_offsets[current_vert];
+ const int next_a = neighbors[offset];
+ const int next_b = neighbors[offset + 1];
+ next_vert = (last_vert == next_a) ? next_b : next_a;
+ }
+
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ point_to_vert_maps.append(std::move(point_to_vert_map));
+ }
+
+ curve->attributes.reallocate(curve->splines().size());
+ return {std::move(curve), std::move(point_to_vert_maps)};
+}
+
+/**
+ * Get a separate array of the indices for edges in a selection (a boolean attribute).
+ * This helps to make the above algorithm simpler by removing the need to check for selection
+ * in many places.
+ */
+static Vector<std::pair<int, int>> get_selected_edges(const Mesh &mesh, const IndexMask selection)
+{
+ Vector<std::pair<int, int>> selected_edges;
+ for (const int i : selection) {
+ selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2});
+ }
+ return selected_edges;
+}
+
+/**
+ * Convert the mesh into one or many poly splines. Since splines cannot have branches,
+ * intersections of more than three edges will become breaks in splines. Attributes that
+ * are not built-in on meshes and not curves are transferred to the result curve.
+ */
+std::unique_ptr<CurveEval> mesh_to_curve_convert(const MeshComponent &mesh_component,
+ const IndexMask selection)
+{
+ const Mesh &mesh = *mesh_component.get_for_read();
+ Vector<std::pair<int, int>> selected_edges = get_selected_edges(*mesh_component.get_for_read(),
+ selection);
+ CurveFromEdgesOutput output = edges_to_curve({mesh.mvert, mesh.totvert}, selected_edges);
+ copy_attributes_to_points(*output.curve, mesh_component, output.point_to_vert_maps);
+ return std::move(output.curve);
+}
+
+} // namespace blender::geometry
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index f748c89c005..850c5fbffff 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -33,6 +33,7 @@ set(INC
../bmesh
../depsgraph
../functions
+ ../geometry
../gpu
../imbuf
../makesdna
@@ -248,6 +249,7 @@ set(SRC
geometry/nodes/node_geo_mesh_primitive_line.cc
geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
geometry/nodes/node_geo_mesh_subdivide.cc
+ geometry/nodes/node_geo_mesh_to_curve.cc
geometry/nodes/node_geo_mesh_to_points.cc
geometry/nodes/node_geo_object_info.cc
geometry/nodes/node_geo_points_to_vertices.cc
@@ -446,6 +448,7 @@ set(SRC
set(LIB
bf_bmesh
bf_functions
+ bf_geometry
bf_intern_sky
)
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index d34acd6e9aa..3b78912fbaa 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -34,6 +34,7 @@ void register_node_type_geo_legacy_attribute_proximity(void);
void register_node_type_geo_legacy_attribute_randomize(void);
void register_node_type_geo_legacy_delete_geometry(void);
void register_node_type_geo_legacy_material_assign(void);
+void register_node_type_geo_legacy_mesh_to_curve(void);
void register_node_type_geo_legacy_points_to_volume(void);
void register_node_type_geo_legacy_select_by_material(void);
void register_node_type_geo_legacy_curve_spline_type(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index b5640207d04..f3435079563 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -376,6 +376,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Subdivide Mesh", "")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "")
DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "")
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc
index 11349dc7d42..7a27e856cef 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc
@@ -14,248 +14,31 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BLI_array.hh"
-#include "BLI_task.hh"
-
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-
-#include "BKE_attribute_math.hh"
-#include "BKE_spline.hh"
+#include "GEO_mesh_to_curve.hh"
#include "node_geometry_util.hh"
-using blender::Array;
-
namespace blender::nodes {
-static void geo_node_mesh_to_curve_declare(NodeDeclarationBuilder &b)
+static void geo_node_legacy_mesh_to_curve_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Mesh");
b.add_input<decl::String>("Selection");
b.add_output<decl::Geometry>("Curve");
}
-template<typename T>
-static void copy_attribute_to_points(const VArray<T> &source_data,
- Span<int> map,
- MutableSpan<T> dest_data)
-{
- for (const int point_index : map.index_range()) {
- const int vert_index = map[point_index];
- dest_data[point_index] = source_data[vert_index];
- }
-}
-
-static void copy_attributes_to_points(CurveEval &curve,
- const MeshComponent &mesh_component,
- Span<Vector<int>> point_to_vert_maps)
-{
- MutableSpan<SplinePtr> splines = curve.splines();
- Set<AttributeIDRef> source_attribute_ids = mesh_component.attribute_ids();
-
- /* Copy builtin control point attributes. */
- if (source_attribute_ids.contains("tilt")) {
- const GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>(
- "tilt", ATTR_DOMAIN_POINT, 0.0f);
- threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) {
- for (const int i : range) {
- copy_attribute_to_points<float>(
- *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts());
- }
- });
- source_attribute_ids.remove_contained("tilt");
- }
- if (source_attribute_ids.contains("radius")) {
- const GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>(
- "radius", ATTR_DOMAIN_POINT, 1.0f);
- threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) {
- for (const int i : range) {
- copy_attribute_to_points<float>(
- *radius_attribute, point_to_vert_maps[i], splines[i]->radii());
- }
- });
- source_attribute_ids.remove_contained("radius");
- }
-
- /* Don't copy other builtin control point attributes. */
- source_attribute_ids.remove("position");
-
- /* Copy dynamic control point attributes. */
- for (const AttributeIDRef &attribute_id : source_attribute_ids) {
- const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(attribute_id,
- ATTR_DOMAIN_POINT);
- /* Some attributes might not exist if they were builtin attribute on domains that don't
- * have any elements, i.e. a face attribute on the output of the line primitive node. */
- if (!mesh_attribute) {
- continue;
- }
-
- const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type());
-
- threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
- for (const int i : range) {
- /* Create attribute on the spline points. */
- splines[i]->attributes.create(attribute_id, data_type);
- std::optional<GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write(
- attribute_id);
- BLI_assert(spline_attribute);
-
- /* Copy attribute based on the map for this spline. */
- attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) {
- using T = decltype(dummy);
- copy_attribute_to_points<T>(
- mesh_attribute->typed<T>(), point_to_vert_maps[i], spline_attribute->typed<T>());
- });
- }
- });
- }
-
- curve.assert_valid_point_attributes();
-}
-
-struct CurveFromEdgesOutput {
- std::unique_ptr<CurveEval> curve;
- Vector<Vector<int>> point_to_vert_maps;
-};
-
-static CurveFromEdgesOutput mesh_to_curve(Span<MVert> verts, Span<std::pair<int, int>> edges)
+static void geo_node_legacy_mesh_to_curve_exec(GeoNodeExecParams params)
{
- std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
- Vector<Vector<int>> point_to_vert_maps;
-
- /* Compute the number of edges connecting to each vertex. */
- Array<int> neighbor_count(verts.size(), 0);
- for (const std::pair<int, int> &edge : edges) {
- neighbor_count[edge.first]++;
- neighbor_count[edge.second]++;
- }
-
- /* Compute an offset into the array of neighbor edges based on the counts. */
- Array<int> neighbor_offsets(verts.size());
- int start = 0;
- for (const int i : verts.index_range()) {
- neighbor_offsets[i] = start;
- start += neighbor_count[i];
- }
-
- /* Use as an index into the "neighbor group" for each vertex. */
- Array<int> used_slots(verts.size(), 0);
- /* Calculate the indices of each vertex's neighboring edges. */
- Array<int> neighbors(edges.size() * 2);
- for (const int i : edges.index_range()) {
- const int v1 = edges[i].first;
- const int v2 = edges[i].second;
- neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2;
- neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1;
- used_slots[v1]++;
- used_slots[v2]++;
- }
-
- /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */
- Array<int> unused_edges = std::move(used_slots);
-
- for (const int start_vert : verts.index_range()) {
- /* The vertex will be part of a cyclic spline. */
- if (neighbor_count[start_vert] == 2) {
- continue;
- }
-
- /* The vertex has no connected edges, or they were already used. */
- if (unused_edges[start_vert] == 0) {
- continue;
- }
-
- for (const int i : IndexRange(neighbor_count[start_vert])) {
- int current_vert = start_vert;
- int next_vert = neighbors[neighbor_offsets[current_vert] + i];
-
- if (unused_edges[next_vert] == 0) {
- continue;
- }
-
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
- Vector<int> point_to_vert_map;
-
- spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
- point_to_vert_map.append(current_vert);
-
- /* Follow connected edges until we read a vertex with more than two connected edges. */
- while (true) {
- int last_vert = current_vert;
- current_vert = next_vert;
-
- spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
- point_to_vert_map.append(current_vert);
- unused_edges[current_vert]--;
- unused_edges[last_vert]--;
-
- if (neighbor_count[current_vert] != 2) {
- break;
- }
-
- const int offset = neighbor_offsets[current_vert];
- const int next_a = neighbors[offset];
- const int next_b = neighbors[offset + 1];
- next_vert = (last_vert == next_a) ? next_b : next_a;
- }
-
- spline->attributes.reallocate(spline->size());
- curve->add_spline(std::move(spline));
- point_to_vert_maps.append(std::move(point_to_vert_map));
- }
- }
-
- /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */
- for (const int start_vert : verts.index_range()) {
- if (unused_edges[start_vert] != 2) {
- continue;
- }
-
- int current_vert = start_vert;
- int next_vert = neighbors[neighbor_offsets[current_vert]];
-
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
- Vector<int> point_to_vert_map;
- spline->set_cyclic(true);
-
- spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
- point_to_vert_map.append(current_vert);
-
- /* Follow connected edges until we loop back to the start vertex. */
- while (next_vert != start_vert) {
- const int last_vert = current_vert;
- current_vert = next_vert;
-
- spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
- point_to_vert_map.append(current_vert);
- unused_edges[current_vert]--;
- unused_edges[last_vert]--;
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
- const int offset = neighbor_offsets[current_vert];
- const int next_a = neighbors[offset];
- const int next_b = neighbors[offset + 1];
- next_vert = (last_vert == next_a) ? next_b : next_a;
- }
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
- spline->attributes.reallocate(spline->size());
- curve->add_spline(std::move(spline));
- point_to_vert_maps.append(std::move(point_to_vert_map));
+ if (!geometry_set.has_mesh()) {
+ params.set_output("Curve", GeometrySet());
+ return;
}
- curve->attributes.reallocate(curve->splines().size());
- return {std::move(curve), std::move(point_to_vert_maps)};
-}
-
-/**
- * Get a separate array of the indices for edges in a selection (a boolean attribute).
- * This helps to make the above algorithm simpler by removing the need to check for selection
- * in many places.
- */
-static Vector<std::pair<int, int>> get_selected_edges(GeoNodeExecParams params,
- const MeshComponent &component)
-{
- const Mesh &mesh = *component.get_for_read();
+ const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
const std::string selection_name = params.extract_input<std::string>("Selection");
if (!selection_name.empty() && !component.attribute_exists(selection_name)) {
params.error_message_add(NodeWarningType::Error,
@@ -264,51 +47,33 @@ static Vector<std::pair<int, int>> get_selected_edges(GeoNodeExecParams params,
GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>(
selection_name, ATTR_DOMAIN_EDGE, true);
- Vector<std::pair<int, int>> selected_edges;
- for (const int i : IndexRange(mesh.totedge)) {
+ Vector<int64_t> selected_edge_indices;
+ for (const int64_t i : IndexRange(component.attribute_domain_size(ATTR_DOMAIN_EDGE))) {
if (selection[i]) {
- selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2});
+ selected_edge_indices.append(i);
}
}
- return selected_edges;
-}
-
-static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params)
-{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
-
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
-
- if (!geometry_set.has_mesh()) {
- params.set_output("Curve", GeometrySet());
- return;
- }
-
- const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
- const Mesh &mesh = *component.get_for_read();
- Span<MVert> verts = Span{mesh.mvert, mesh.totvert};
- Vector<std::pair<int, int>> selected_edges = get_selected_edges(params, component);
- if (selected_edges.size() == 0) {
+ if (selected_edge_indices.size() == 0) {
params.set_output("Curve", GeometrySet());
return;
}
- CurveFromEdgesOutput output = mesh_to_curve(verts, selected_edges);
- copy_attributes_to_points(*output.curve, component, output.point_to_vert_maps);
+ std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert(
+ component, IndexMask(selected_edge_indices));
- params.set_output("Curve", GeometrySet::create_with_curve(output.curve.release()));
+ params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
}
} // namespace blender::nodes
-void register_node_type_geo_mesh_to_curve()
+void register_node_type_geo_legacy_mesh_to_curve()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_LEGACY_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_mesh_to_curve_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec;
+ ntype.declare = blender::nodes::geo_node_legacy_mesh_to_curve_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_legacy_mesh_to_curve_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
new file mode 100644
index 00000000000..7bca9ec141b
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
@@ -0,0 +1,69 @@
+/*
+ * 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 "GEO_mesh_to_curve.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_legacy_mesh_to_curve_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Mesh");
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
+ b.add_output<decl::Geometry>("Curve");
+}
+
+static void geo_node_legacy_mesh_to_curve_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_mesh()) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
+ GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE};
+ fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_EDGE)};
+ evaluator.add(params.get_input<Field<bool>>("Selection"));
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_as_mask(0);
+ if (selection.size() == 0) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert(component, selection);
+ geometry_set.replace_curve(curve.release());
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES});
+ });
+
+ params.set_output("Curve", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_mesh_to_curve()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_legacy_mesh_to_curve_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_legacy_mesh_to_curve_exec;
+ nodeRegisterType(&ntype);
+}