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
This commit is contained in:
Hans Goudey 2021-10-14 12:06:48 -05:00
parent 1996efe7aa
commit 17b8da7196
12 changed files with 463 additions and 260 deletions

View File

@ -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")

View File

@ -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)

View File

@ -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
/** \} */

View File

@ -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();

View File

@ -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}")

View File

@ -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

View File

@ -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

View File

@ -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
)

View File

@ -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);

View File

@ -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", "")

View File

@ -14,267 +14,20 @@
* 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)
{
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(GeoNodeExecParams params,
const MeshComponent &component)
{
const Mesh &mesh = *component.get_for_read();
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,
TIP_("No attribute with name \"") + selection_name + "\"");
}
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)) {
if (selection[i]) {
selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2});
}
}
return selected_edges;
}
static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params)
static void geo_node_legacy_mesh_to_curve_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
@ -286,29 +39,41 @@ static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params)
}
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) {
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,
TIP_("No attribute with name \"") + selection_name + "\"");
}
GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>(
selection_name, ATTR_DOMAIN_EDGE, true);
Vector<int64_t> selected_edge_indices;
for (const int64_t i : IndexRange(component.attribute_domain_size(ATTR_DOMAIN_EDGE))) {
if (selection[i]) {
selected_edge_indices.append(i);
}
}
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);
}

View File

@ -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);
}