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:
-rw-r--r--release/scripts/startup/nodeitems_builtins.py1
-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
12 files changed, 457 insertions, 254 deletions
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index b2faff56656..d4885f7bad8 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -145,6 +145,7 @@ def mesh_node_items(context):
yield NodeItem("GeometryNodeEdgeSplit")
yield NodeItem("GeometryNodeBoolean")
+ yield NodeItem("GeometryNodeMeshToCurve")
yield NodeItem("GeometryNodeMeshToPoints")
yield NodeItem("GeometryNodeMeshSubdivide")
yield NodeItem("GeometryNodeTriangulate")
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);
+}