This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/geometry/intern/mesh_to_curve_convert.cc
Hans Goudey 1dc57a89e9 Mesh: Move functions to C++ header
Refactoring mesh code, it has become clear that local cleanups and
simplifications are limited by the need to keep a C public API for
mesh functions. This change makes code more obvious and makes further
refactoring much easier.

- Add a new `BKE_mesh.hh` header for a C++ only mesh API
- Introduce a new `blender::bke::mesh` namespace, documented here:
  https://wiki.blender.org/wiki/Source/Objects/Mesh#Namespaces
- Move some functions to the new namespace, cleaning up their arguments
- Move code to `Array` and `float3` where necessary to use the new API
- Define existing inline mesh data access functions to the new header
- Keep some C API functions where necessary because of RNA
- Move all C++ files to use the new header, which includes the old one

In the future it may make sense to split up `BKE_mesh.hh` more, but for
now keeping the same name as the existing header keeps things simple.

Pull Request: blender/blender#105416
2023-03-12 22:29:15 +01:00

230 lines
7.6 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_array.hh"
#include "BLI_array_utils.hh"
#include "BLI_set.hh"
#include "BLI_task.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_attribute.hh"
#include "BKE_attribute_math.hh"
#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_mesh.hh"
#include "GEO_mesh_to_curve.hh"
namespace blender::geometry {
bke::CurvesGeometry create_curve_from_vert_indices(
const Mesh &mesh,
const Span<int> vert_indices,
const Span<int> curve_offsets,
const IndexRange cyclic_curves,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
bke::CurvesGeometry curves(vert_indices.size(), curve_offsets.size());
curves.offsets_for_write().drop_back(1).copy_from(curve_offsets);
curves.offsets_for_write().last() = vert_indices.size();
curves.fill_curve_types(CURVE_TYPE_POLY);
const bke::AttributeAccessor mesh_attributes = mesh.attributes();
bke::MutableAttributeAccessor curves_attributes = curves.attributes_for_write();
if (!cyclic_curves.is_empty()) {
bke::SpanAttributeWriter cyclic = curves_attributes.lookup_or_add_for_write_span<bool>(
"cyclic", ATTR_DOMAIN_CURVE);
cyclic.span.slice(cyclic_curves).fill(true);
cyclic.finish();
}
Set<bke::AttributeIDRef> source_attribute_ids = mesh_attributes.all_ids();
for (const bke::AttributeIDRef &attribute_id : source_attribute_ids) {
if (mesh_attributes.is_builtin(attribute_id) && !curves_attributes.is_builtin(attribute_id)) {
/* Don't copy attributes that are built-in on meshes but not on curves. */
continue;
}
if (attribute_id.is_anonymous() && !propagation_info.propagate(attribute_id.anonymous_id())) {
continue;
}
const GVArray mesh_attribute = mesh_attributes.lookup(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;
}
/* Copy attribute based on the map for this curve. */
attribute_math::convert_to_static_type(mesh_attribute.type(), [&](auto dummy) {
using T = decltype(dummy);
bke::SpanAttributeWriter<T> attribute =
curves_attributes.lookup_or_add_for_write_only_span<T>(attribute_id, ATTR_DOMAIN_POINT);
array_utils::gather<T>(mesh_attribute.typed<T>(), vert_indices, attribute.span);
attribute.finish();
});
}
return curves;
}
struct CurveFromEdgesOutput {
/** The indices in the mesh for each control point of each result curves. */
Vector<int> vert_indices;
/** The first index of each curve in the result. */
Vector<int> curve_offsets;
/** A subset of curves that should be set cyclic. */
IndexRange cyclic_curves;
};
static CurveFromEdgesOutput edges_to_curve_point_indices(const int verts_num,
Span<std::pair<int, int>> edges)
{
Vector<int> vert_indices;
vert_indices.reserve(edges.size());
Vector<int> curve_offsets;
/* Compute the number of edges connecting to each vertex. */
Array<int> neighbor_count(verts_num, 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_num);
int start = 0;
for (const int i : IndexRange(verts_num)) {
neighbor_offsets[i] = start;
start += neighbor_count[i];
}
/* Use as an index into the "neighbor group" for each vertex. */
Array<int> used_slots(verts_num, 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 : IndexRange(verts_num)) {
/* The vertex will be part of a cyclic curve. */
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;
}
/* Start a new curve in the output. */
curve_offsets.append(vert_indices.size());
vert_indices.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;
vert_indices.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;
}
}
}
/* All curves added after this are cyclic. */
const int cyclic_start = curve_offsets.size();
/* All remaining edges are part of cyclic curves (we skipped vertices with two edges before). */
for (const int start_vert : IndexRange(verts_num)) {
if (unused_edges[start_vert] != 2) {
continue;
}
int current_vert = start_vert;
int next_vert = neighbors[neighbor_offsets[current_vert]];
curve_offsets.append(vert_indices.size());
vert_indices.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;
vert_indices.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;
}
}
const IndexRange cyclic_curves = curve_offsets.index_range().drop_front(cyclic_start);
return {std::move(vert_indices), std::move(curve_offsets), cyclic_curves};
}
/**
* 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;
const Span<MEdge> edges = mesh.edges();
for (const int i : selection) {
selected_edges.append({edges[i].v1, edges[i].v2});
}
return selected_edges;
}
bke::CurvesGeometry mesh_to_curve_convert(
const Mesh &mesh,
const IndexMask selection,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
Vector<std::pair<int, int>> selected_edges = get_selected_edges(mesh, selection);
CurveFromEdgesOutput output = edges_to_curve_point_indices(mesh.totvert, selected_edges);
return create_curve_from_vert_indices(
mesh, output.vert_indices, output.curve_offsets, output.cyclic_curves, propagation_info);
}
} // namespace blender::geometry