Merge branch 'blender-v3.6-release'

This commit is contained in:
Hans Goudey
2023-06-13 08:22:50 -04:00
4 changed files with 113 additions and 157 deletions

View File

@@ -11,6 +11,7 @@
#include "DNA_object_types.h"
#include "BLI_map.hh"
#include "BLI_ordered_edge.hh"
#include "BLI_task.hh"
#include "BLI_threads.h"
#include "BLI_timeit.hh"
@@ -21,43 +22,14 @@
namespace blender::bke::calc_edges {
/** This is used to uniquely identify edges in a hash map. */
struct OrderedEdge {
int v_low, v_high;
OrderedEdge(const int v1, const int v2)
{
if (v1 < v2) {
v_low = v1;
v_high = v2;
}
else {
v_low = v2;
v_high = v1;
}
}
OrderedEdge(const uint v1, const uint v2) : OrderedEdge(int(v1), int(v2)) {}
uint64_t hash() const
{
return (this->v_low << 8) ^ this->v_high;
}
/** Return a hash value that is likely to be different in the low bits from the normal `hash()`
* function. This is necessary to avoid collisions in #BKE_mesh_calc_edges. */
uint64_t hash2() const
{
return this->v_low;
}
friend bool operator==(const OrderedEdge &e1, const OrderedEdge &e2)
{
BLI_assert(e1.v_low < e1.v_high);
BLI_assert(e2.v_low < e2.v_high);
return e1.v_low == e2.v_low && e1.v_high == e2.v_high;
}
};
/**
* Return a hash value that is likely to be different in the low bits from the normal `hash()`
* function. This is necessary to avoid collisions in #BKE_mesh_calc_edges.
*/
static uint64_t edge_hash_2(const OrderedEdge &edge)
{
return edge.v_low;
}
/* The map first contains an edge pointer and later an index. */
union OrigEdgeOrIndex {
@@ -86,7 +58,7 @@ static void add_existing_edges_to_hash_maps(Mesh *mesh,
for (const int2 &edge : edges) {
OrderedEdge ordered_edge{edge[0], edge[1]};
/* Only add the edge when it belongs into this map. */
if (task_index == (parallel_mask & ordered_edge.hash2())) {
if (task_index == (parallel_mask & edge_hash_2(ordered_edge))) {
edge_map.add_new(ordered_edge, {&edge});
}
}
@@ -109,7 +81,7 @@ static void add_polygon_edges_to_hash_maps(Mesh *mesh,
if (vert_prev != vert) {
OrderedEdge ordered_edge{vert_prev, vert};
/* Only add the edge when it belongs into this map. */
if (task_index == (parallel_mask & ordered_edge.hash2())) {
if (task_index == (parallel_mask & edge_hash_2(ordered_edge))) {
edge_map.lookup_or_add(ordered_edge, {nullptr});
}
}
@@ -171,7 +143,7 @@ static void update_edge_indices_in_poly_loops(const OffsetIndices<int> polys,
if (vert_prev != vert) {
OrderedEdge ordered_edge{vert_prev, vert};
/* Double lookup: First find the map that contains the edge, then lookup the edge. */
const EdgeMap &edge_map = edge_maps[parallel_mask & ordered_edge.hash2()];
const EdgeMap &edge_map = edge_maps[parallel_mask & edge_hash_2(ordered_edge)];
edge_index = edge_map.lookup(ordered_edge).index;
}
else {

View File

@@ -0,0 +1,47 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_assert.h"
#include "BLI_math_vector_types.hh"
namespace blender {
/**
* A version of `int2` used as a key for hash-maps, agnostic of the arbitrary order of the two
* vertices in a mesh edge.
*/
struct OrderedEdge {
int v_low;
int v_high;
OrderedEdge(const int v1, const int v2)
{
if (v1 < v2) {
v_low = v1;
v_high = v2;
}
else {
v_low = v2;
v_high = v1;
}
}
OrderedEdge(const int2 edge) : OrderedEdge(edge[0], edge[1]) {}
OrderedEdge(const uint v1, const uint v2) : OrderedEdge(int(v1), int(v2)) {}
uint64_t hash() const
{
return (this->v_low << 8) ^ this->v_high;
}
friend bool operator==(const OrderedEdge &e1, const OrderedEdge &e2)
{
BLI_assert(e1.v_low < e1.v_high);
BLI_assert(e2.v_low < e2.v_high);
return e1.v_low == e2.v_low && e1.v_high == e2.v_high;
}
};
} // namespace blender

View File

@@ -320,6 +320,7 @@ set(SRC
BLI_noise.hh
BLI_offset_indices.hh
BLI_offset_span.hh
BLI_ordered_edge.hh
BLI_parameter_pack_utils.hh
BLI_path_util.h
BLI_polyfill_2d.h

View File

@@ -4,6 +4,8 @@
#include "BLI_array_utils.hh"
#include "BLI_index_mask.hh"
#include "BLI_ordered_edge.hh"
#include "BLI_vector_set.hh"
#include "BKE_attribute.hh"
#include "BKE_attribute_math.hh"
@@ -14,12 +16,6 @@
namespace blender::geometry {
/* Naively checks if the first vertices and the second vertices are the same. */
static inline bool naive_edges_equal(const int2 &edge1, const int2 &edge2)
{
return edge1 == edge2;
}
static void add_new_vertices(Mesh &mesh, const Span<int> new_to_old_verts_map)
{
/* These types aren't supported for interpolation below. */
@@ -154,70 +150,6 @@ static void add_new_edges(Mesh &mesh,
}
}
/**
* Merge the new_edge into the original edge.
*
* NOTE: This function is very specific to the situation and makes a lot of assumptions.
*/
static void merge_edges(const int orig_edge_i,
const int new_edge_i,
MutableSpan<int> new_corner_edges,
Vector<Vector<int>> &edge_to_loop_map,
Vector<int2> &new_edges,
Vector<int> &new_to_old_edges_map)
{
/* Merge back into the original edge by undoing the topology changes. */
BLI_assert(edge_to_loop_map[new_edge_i].size() == 1);
const int loop_i = edge_to_loop_map[new_edge_i][0];
new_corner_edges[loop_i] = orig_edge_i;
/* We are putting the last edge in the location of new_edge in all the maps, to remove
* new_edge efficiently. We have to update the topology information for this last edge
* though. Essentially we are replacing every instance of last_edge_i with new_edge_i. */
const int last_edge_i = new_edges.size() - 1;
if (last_edge_i != new_edge_i) {
BLI_assert(edge_to_loop_map[last_edge_i].size() == 1);
const int last_edge_loop_i = edge_to_loop_map[last_edge_i][0];
new_corner_edges[last_edge_loop_i] = new_edge_i;
}
/* We can now safely swap-remove. */
new_edges.remove_and_reorder(new_edge_i);
edge_to_loop_map.remove_and_reorder(new_edge_i);
new_to_old_edges_map.remove_and_reorder(new_edge_i);
}
/**
* Replace the vertex of an edge with a new one, and update the connected loops.
*
* NOTE: This only updates the loops containing the edge and the old vertex. It should therefore
* also be called on the adjacent edge.
*/
static void swap_vertex_of_edge(int2 &edge,
const int old_vert,
const int new_vert,
MutableSpan<int> corner_verts,
const Span<int> connected_loops)
{
if (edge[0] == old_vert) {
edge[0] = new_vert;
}
else if (edge[1] == old_vert) {
edge[1] = new_vert;
}
else {
BLI_assert_unreachable();
}
for (const int loop_i : connected_loops) {
if (corner_verts[loop_i] == old_vert) {
corner_verts[loop_i] = new_vert;
}
/* The old vertex is on the loop containing the adjacent edge. Since this function is also
* called on the adjacent edge, we don't replace it here. */
}
}
/** Split the vertex into duplicates so that each fan has a different vertex. */
static void split_vertex_per_fan(const int vertex,
const int start_offset,
@@ -225,7 +157,6 @@ static void split_vertex_per_fan(const int vertex,
const Span<int> fans,
const Span<int> fan_sizes,
const Span<Vector<int>> edge_to_loop_map,
MutableSpan<int2> new_edges,
MutableSpan<int> corner_verts,
MutableSpan<int> new_to_old_verts_map)
{
@@ -237,8 +168,13 @@ static void split_vertex_per_fan(const int vertex,
new_to_old_verts_map[new_vert_i - orig_verts_num] = vertex;
for (const int edge_i : fans.slice(fan_start, fan_sizes[i])) {
swap_vertex_of_edge(
new_edges[edge_i], vertex, new_vert_i, corner_verts, edge_to_loop_map[edge_i]);
for (const int loop_i : edge_to_loop_map[edge_i]) {
if (corner_verts[loop_i] == vertex) {
corner_verts[loop_i] = new_vert_i;
}
/* The old vertex is on the loop containing the adjacent edge. Since this function is also
* called on the adjacent edge, we don't replace it here. */
}
}
fan_start += fan_sizes[i];
}
@@ -267,7 +203,7 @@ static int adjacent_edge(const Span<int> corner_verts,
* be used to retrieve the fans from connected_edges.
*/
static void calc_vertex_fans(const int vertex,
const Span<int> new_corner_verts,
const Span<int> corner_verts,
const Span<int> new_corner_edges,
const OffsetIndices<int> polys,
const Span<Vector<int>> edge_to_loop_map,
@@ -298,7 +234,7 @@ static void calc_vertex_fans(const int vertex,
/* Add adjacent edges to search stack. */
for (const int loop_i : edge_to_loop_map[curr_edge_i]) {
const int adjacent_edge_i = adjacent_edge(
new_corner_verts, new_corner_edges, loop_i, polys[loop_to_poly_map[loop_i]], vertex);
corner_verts, new_corner_edges, loop_i, polys[loop_to_poly_map[loop_i]], vertex);
/* Find out if this edge was visited already. */
int i = curr_i + 1;
@@ -340,18 +276,13 @@ static void calc_vertex_fans(const int vertex,
static void split_edge_per_poly(const int edge_i,
const int new_edge_start,
MutableSpan<Vector<int>> edge_to_loop_map,
MutableSpan<int> corner_edges,
MutableSpan<int2> new_edges,
MutableSpan<int> new_to_old_edges_map)
MutableSpan<int> corner_edges)
{
if (edge_to_loop_map[edge_i].size() <= 1) {
return;
}
int new_edge_index = new_edge_start;
for (const int loop_i : edge_to_loop_map[edge_i].as_span().drop_front(1)) {
const int2 &new_edge(new_edges[edge_i]);
new_edges[new_edge_index] = new_edge;
new_to_old_edges_map[new_edge_index] = edge_i;
edge_to_loop_map[new_edge_index].append({loop_i});
corner_edges[loop_i] = new_edge_index;
new_edge_index++;
@@ -401,11 +332,11 @@ void split_edges(Mesh &mesh,
});
const OffsetIndices polys = mesh.polys();
const Array<int> orig_corner_edges = mesh.corner_edges();
IndexMaskMemory memory;
const IndexMask loose_edges = IndexMask::from_bits(mesh.loose_edges().is_loose_bits, memory);
MutableSpan<int> corner_verts = mesh.corner_verts_for_write();
MutableSpan<int> corner_edges = mesh.corner_edges_for_write();
Vector<int2> new_edges(new_edges_size);
new_edges.as_mutable_span().take_front(edges.size()).copy_from(edges);
Vector<Vector<int>> edge_to_loop_map(new_edges_size);
threading::parallel_for(edges.index_range(), 512, [&](const IndexRange range) {
@@ -414,19 +345,18 @@ void split_edges(Mesh &mesh,
}
});
/* Used for transferring attributes. */
Vector<int> new_to_old_edges_map(new_edges.size());
std::iota(new_to_old_edges_map.begin(), new_to_old_edges_map.end(), 0);
/* Step 1: Split the edges. */
threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) {
for (const int mask_i : range) {
const int edge_i = mask[mask_i];
split_edge_per_poly(edge_i, edge_offsets[edge_i], edge_to_loop_map, corner_edges);
}
});
/* Step 1: Split the edges. */
mask.foreach_index(GrainSize(512), [&](const int edge_i) {
split_edge_per_poly(edge_i,
edge_offsets[edge_i],
edge_to_loop_map,
corner_edges,
new_edges,
new_to_old_edges_map);
mask.foreach_index([&](const int edge_i) {
split_edge_per_poly(edge_i, edge_offsets[edge_i], edge_to_loop_map, corner_edges);
});
/* Step 1.5: Update topology information (can't parallelize). */
@@ -438,6 +368,8 @@ void split_edges(Mesh &mesh,
}
});
MutableSpan<int> corner_verts = mesh.corner_verts_for_write();
/* Step 2: Calculate vertex fans. */
Array<Vector<int>> vertex_fan_sizes(mesh.totvert);
threading::parallel_for(IndexRange(mesh.totvert), 512, [&](IndexRange range) {
@@ -483,39 +415,43 @@ void split_edges(Mesh &mesh,
vert_to_edge_map[vert],
vertex_fan_sizes[vert],
edge_to_loop_map,
new_edges,
corner_verts,
new_to_old_verts_map);
}
});
/* Step 4: Deduplicate edges. We loop backwards so we can use remove_and_reorder. Although this
* does look bad (3 nested loops), in practice the inner loops are very small. For most meshes,
* there are at most 2 polygons connected to each edge, and hence you'll only get at most 1
* duplicate per edge. */
for (int mask_i = mask.size() - 1; mask_i >= 0; mask_i--) {
const int edge = mask[mask_i];
int start_of_duplicates = edge_offsets[edge];
int end_of_duplicates = start_of_duplicates + num_edge_duplicates[edge] - 1;
for (int duplicate = end_of_duplicates; duplicate >= start_of_duplicates; duplicate--) {
if (naive_edges_equal(new_edges[edge], new_edges[duplicate])) {
merge_edges(
edge, duplicate, corner_edges, edge_to_loop_map, new_edges, new_to_old_edges_map);
break;
}
for (int other = start_of_duplicates; other < duplicate; other++) {
if (naive_edges_equal(new_edges[other], new_edges[duplicate])) {
merge_edges(
other, duplicate, corner_edges, edge_to_loop_map, new_edges, new_to_old_edges_map);
break;
}
}
VectorSet<OrderedEdge> new_edges;
new_edges.reserve(new_edges_size + loose_edges.size());
for (const int i : polys.index_range()) {
const IndexRange poly = polys[i];
for (const int corner : poly) {
const int vert_1 = corner_verts[corner];
const int vert_2 = corner_verts[bke::mesh::poly_corner_next(poly, corner)];
corner_edges[corner] = new_edges.index_of_or_add_as(OrderedEdge(vert_1, vert_2));
}
}
loose_edges.foreach_index([&](const int64_t i) { new_edges.add(OrderedEdge(edges[i])); });
Array<int> new_to_old_edges_map(new_edges.size());
auto index_mask_to_indices = [&](const IndexMask &mask, MutableSpan<int> indices) {
for (const int i : mask.index_range()) {
indices[i] = mask[i];
}
};
index_mask_to_indices(loose_edges,
new_to_old_edges_map.as_mutable_span().take_back(loose_edges.size()));
for (const int i : polys.index_range()) {
const IndexRange poly = polys[i];
for (const int corner : poly) {
const int new_edge_i = corner_edges[corner];
const int old_edge_i = orig_corner_edges[corner];
new_to_old_edges_map[new_edge_i] = old_edge_i;
}
}
/* Step 5: Resize the mesh to add the new vertices and rebuild the edges. */
add_new_vertices(mesh, new_to_old_verts_map);
add_new_edges(mesh, new_edges, new_to_old_edges_map, propagation_info);
add_new_edges(mesh, new_edges.as_span().cast<int2>(), new_to_old_edges_map, propagation_info);
BKE_mesh_tag_edges_split(&mesh);
}