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:
authorHoward Trickey <howard.trickey@gmail.com>2022-10-24 20:25:22 +0300
committerHoward Trickey <howard.trickey@gmail.com>2022-10-24 20:25:22 +0300
commitfc8f9e420426570dcb3e026ecbe8145cd0fae5ca (patch)
treee88846153e73c2cbef4638dfaa4662d9d999da78
parent6df669a0bd573eb46b10b324bf21a5fbf6ac3760 (diff)
Refactored vertex data structures.
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_bevel_mesh.cc1520
1 files changed, 783 insertions, 737 deletions
diff --git a/source/blender/nodes/geometry/nodes/node_geo_bevel_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_bevel_mesh.cc
index ab6b24515e6..9f0a54ea9c3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_bevel_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_bevel_mesh.cc
@@ -9,6 +9,8 @@
#include "BKE_mesh_runtime.h"
#include "BLI_array.hh"
+#include "BLI_hash.hh"
+#include "BLI_map.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_math_vector.hh"
#include "BLI_mesh_inset.hh"
@@ -245,594 +247,37 @@ float3 MeshTopology::edge_dir_from_vert_normalized(int e, int v) const
return math::normalize(edge_dir_from_vert(e, v));
}
-/** A Vertex Cap consists of a vertex in a mesh and an CCW ordering of
- * alternating edges and faces around it, as viewed from the face's
- * normal side. Some faces may be missing (i.e., gaps).
- * (If there are other edges and faces attached to the vertex that
- * don't fit into this pattern, they need to go into other Vertex Caps
- * or ignored, for the sake of beveling.)
+/** Canon
+ * CanonVertPair is a pair of vertex indices in canonical order (first index <= second index).
+ * This is suitable for a key to look up edges by.
*/
-class VertexCap {
- Array<int> edges_;
- Array<int> faces_; // face_[i] is between edges i and i+1
-
+class CanonVertPair {
public:
- /* The vertex (as index into a mesh) that the cap is around. */
- int vert;
-
- VertexCap() : vert(-1)
- {
- }
- VertexCap(int vert, Span<int> edges, Span<int> faces) : edges_(edges), faces_(faces), vert(vert)
- {
- }
-
- /* Initialize for vertex v, given a mesh topo. */
- void init_from_topo(const int vert, const MeshTopology &topo);
-
- /* The number of edges around the cap. */
- int size() const
- {
- return edges_.size();
- }
-
- /* Edges in CCW order (viewed from top) around the cap. */
- Span<int> edges() const
- {
- return edges_.as_span();
- }
+ int v1;
+ int v2;
- /* Faces in CCW order (viewed from top) around the cap. -1 means a gap. */
- Span<int> faces() const
- {
- return faces_.as_span();
- }
-
- /* The ith edge. */
- int edge(int i) const
- {
- return edges_[i];
- }
- /* The edge after the ith edge (with wraparound). */
- int next_edge(int i) const
- {
- return i < edges_.size() - 1 ? edges_[i + 1] : edges_[0];
- }
- /* The edge before the ith edge (with wraparound). */
- int prev_edge(int i) const
+ CanonVertPair(int a, int b)
{
- return i > 1 ? edges_[i - 1] : edges_.last();
- }
-
- /* The face returned may be -1, meaning "gap". */
- /* The face betwen edge(i) and next_edge(i). */
- int face(int i) const
- {
- return faces_[i];
- }
- /* The face between edge(i) and prev_edge(i). */
- int prev_face(int i) const
- {
- return i > 1 ? faces_[i - 1] : faces_.last();
- }
- /* True if there is a gap between edges i and next_edge(i). */
- bool is_gap(int i) const
- {
- return face(i) == -1;
- }
-};
-
-/** Construct and return the VertexCap for vertex `vert`. */
-void VertexCap::init_from_topo(const int vert, const MeshTopology &topo)
-{
- this->vert = vert;
- Span<int> incident_edges = topo.vert_edges(vert);
- const int num_edges = incident_edges.size();
- if (num_edges == 0) {
- return;
- }
-
- /* First check for the most common case: a complete manifold cap:
- * That is, each edge is incident on exactly two faces and the
- * edge--face--edge--...--face chain forms a single cycle.
- */
- bool all_edges_manifold = true;
- for (const int e : incident_edges) {
- if (!topo.edge_is_manifold(e)) {
- all_edges_manifold = false;
- break;
- }
- }
- if (all_edges_manifold) {
- bool is_manifold_cap = true;
- Array<int> ordered_edges(num_edges, -1);
- Array<int> ordered_faces(num_edges, -1);
- Set<int, 16> used_edges;
- Set<int, 16> used_faces;
-
- int next_edge = incident_edges[0];
- for (int slot = 0; slot < num_edges; slot++) {
- /* Invariant: ordered_edges and ordered_faces are filled
- * up to slot-1 with a valid sequence for the cap, and
- * next_edge is a valid continuation edge but we don't
- * yet know if it has already been used.
- */
- ordered_edges[slot] = next_edge;
- used_edges.add_new(next_edge);
- /* Find a face attached to next_edge that is not yet used. */
- int next_face;
- if (slot == 0) {
- next_face = topo.edge_faces(next_edge)[0];
- }
- else {
- const int prev_face = ordered_faces[slot - 1];
- next_face = topo.edge_other_manifold_face(next_edge, prev_face);
- }
- if (used_faces.contains(next_face)) {
- is_manifold_cap = false;
- break;
- }
- ordered_faces[slot] = next_face;
- next_edge = topo.face_other_edge_at_vert(next_face, vert, next_edge);
- if (slot < num_edges - 1 && used_edges.contains(next_edge)) {
- is_manifold_cap = false;
- break;
- }
- }
- is_manifold_cap = is_manifold_cap && next_edge == ordered_edges[0];
- if (is_manifold_cap) {
- /* Check if cap is oriented properly, and fix it if not.
- * A pair of successive edges in ordered_edges should be going CW
- * in the face in between. For now, just check the first pair.
- */
- if (num_edges > 1) {
- if (topo.edge_is_successor_in_face(ordered_edges[0], ordered_edges[1], ordered_faces[0])) {
- /* They are in the wrong orientation, so we need to reverse.
- * To make interleaving of edges and faces work out, reverse only 1..end of edges
- * and reverse all of faces.
- */
- std::reverse(ordered_edges.begin() + 1, ordered_edges.end());
- std::reverse(ordered_faces.begin(), ordered_faces.end());
- }
- }
- this->edges_ = ordered_edges;
- this->faces_ = ordered_faces;
- return;
- }
- }
- std::cout << "to implement: VertexCap for non-manifold edges\n";
-}
-
-static std::ostream &operator<<(std::ostream &os, const VertexCap &cap)
-{
- os << "cap at v" << cap.vert << ": ";
- for (const int i : cap.edges().index_range()) {
- os << "e" << cap.edge(i) << " ";
- if (cap.face(i) == -1) {
- os << "<gap> ";
+ if (a < b) {
+ v1 = a;
+ v2 = b;
}
else {
- os << "f" << cap.face(i) << " ";
+ v1 = b;
+ v2 = a;
}
}
- os << "\n";
- return os;
-}
-
-/* The different types of BoundaryVerts (see below). */
-typedef enum eBoundaryVertType {
- BV_ON_EDGE = 0,
- BV_ON_FACE = 1,
- BV_ABOVE_FACE = 2,
- BV_OTHER = 3,
-} eBoundaryVertType;
-
-static const char *bv_type_name[4] = {"on_edge", "on_face", "above_face", "other"};
-
-/* A BoundaryVert is a vertex placed somewhere around a vertex involved
- * in a bevel. BoundaryVerts will be joined with line or arcs (depending on the
- * number of segments in the bevel).
- */
-class BoundaryVert {
- public:
- /* The position of the Boundary Vertex. */
- float3 co;
- /* If the type references an edge or a face.
- * the index of the corresponding edge or face in the VertexCap. */
- int vc_index;
- /* Mesh index of this vertex in the output mesh. */
- int mesh_index;
- /* The type of this Boundary Vertex. */
- eBoundaryVertType type;
-
- BoundaryVert() : co(0.0, 0.0, 0.0), vc_index(-1), mesh_index(-1), type(BV_OTHER)
- {
- }
-};
-
-static std::ostream &operator<<(std::ostream &os, const BoundaryVert &bv)
-{
- os << "bv{" << bv_type_name[bv.type] << " "
- << "vc#=" << bv.vc_index << " "
- << "mesh#" << bv.mesh_index << " "
- << "co=" << bv.co << "}";
- return os;
-}
-
-/** The different types of HalfEdges (see below). */
-typedef enum eHalfEdgeType {
- HE_UNBEVELED = 0,
- HE_BEVELED = 1,
- HE_FACE_BEVEL_BOTH = 2,
- HE_FACE_BEVEL_LEFT = 3,
- HE_FACE_BEVEL_RIGHT = 4,
- BE_OTHER = 5,
-} eHalfEdgeType;
-
-static const char *he_type_name[6] = {
- "unbev", "bev", "facebev_both", "facebev_l", "facebev_r", "other"};
-
-/** A HalfEdge is one end of an edge, attached to a vertex in a VertexCap.
- * This data describes how it is involved in beveling, and how it is attached
- * to BoundaryVerts.
- * Note: when the descriptors "left" and "right" are used to refer to sides of
- * edges, these are to be taken as left and right when looking down the edge
- * towards the VertexCap's vertex.
- */
-class HalfEdge {
- public:
- /* The mesh index of the edge. */
- int edge;
- /* Where it is found in the list of edges in the VertexCap. */
- int vc_index;
- /* The boundary vertex index where the edge is attached,
- * only used for HE_UNBEVELED and HE_FACE_BEVEL_* types. */
- int bv_index;
- /* The boundary vertex index where the left half of a HE_BEVELED,
- * HE_FACE_BEVEL_BOTH, or HE_FACE_BEVEL_LEFT attached. */
- int bv_left_index;
- /* The boundary vertex index where the left half of a HE_BEVELED,
- * HE_FACE_BEVEL_BOTH, or HE_FACE_BEVEL_RIGHT attached. */
- int bv_right_index;
- /* The index of this edge, if unbeveled, in output mesh. */
- int mesh_index;
- /* The type of this HalfEdge. */
- eHalfEdgeType type;
-
- HalfEdge()
- : edge(-1),
- vc_index(-1),
- bv_index(-1),
- bv_left_index(-1),
- bv_right_index(-1),
- mesh_index(-1),
- type(BE_OTHER)
- {
- }
-};
-
-/** A BevelEdge holds the two ends (HalfEdges) of an edge that is to be beveled.
- * The underlying edge has a direction, and he1 is the HalfEdge at the source end,
- * while he2 is the HalfEdge at the destination end.
- */
-class BevelEdge {
- /* Index in mesh of the underlying edge. */
- int edge_;
-
- public:
- /* Source end HalfEdge. */
- HalfEdge *he1;
- /* Destination end HalfEdge. */
- HalfEdge *he2;
- /* Bevel amount for this edge. */
- float amount;
-
- BevelEdge() : edge_(-1), he1(nullptr), he2(nullptr), amount(0.0f)
- {
- }
- BevelEdge(int edge, HalfEdge *he1, HalfEdge *he2, float amount)
- : edge_(edge), he1(he1), he2(he2), amount(amount)
+ uint64_t hash() const
{
+ return get_default_hash_2(v1, v2);
}
- int edge() const
- {
- return edge_;
- }
+ friend bool operator==(const CanonVertPair a, const CanonVertPair b);
};
-/** A BoundaryConnector has the vertices and edges in the output mesh
- * of the connection between two successive BoundaryVerts.
- */
-class BoundaryConnector {
- public:
- /* Temporary: for now, just one edge. Will eventually be array
- * of vertices with intervening edges. */
- int edge;
-
- BoundaryConnector() : edge(-1)
- {
- }
-
- BoundaryConnector(int e) : edge(e)
- {
- }
-};
-
-static std::ostream &operator<<(std::ostream &os, const HalfEdge &he)
-{
- os << "he{" << he_type_name[he.type] << " "
- << "edge=" << he.edge << " "
- << "vc#=" << he.vc_index << " "
- << "bv#=" << he.bv_index << " "
- << "bvl#=" << he.bv_left_index << " "
- << "bvr#=" << he.bv_right_index << " "
- << "eout=" << he.mesh_index << "}";
- return os;
-}
-
-/** BevelVertexData holds the data used to bevel around a vertex. */
-class BevelVertexData {
- VertexCap vertex_cap_;
- Array<BoundaryVert> boundary_vert_;
- Array<HalfEdge> half_edge_;
- /* boundary_conn_[i] goes from boundary_vert_[i] to the following one. */
- Array<BoundaryConnector> boundary_conn_;
-
- public:
- BevelVertexData()
- {
- }
- ~BevelVertexData()
- {
- }
-
- void construct_vertex_cap(int vert, const MeshTopology &topo)
- {
- vertex_cap_.init_from_topo(vert, topo);
- }
-
- void construct_vertex_bevel(int vert, float amount, const MeshTopology &topo);
-
- const VertexCap &vertex_cap() const
- {
- return vertex_cap_;
- }
-
- const int beveled_vert() const
- {
- return vertex_cap_.vert;
- }
-
- Span<BoundaryVert> boundary_verts() const
- {
- return boundary_vert_.as_span();
- }
-
- MutableSpan<BoundaryVert> mutable_boundary_verts()
- {
- return boundary_vert_.as_mutable_span();
- }
-
- Span<HalfEdge> half_edges() const
- {
- return half_edge_.as_span();
- }
-
- const BoundaryVert &boundary_vert(int boundary_vert_pos) const
- {
- return boundary_vert_[boundary_vert_pos];
- }
-
- const BoundaryVert &next_boundary_vert(int boundary_vert_pos) const
- {
- int n = (boundary_vert_pos + 1) % boundary_vert_.size();
- return boundary_vert_[n];
- }
-
- void set_boundary_connection(int boundary_vert_pos, const BoundaryConnector conn)
- {
- boundary_conn_[boundary_vert_pos] = conn;
- }
-
- const int boundary_connector_edge(int boundary_vert_pos, int edge_index) const
- {
- BLI_assert(edge_index == 0); // Temporary
- return boundary_conn_[boundary_vert_pos].edge;
- }
-
- /* Find the HalfEdge for `edge`, returning nullptr if not found. */
- HalfEdge *find_half_edge(int edge) const;
-};
-
-static std::ostream &operator<<(std::ostream &os, const BevelVertexData &bvd)
-{
- const VertexCap &vc = bvd.vertex_cap();
- os << "bevel vertex data for vertex " << vc.vert << "\n";
- os << vc;
- Span<BoundaryVert> bvs = bvd.boundary_verts();
- os << "boundary verts:\n";
- for (const int i : bvs.index_range()) {
- os << "[" << i << "] " << bvs[i] << "\n";
- }
- Span<HalfEdge> hes = bvd.half_edges();
- os << "boundary edges:\n";
- for (const int i : hes.index_range()) {
- os << "[" << i << "] " << hes[i] << "\n";
- }
- return os;
-}
-
-/** Calculate the `BevelVertexData` for one vertex, `vert`, by the given `amount`.
- * This doesn't calculate limits to the bevel caused by collisions with vertex bevels
- * at adjacent vertices; that needs to done after all of these are calculated,
- * so that this operation can be done in parallel with all other vertex constructions.
- */
-void BevelVertexData::construct_vertex_bevel(int vert, float amount, const MeshTopology &topo)
-{
- construct_vertex_cap(vert, topo);
-
- const int num_edges = vertex_cap().size();
-
- /* There will be one boundary vertex on each edge attached to `vert`. */
- half_edge_.reinitialize(num_edges);
- boundary_vert_.reinitialize(num_edges);
- boundary_conn_.reinitialize(num_edges);
-
- const float3 vert_co = topo.vert_co(vertex_cap().vert);
- for (const int i : IndexRange(num_edges)) {
- BoundaryVert &bv = boundary_vert_[i];
- bv.type = BV_ON_EDGE;
- bv.vc_index = i;
- HalfEdge &he = half_edge_[i];
- he.edge = vertex_cap().edge(i);
- he.type = HE_UNBEVELED;
- he.bv_index = i;
- he.vc_index = i;
-
- /* Set the position of the boundary vertex by sliding at distance `amount` along the edge. */
- float3 dir = topo.edge_dir_from_vert_normalized(he.edge, vert);
- bv.co = vert_co + amount * dir;
- }
-}
-
-HalfEdge *BevelVertexData::find_half_edge(int edge) const
-{
- for (int i : half_edge_.index_range()) {
- if (half_edge_[i].edge == edge) {
- /* There's no non-const rvalue subscripting in Array. */
- HalfEdge *he = const_cast<HalfEdge *>(&half_edge_[i]);
- return he;
- }
- }
- return nullptr;
-}
-
-/** BevelSpec holds the data the specifies what the user wants beveled.
- * There will be a derived class for each type of bevel.
- */
-class BevelSpec {
- public:
- /* Are we beveling vertices, edges, or faces? */
- GeometryNodeBevelMeshMode bevel_mode;
- /* A mask over the elements of the beveled type, saying what is to bovel. */
- IndexMask to_bevel;
-
- BevelSpec(GeometryNodeBevelMeshMode mode, IndexMask to_bevel)
- : bevel_mode(mode), to_bevel(to_bevel)
- {
- }
-
- virtual void dump_spec() = 0;
-};
-
-class VertexBevelSpec : public BevelSpec {
- public:
- /* Indexed by Mesh vertex index, the amount to slide along all edges
- * attached to the vertex. */
- VArray<float> amount;
-
- VertexBevelSpec(IndexMask to_bevel, VArray<float> amount)
- : BevelSpec{GEO_NODE_BEVEL_MESH_VERTICES, to_bevel}, amount(amount)
- {
- }
-
- void dump_spec();
-};
-
-void VertexBevelSpec::dump_spec()
-{
- std::cout << "VertexBevelSpec\n";
- for (const int v : to_bevel.index_range()) {
- if (to_bevel[v]) {
- std::cout << v << ": " << amount[v] << "\n";
- }
- }
-}
-
-class FaceBevelSpec : public BevelSpec {
- public:
- /* Indexed by Mesh poly index, the amount to inset the face by. */
- VArray<float> amount;
- /* Indexed by Mesh poly index, the slope to follow when insetting the face. */
- VArray<float> slope;
- bool use_regions;
-
- FaceBevelSpec(IndexMask to_bevel, VArray<float> amount, VArray<float> slope, bool use_regions)
- : BevelSpec{GEO_NODE_BEVEL_MESH_FACES, to_bevel},
- amount(amount),
- slope(slope),
- use_regions(use_regions)
- {
- }
-
- void dump_spec();
-};
-
-void FaceBevelSpec::dump_spec()
-{
- std::cout << "FaceBevelSpec\n";
- if (use_regions) {
- std::cout << "use regions\n";
- }
- for (const int f : to_bevel.index_range()) {
- if (to_bevel[f]) {
- std::cout << f << ": " << amount[f] << ", slope=" << slope[f] << "\n";
- }
- }
-}
-
-class EdgeBevelSpec : public BevelSpec {
- public:
- /* Indexed by Mesh edge index, the amounts to bevel the edge.
- * `left_amount[0]` is the left side amount for the source end and
- * `right_amount[0]` is the right side amount for the source end,
- * where "left" and "right" mean: those sides as you look
- * along the edge to the source.
- * Similarly, the 1-indexed elements of those arrays are for the
- * destination end, with left and right as you look towards the dest end.
- */
- VArray<float> left_amount[2];
- VArray<float> right_amount[2];
-
- EdgeBevelSpec(IndexMask to_bevel, VArray<float> amount)
- : BevelSpec(GEO_NODE_BEVEL_MESH_EDGES, to_bevel),
- left_amount{amount, amount},
- right_amount{amount, amount}
- {
- }
-
- EdgeBevelSpec(IndexMask to_bevel,
- VArray<float> src_left_amount,
- VArray<float> src_right_amount,
- VArray<float> dst_left_amount,
- VArray<float> dst_right_amount)
- : BevelSpec(GEO_NODE_BEVEL_MESH_EDGES, to_bevel),
- left_amount{src_left_amount, dst_left_amount},
- right_amount{src_right_amount, dst_right_amount}
- {
- }
-
- void dump_spec();
-};
-
-void EdgeBevelSpec::dump_spec()
-{
- std::cout << "EdgeBevelSpec\n";
- for (const int e : to_bevel.index_range()) {
- if (to_bevel[e]) {
- std::cout << e << ": ";
- if (left_amount[0] == right_amount[0] && left_amount[0] == left_amount[1] &&
- left_amount[1] == right_amount[1]) {
- std::cout << left_amount[0][e] << "\n";
- }
- else {
- std::cout << "0(" << left_amount[0][e] << ", " << right_amount[0][e] << ") 1("
- << left_amount[1][e] << ", " << right_amount[1][e] << ")\n";
- }
- }
- }
+bool operator==(const CanonVertPair a, const CanonVertPair b){
+ return a.v1 == b.v1 && a.v2 == b.v2;
}
/** IndexAlloc allocates sequential integers, starting from a given start value. */
@@ -861,6 +306,10 @@ class IndexAlloc {
/** MeshDelta represents a delta to a Mesh: additions and deletions
* of Mesh elements.
+ * New elements will get index numbers starting at the end of the current
+ * range of the elements in the base mesh, `mesh_`.
+ * There is also a fast method for finding an edge, if any, joining two
+ * vertices (either in the base mesh, or in the added vertices, or mixed).
*/
class MeshDelta {
const Mesh &mesh_;
@@ -881,6 +330,8 @@ class MeshDelta {
Vector<int> new_edge_rep_;
Vector<int> new_poly_rep_;
Vector<int> new_loop_rep_;
+ /* Lookup map for added edges. */
+ Map<CanonVertPair, int> new_edge_map_;
public:
MeshDelta(const Mesh &mesh, const MeshTopology &topo);
@@ -890,6 +341,8 @@ class MeshDelta {
int new_edge(int v1, int v2, int rep);
int new_loop(int v, int e, int rep);
int new_face(int loopstart, int totloop, int rep);
+ /* A new face from a span of vertex indices and edge indices (-1 means: make a new edge). */
+ int new_face(Span<int> verts, Span<int> edges, int rep);
/* Find the edge either in mesh or new_edges_, or make a new one if not there. */
int find_or_add_edge(int v1, int v2, int rep);
@@ -945,12 +398,14 @@ int MeshDelta::new_edge(int v1, int v2, int rep)
medge.flag = ME_EDGEDRAW;
new_edges_.append(medge);
new_edge_rep_.append(rep);
+ new_edge_map_.add_new(CanonVertPair(v1, v2), e);
return e;
}
int MeshDelta::find_or_add_edge(int v1, int v2, int rep)
{
- if (v1 < mesh_.totvert) {
+ /* First see if (v1, v2) is an existing edge in the base mesh. */
+ if (v1 < mesh_.totvert && v2 < mesh_.totvert) {
for (int i : topo_.vert_edges(v1)) {
const MEdge &e = mesh_.edges()[i];
if ((e.v1 == v1 && e.v2 == v2) || (e.v1 == v2 && e.v2 == v1)) {
@@ -958,12 +413,10 @@ int MeshDelta::find_or_add_edge(int v1, int v2, int rep)
}
}
}
- /* TODO: have an equivalent of vert_edges() in MeshDelta to make this fast! */
- for (int i : new_edges_.index_range()) {
- const MEdge &e = new_edges_[i];
- if ((e.v1 == v1 && e.v2 == v2) || (e.v1 == v2 && e.v2 == v1)) {
- return mesh_.totedge + i;
- }
+ /* Look inside the new edge map to see if it is there. */
+ int e = new_edge_map_.lookup_default(CanonVertPair(v1, v2), -1);
+ if (e != -1) {
+ return e;
}
return new_edge(v1, v2, rep);
}
@@ -991,6 +444,28 @@ int MeshDelta::new_face(int loopstart, int totloop, int rep)
return f;
}
+int MeshDelta::new_face(Span<int> verts, Span<int> edges, int rep)
+{
+ const int n = verts.size();
+ int lfirst = -1;
+ for (const int i : IndexRange(n)) {
+ const int v1 = verts[i];
+ int e = -1;
+ if (edges.size() != 0) {
+ e = edges[i];
+ }
+ if (e == -1) {
+ const int v2 = verts[(i + 1) % n];
+ e = new_edge(v1, v2, -1);
+ }
+ int l = new_loop(v1, e, -1);
+ if (i == 0) {
+ lfirst = l;
+ }
+ }
+ return new_face(lfirst, n, rep);
+}
+
/** Delete the MPoly and the loops.
* The edges and vertices need to be deleted elsewhere, if necessary.
*/
@@ -1487,6 +962,573 @@ void MeshDelta::print(const std::string &label) const
std::cout << "\n";
}
+class VertexCap;
+
+/** A HalfEdge represents one end of a Mesh Edge.
+ * There is an VertexCap (see following) that the HalfEdge is
+ * connected to, and a position within the ordered edges in
+ * that VertexCap, that will be recorded here.
+ */
+class HalfEdge {
+ public:
+ /* Index of the edge in the Mesh. */
+ int mesh_index{-1};
+ /* Index of this HalfEdge in the edges of a VertexCap. */
+ int cap_index{-1};
+ VertexCap *cap{nullptr};
+
+ HalfEdge()
+ {
+ }
+};
+
+/** A Vertex Cap consists of a vertex in a mesh and an CCW ordering of
+ * alternating edges and faces around it, as viewed from the face's
+ * normal side. Some faces may be missing (i.e., gaps).
+ * (If there are other edges and faces attached to the vertex that
+ * don't fit into this pattern, they need to go into other Vertex Caps
+ * or ignored, for the sake of beveling.)
+ */
+class VertexCap {
+ /* The HalfEdges in CCW order around the cap. */
+ Array<HalfEdge> edges_;
+ /* The mesh indices of the faces: ace_[i] is between edges i and i+1; -1 means no face. */
+ Array<int> faces_;
+
+ public:
+ /* The vertex (as index into a mesh) that the cap is around. */
+ int vert;
+
+ VertexCap() : vert(-1)
+ {
+ }
+
+ /* Initialize for vertex v, given a mesh topo. */
+ void init_from_topo(const int vert, const MeshTopology &topo);
+
+ /* The number of edges around the cap. */
+ int size() const
+ {
+ return edges_.size();
+ }
+
+ /* Edges in CCW order (viewed from top) around the cap. */
+ Span<HalfEdge> edges() const
+ {
+ return edges_.as_span();
+ }
+
+ /* Faces in CCW order (viewed from top) around the cap. -1 means a gap. */
+ Span<int> faces() const
+ {
+ return faces_.as_span();
+ }
+
+ /* The ith edge. */
+ const HalfEdge &edge(int i) const
+ {
+ return edges_[i];
+ }
+ /* The edge after the ith edge (with wraparound). */
+ const HalfEdge &next_edge(int i) const
+ {
+ return i < edges_.size() - 1 ? edges_[i + 1] : edges_[0];
+ }
+ /* The edge before the ith edge (with wraparound). */
+ const HalfEdge &prev_edge(int i) const
+ {
+ return i > 1 ? edges_[i - 1] : edges_.last();
+ }
+
+ /* The face returned may be -1, meaning "gap". */
+ /* The face betwen edge(i) and next_edge(i). */
+ int face(int i) const
+ {
+ return faces_[i];
+ }
+ /* The face between edge(i) and prev_edge(i). */
+ int prev_face(int i) const
+ {
+ return i > 1 ? faces_[i - 1] : faces_.last();
+ }
+ /* True if there is a gap between edges i and next_edge(i). */
+ bool is_gap(int i) const
+ {
+ return face(i) == -1;
+ }
+
+ /* Return the HalfEdge that is for the Mesh edge with index mesh_index. */
+ const HalfEdge *half_edge_for_edge(int mesh_index) const;
+};
+
+/** Construct and return the VertexCap for vertex `vert`. */
+void VertexCap::init_from_topo(const int vert, const MeshTopology &topo)
+{
+ this->vert = vert;
+ Span<int> incident_edges = topo.vert_edges(vert);
+ const int num_edges = incident_edges.size();
+ if (num_edges == 0) {
+ return;
+ }
+
+ /* First check for the most common case: a complete manifold cap:
+ * That is, each edge is incident on exactly two faces and the
+ * edge--face--edge--...--face chain forms a single cycle.
+ */
+ bool all_edges_manifold = true;
+ for (const int e : incident_edges) {
+ if (!topo.edge_is_manifold(e)) {
+ all_edges_manifold = false;
+ break;
+ }
+ }
+ if (all_edges_manifold) {
+ bool is_manifold_cap = true;
+ Array<int> ordered_edges(num_edges, -1);
+ Array<int> ordered_faces(num_edges, -1);
+ Set<int, 16> used_edges;
+ Set<int, 16> used_faces;
+
+ int next_edge = incident_edges[0];
+ for (int slot = 0; slot < num_edges; slot++) {
+ /* Invariant: ordered_edges and ordered_faces are filled
+ * up to slot-1 with a valid sequence for the cap, and
+ * next_edge is a valid continuation edge but we don't
+ * yet know if it has already been used.
+ */
+ ordered_edges[slot] = next_edge;
+ used_edges.add_new(next_edge);
+ /* Find a face attached to next_edge that is not yet used. */
+ int next_face;
+ if (slot == 0) {
+ next_face = topo.edge_faces(next_edge)[0];
+ }
+ else {
+ const int prev_face = ordered_faces[slot - 1];
+ next_face = topo.edge_other_manifold_face(next_edge, prev_face);
+ }
+ if (used_faces.contains(next_face)) {
+ is_manifold_cap = false;
+ break;
+ }
+ ordered_faces[slot] = next_face;
+ next_edge = topo.face_other_edge_at_vert(next_face, vert, next_edge);
+ if (slot < num_edges - 1 && used_edges.contains(next_edge)) {
+ is_manifold_cap = false;
+ break;
+ }
+ }
+ is_manifold_cap = is_manifold_cap && next_edge == ordered_edges[0];
+ if (is_manifold_cap) {
+ /* Check if cap is oriented properly, and fix it if not.
+ * A pair of successive edges in ordered_edges should be going CW
+ * in the face in between. For now, just check the first pair.
+ */
+ if (num_edges > 1) {
+ if (topo.edge_is_successor_in_face(ordered_edges[0], ordered_edges[1], ordered_faces[0])) {
+ /* They are in the wrong orientation, so we need to reverse.
+ * To make interleaving of edges and faces work out, reverse only 1..end of edges
+ * and reverse all of faces.
+ */
+ std::reverse(ordered_edges.begin() + 1, ordered_edges.end());
+ std::reverse(ordered_faces.begin(), ordered_faces.end());
+ }
+ }
+ edges_.reinitialize(ordered_edges.size());
+ for (const int i : ordered_edges.index_range()) {
+ HalfEdge &he = edges_[i];
+ he.cap = this;
+ he.cap_index = i;
+ he.mesh_index = ordered_edges[i];
+ }
+ faces_ = ordered_faces;
+ return;
+ }
+ }
+ std::cout << "to implement: VertexCap for non-manifold edges\n";
+}
+
+const HalfEdge *VertexCap::half_edge_for_edge(int mesh_index) const
+{
+ for (const HalfEdge &he : edges_) {
+ if (he.mesh_index == mesh_index) {
+ return &he;
+ }
+ }
+ return nullptr;
+}
+
+static std::ostream &operator<<(std::ostream &os, const HalfEdge &he)
+{
+ os << "e" << he.mesh_index;
+ if (he.cap != nullptr) {
+ os << "@(v" << he.cap->vert << " pos " << he.cap_index << ")";
+ }
+ return os;
+}
+
+static std::ostream &operator<<(std::ostream &os, const VertexCap &cap)
+{
+ os << "cap at v" << cap.vert << ": ";
+ for (const int i : cap.edges().index_range()) {
+ os << "e" << cap.edge(i).mesh_index << " ";
+ if (cap.face(i) == -1) {
+ os << "<gap> ";
+ }
+ else {
+ os << "f" << cap.face(i) << " ";
+ }
+ }
+ os << "\n";
+ return os;
+}
+
+/** A VertexMesh contains the structure of the new mesh around a bevel-involved vertex.
+ * It uses vertex and face indices in an implicit MeshDelta.
+ * For now, we only need to keep track of the "boundary", the vertices that are on the
+ * outside of the vertex mesh -- the ones that edge meshes and other faces will attach to.
+ * We also need the edges between those vertices, so that we share them when reconstructing faces.
+ */
+class VertexMesh {
+ /* The MeshDelta vertex indices of the boundary of the mesh. */
+ Vector<int> boundary_vert_;
+ /* The MeshDelta edge indices of the edge starting at the corresponding boundary vert. */
+ Vector<int> boundary_edge_;
+
+ public:
+ VertexMesh()
+ {
+ }
+
+ void append_boundary_vert(int v)
+ {
+ boundary_vert_.append(v);
+ }
+
+ void append_boundary_edge(int e)
+ {
+ boundary_edge_.append(e);
+ }
+
+ int boundary_size() const
+ {
+ return boundary_vert_.size();
+ }
+
+ Span<int> boundary_verts_as_span() const
+ {
+ return boundary_vert_.as_span();
+ }
+
+ int boundary_vert(int i) const
+ {
+ return boundary_vert_[i];
+ }
+
+ int next_boundary_vert(int i) const
+ {
+ return boundary_vert_[(i + 1) % boundary_vert_.size()];
+ }
+
+ int next_boundary_pos(int i) const
+ {
+ return (i + 1) % boundary_vert_.size();
+ }
+
+ int prev_boundary_pos(int i) const
+ {
+ return (i + boundary_vert_.size() - 1) % boundary_vert_.size();
+ }
+
+ Span<int> boundary_edges_as_span() const
+ {
+ return boundary_edge_.as_span();
+ }
+
+ int boundary_edge(int i) const
+ {
+ return boundary_edge_[i];
+ }
+
+ /* Find and return the boundary position containing \a v. Return -1 if not found. */
+ int boundary_pos_for_vert(int v) const;
+};
+
+int VertexMesh::boundary_pos_for_vert(int v) const
+{
+ for (int i : boundary_vert_.index_range()) {
+ if (boundary_vert_[i] == v) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static std::ostream &operator<<(std::ostream &os, const VertexMesh &vm)
+{
+ os << "Vertex Mesh\nboundary:";
+ for (const int v : vm.boundary_verts_as_span()) {
+ os << " " << v;
+ }
+ os << "\n";
+ return os;
+}
+
+/** A BevelEdge holds the two ends (HalfEdges) of an edge that is to be beveled.
+ * The underlying edge has a direction, and he1 is the HalfEdge at the source end,
+ * while he2 is the HalfEdge at the destination end.
+ */
+class BevelEdge {
+ /* Index in mesh of the underlying edge. */
+ int edge_;
+
+ public:
+ /* Source end HalfEdge. */
+ HalfEdge *he1;
+ /* Destination end HalfEdge. */
+ HalfEdge *he2;
+
+ BevelEdge() : edge_(-1), he1(nullptr), he2(nullptr)
+ {
+ }
+
+ BevelEdge(int edge, HalfEdge *he1, HalfEdge *he2) : edge_(edge), he1(he1), he2(he2)
+ {
+ }
+
+ int edge() const
+ {
+ return edge_;
+ }
+};
+
+/** BevelVertexData holds the data used to bevel around a vertex. */
+class BevelVertexData {
+ /* The vertex cap for the vertex. */
+ VertexCap vertex_cap_;
+ /* The vertex mesh for the vertex. */
+ VertexMesh vertex_mesh_;
+ /* Map from vertex_cap_ edge position to the attachement point on the boundary
+ * in vertex_mesh_. If the corresponding edge is beveled, this will be the attachment
+ * point for the left side (looking at the vertex) of the edge mesh.
+ */
+ Array<int> cap_pos_to_boundary_pos_;
+
+ public:
+ BevelVertexData()
+ {
+ }
+ ~BevelVertexData()
+ {
+ }
+
+ /* Initialize the vertex cap data for \a vert. */
+ void construct_vertex_cap(int vert, const MeshTopology &topo)
+ {
+ vertex_cap_.init_from_topo(vert, topo);
+ }
+
+ /* Construct the vertex mesh for this vert, for a vertex bevel by \a amount. Assume the vertex
+ * cap is already built. */
+ void construct_vertex_mesh_for_vertex_bevel(float amount,
+ const MeshTopology &topo,
+ MeshDelta &mesh_delta);
+
+ const VertexCap &vertex_cap() const
+ {
+ return vertex_cap_;
+ }
+
+ const VertexMesh &vertex_mesh() const
+ {
+ return vertex_mesh_;
+ }
+
+ const int cap_pos_to_boundary_pos(int cap_pos) const
+ {
+ return cap_pos_to_boundary_pos_[cap_pos];
+ }
+
+ const int beveled_vert() const
+ {
+ return vertex_cap_.vert;
+ }
+};
+
+static std::ostream &operator<<(std::ostream &os, const BevelVertexData &bvd)
+{
+ const VertexCap &vc = bvd.vertex_cap();
+ os << "bevel vertex data for vertex " << vc.vert << "\n";
+ os << vc;
+ os << bvd.vertex_mesh();
+ return os;
+}
+
+/** Pick a face to be a representative for a beveled vertex. */
+static int face_rep_for_beveled_vert(const BevelVertexData &bvd)
+{
+ /* For now: just pick the first face we find. */
+ for (const int f : bvd.vertex_cap().faces()) {
+ if (f != -1) {
+ return f;
+ }
+ }
+ return -1;
+}
+
+/** Construct the vertex mesh for \a vert, for a vertex bevel by \a amount. Assume the vertex cap
+ * is already built.
+ * TODO: deal with collisions.
+ */
+void BevelVertexData::construct_vertex_mesh_for_vertex_bevel(float amount,
+ const MeshTopology &topo,
+ MeshDelta &mesh_delta)
+{
+ const VertexCap &cap = vertex_cap();
+ const int num_edges = cap.size();
+ const int vert = vertex_cap().vert;
+ const float3 vert_co = topo.vert_co(vertex_cap().vert);
+ cap_pos_to_boundary_pos_.reinitialize(num_edges);
+ for (const int i : IndexRange(num_edges)) {
+ const HalfEdge &he = cap.edge(i);
+ /* Set the position of the boundary vertex by sliding at distance `amount` along the edge. */
+ float3 dir = topo.edge_dir_from_vert_normalized(he.mesh_index, vert);
+ float3 new_co = vert_co + amount * dir;
+ int newv = mesh_delta.new_vert(new_co, vert);
+ cap_pos_to_boundary_pos_[i] = i;
+ vertex_mesh_.append_boundary_vert(newv);
+ }
+ /* All of the edges are new, so can pass an empty span for the edges. */
+ mesh_delta.new_face(
+ vertex_mesh_.boundary_verts_as_span(), Span<int>(), face_rep_for_beveled_vert(*this));
+}
+
+/** BevelSpec holds the data the specifies what the user wants beveled.
+ * There will be a derived class for each type of bevel.
+ */
+class BevelSpec {
+ public:
+ /* Are we beveling vertices, edges, or faces? */
+ GeometryNodeBevelMeshMode bevel_mode;
+ /* A mask over the elements of the beveled type, saying what is to bovel. */
+ IndexMask to_bevel;
+
+ BevelSpec(GeometryNodeBevelMeshMode mode, IndexMask to_bevel)
+ : bevel_mode(mode), to_bevel(to_bevel)
+ {
+ }
+
+ virtual void dump_spec() = 0;
+};
+
+class VertexBevelSpec : public BevelSpec {
+ public:
+ /* Indexed by Mesh vertex index, the amount to slide along all edges
+ * attached to the vertex. */
+ VArray<float> amount;
+
+ VertexBevelSpec(IndexMask to_bevel, VArray<float> amount)
+ : BevelSpec{GEO_NODE_BEVEL_MESH_VERTICES, to_bevel}, amount(amount)
+ {
+ }
+
+ void dump_spec();
+};
+
+void VertexBevelSpec::dump_spec()
+{
+ std::cout << "VertexBevelSpec\n";
+ for (const int v : to_bevel.index_range()) {
+ if (to_bevel[v]) {
+ std::cout << v << ": " << amount[v] << "\n";
+ }
+ }
+}
+
+class FaceBevelSpec : public BevelSpec {
+ public:
+ /* Indexed by Mesh poly index, the amount to inset the face by. */
+ VArray<float> amount;
+ /* Indexed by Mesh poly index, the slope to follow when insetting the face. */
+ VArray<float> slope;
+ bool use_regions;
+
+ FaceBevelSpec(IndexMask to_bevel, VArray<float> amount, VArray<float> slope, bool use_regions)
+ : BevelSpec{GEO_NODE_BEVEL_MESH_FACES, to_bevel},
+ amount(amount),
+ slope(slope),
+ use_regions(use_regions)
+ {
+ }
+
+ void dump_spec();
+};
+
+void FaceBevelSpec::dump_spec()
+{
+ std::cout << "FaceBevelSpec\n";
+ if (use_regions) {
+ std::cout << "use regions\n";
+ }
+ for (const int f : to_bevel.index_range()) {
+ if (to_bevel[f]) {
+ std::cout << f << ": " << amount[f] << ", slope=" << slope[f] << "\n";
+ }
+ }
+}
+
+class EdgeBevelSpec : public BevelSpec {
+ public:
+ /* Indexed by Mesh edge index, the amounts to bevel the edge.
+ * `left_amount[0]` is the left side amount for the source end and
+ * `right_amount[0]` is the right side amount for the source end,
+ * where "left" and "right" mean: those sides as you look
+ * along the edge to the source.
+ * Similarly, the 1-indexed elements of those arrays are for the
+ * destination end, with left and right as you look towards the dest end.
+ */
+ VArray<float> left_amount[2];
+ VArray<float> right_amount[2];
+
+ EdgeBevelSpec(IndexMask to_bevel, VArray<float> amount)
+ : BevelSpec(GEO_NODE_BEVEL_MESH_EDGES, to_bevel),
+ left_amount{amount, amount},
+ right_amount{amount, amount}
+ {
+ }
+
+ EdgeBevelSpec(IndexMask to_bevel,
+ VArray<float> src_left_amount,
+ VArray<float> src_right_amount,
+ VArray<float> dst_left_amount,
+ VArray<float> dst_right_amount)
+ : BevelSpec(GEO_NODE_BEVEL_MESH_EDGES, to_bevel),
+ left_amount{src_left_amount, dst_left_amount},
+ right_amount{src_right_amount, dst_right_amount}
+ {
+ }
+
+ void dump_spec();
+};
+
+void EdgeBevelSpec::dump_spec()
+{
+ std::cout << "EdgeBevelSpec\n";
+ for (const int e : to_bevel.index_range()) {
+ if (to_bevel[e]) {
+ std::cout << e << ": ";
+ if (left_amount[0] == right_amount[0] && left_amount[0] == left_amount[1] &&
+ left_amount[1] == right_amount[1]) {
+ std::cout << left_amount[0][e] << "\n";
+ }
+ else {
+ std::cout << "0(" << left_amount[0][e] << ", " << right_amount[0][e] << ") 1("
+ << left_amount[1][e] << ", " << right_amount[1][e] << ")\n";
+ }
+ }
+ }
+}
+
/** BevelData holds the global data needed for a bevel. */
class BevelData {
/* The specification of the bevel. */
@@ -1549,19 +1591,38 @@ class BevelData {
return nullptr;
}
- Span<BevelVertexData> beveled_vertices_data() const
- {
- return bevel_vert_data_.as_span();
- }
+ /* Return all faces affected by bevel, in a deterministic order.
+ * Assume the bevel_vert_data_ has been initialized.
+ */
+ Array<int> affected_faces() const;
- MutableSpan<BevelVertexData> mutable_beveled_vertices_data()
- {
- return bevel_vert_data_.as_mutable_span();
- }
+ /* Find and return the input and output vertices in the new mesh that will replace vertex \a v
+ * when in a face with incoming edge \a e.
+ */
+ std::pair<int, int> attach_verts_for_beveled_vert(int v, int e, const BevelVertexData *bvd);
+
+ /* Allocate in mesh_delta_ and return a new MLoop for an edge from v1 to v2. Make the MEdge if
+ * needed. */
+ int new_loop_for_vert_pair(int v1, int v2);
+
+ /* Allocate in mesh_delta_ new MLoops for all int edges from vi to vo in the boundary given in
+ * bvd. If bvd is null, does nothing. */
+ std::pair<int, int> new_loops_for_beveled_vert(const BevelVertexData *bvd, int vi, int vo);
void print(const std::string &label) const;
};
+void BevelData::print(const std::string &label) const
+{
+ if (label.size() > 0) {
+ std::cout << label << " ";
+ }
+ std::cout << "BevelData\n";
+ for (const BevelVertexData &bvd : bevel_vert_data_.as_span()) {
+ std::cout << bvd;
+ }
+}
+
/** Make a transation map from mesh vertex index to indices in bevel_vert_data_. */
void BevelData::setup_vert_map()
{
@@ -1571,29 +1632,94 @@ void BevelData::setup_vert_map()
}
}
-void BevelData::print(const std::string &label) const
+/* Return all faces affected by bevel, in a deterministic order.
+ * Assume the bevel_vert_data_ has been initialized.
+ */
+Array<int> BevelData::affected_faces() const
{
- if (label.size() > 0) {
- std::cout << label << " ";
+ Set<int> need_face;
+ for (const BevelVertexData &bvd : bevel_vert_data_) {
+ for (const int f : bvd.vertex_cap().faces()) {
+ need_face.add(f);
+ }
}
- std::cout << "BevelData\n";
- for (const BevelVertexData &bvd : bevel_vert_data_.as_span()) {
- std::cout << bvd;
+ int n = need_face.size();
+ Array<int> ans(n);
+ int i = 0;
+ for (const int f : need_face) {
+ ans[i++] = f;
}
+ /* Sort the answer to make the result deterministic run-to-run. */
+ std::sort(ans.begin(), ans.end());
+ return ans;
}
-/** Pick a face to be a representative for a beveled vertex. */
-static int face_rep_for_beveled_vert(const BevelVertexData &bvd)
+/* Find and return the input and output vertices in the new mesh that will replace vertex \a v
+ * when in a face with incoming edge \a e. If \a v is beveled then \a bvd is its BevelVertexData.
+ */
+std::pair<int, int> BevelData::attach_verts_for_beveled_vert(int v,
+ int e,
+ const BevelVertexData *bvd)
{
- /* For now: just pick the first face we find. */
- for (const int f : bvd.vertex_cap().faces()) {
- if (f != -1) {
- return f;
+ if (bvd == nullptr) {
+ return std::pair<int, int>(v, v);
+ }
+ const HalfEdge *he = bvd->vertex_cap().half_edge_for_edge(e);
+ BLI_assert(he != nullptr);
+ const HalfEdge *he_next = &bvd->vertex_cap().next_edge(he->cap_index);
+ int vin_pos = bvd->cap_pos_to_boundary_pos(he_next->cap_index);
+ int vout_pos = bvd->cap_pos_to_boundary_pos(he->cap_index);
+ int vin = bvd->vertex_mesh().boundary_vert(vin_pos);
+ int vout = bvd->vertex_mesh().boundary_vert(vout_pos);
+ return std::pair<int, int>(vin, vout);
+}
+
+/* Allocate in mesh_delta_ new MLoop for an edge from v1 to v2. Make the MEdge if needed. */
+int BevelData::new_loop_for_vert_pair(int v1, int v2)
+{
+ /* TODO: something about representative edges and loops. */
+ int e = mesh_delta_.find_or_add_edge(v1, v2, -1);
+ return mesh_delta_.new_loop(v1, e, -1);
+}
+
+/* Allocate in mesh_delta_ new MLoops for all int edges from vi to vo in the boundary given in bvd.
+ * If bvd is null, does nothing.
+ * Return a pair of (index of first loop allocate, number allocated).
+ */
+std::pair<int, int> BevelData::new_loops_for_beveled_vert(const BevelVertexData *bvd,
+ int vi,
+ int vo)
+{
+ int lfirst = -1;
+ int count = 0;
+ if (vi == vo || bvd == nullptr) {
+ return std::pair<int, int>(lfirst, count);
+ }
+ const int vi_boundary_pos = bvd->vertex_mesh().boundary_pos_for_vert(vi);
+ int pos = vi_boundary_pos;
+ const VertexMesh &vmesh = bvd->vertex_mesh();
+ for (;;) {
+ int pos_prev = vmesh.prev_boundary_pos(pos);
+ int v_prev = vmesh.boundary_vert(pos_prev);
+ int v = vmesh.boundary_vert(pos);
+ int l = new_loop_for_vert_pair(v, v_prev);
+ if (pos == vi_boundary_pos) {
+ lfirst = l;
+ }
+ count++;
+ if (v_prev == vo) {
+ break;
}
+ pos = pos_prev;
+ BLI_assert(v_prev != vi); /* Shouldn't wrap around. */
}
- return -1;
+ return std::pair<int, int>(lfirst, count);
}
+/** Calculate the vertex bevels and leave the result in the mesh_delta_ member.
+ * Assume that the `spec_` member has been set up as a `VertexBevelSpec` to
+ * specify how the vertex bevel is to be done.
+ */
void BevelData::calculate_vertex_bevel()
{
const VertexBevelSpec *spec = dynamic_cast<const VertexBevelSpec *>(spec_);
@@ -1602,164 +1728,71 @@ void BevelData::calculate_vertex_bevel()
threading::parallel_for(to_bevel.index_range(), 1024, [&](const IndexRange range) {
for (const int i : range) {
const int vert = to_bevel[i];
- bevel_vert_data_[i].construct_vertex_bevel(vert, spec->amount[vert], *topo_);
+ bevel_vert_data_[i].construct_vertex_cap(vert, *topo_);
}
});
setup_vert_map();
- /* Make the polygons that will replace the beveled vertices. */
+ /* Make the polygons that will replace the beveled vertices. Since this allocates new elements
+ * in mesh_delta_, need to do this serially.
+ * Also delete the vertices and the original edges attached to them.
+ */
for (BevelVertexData &bvd : bevel_vert_data_) {
/* Allocate vertices for the boundary vertices. */
- MutableSpan<BoundaryVert> boundary_verts = bvd.mutable_boundary_verts();
- int n = boundary_verts.size();
- for (BoundaryVert &bv : boundary_verts) {
- bv.mesh_index = mesh_delta_.new_vert(bv.co, bvd.beveled_vert());
- }
- /* Allocate the edges and loops for the polygon. */
- Array<int> edges(n);
- Array<int> loops(n);
- int lfirst = -1;
- int lprev = -1;
- for (int i : IndexRange(n)) {
- const BoundaryVert &bv = boundary_verts[i];
- const BoundaryVert &bv_next = boundary_verts[i == n - 1 ? 0 : i + 1];
- int v1 = bv.mesh_index;
- int v2 = bv_next.mesh_index;
- int e = mesh_delta_.new_edge(v1, v2, -1);
- bvd.set_boundary_connection(i, BoundaryConnector(e));
- int l = mesh_delta_.new_loop(v1, e, -1);
- if (i == 0) {
- lfirst = l;
- }
- lprev = l;
- }
- /* Now make the face. Assert that we allocated contiguous loop indices. */
- BLI_assert(lfirst != -1 && lprev == lfirst + n - 1);
- mesh_delta_.new_face(lfirst, n, face_rep_for_beveled_vert(bvd));
+ const int vert = bvd.beveled_vert();
+ const float amount = spec->amount[vert];
+ bvd.construct_vertex_mesh_for_vertex_bevel(amount, *topo_, mesh_delta_);
+
/* Delete the beveled vertex, which is now being replaced.
* TODO: only do this if there is no extra stuff attached to it.
*/
- mesh_delta_.delete_vert(bvd.vertex_cap().vert);
+ mesh_delta_.delete_vert(vert);
/* We also delete any edges that were using that vertex. */
for (int e : topo_->vert_edges(bvd.vertex_cap().vert)) {
mesh_delta_.delete_edge(e);
}
}
- /* Reconstruct all faces that involve a beveled vertex.
- * For now, go through all faces to see which ones are affected.
- * TODO: gather affected faces via connections to beveled vertices.
- */
+ /* Reconstruct all faces that involve a beveled vertex. */
Span<MPoly> polys = mesh_->polys();
Span<MLoop> loops = mesh_->loops();
- for (int f : IndexRange(mesh_->totpoly)) {
+ Array<int> faces_to_reconstruct = affected_faces();
+ for (const int f : faces_to_reconstruct) {
const MPoly &mpoly = polys[f];
-
- /* Are there any beveled vertices in f? */
- int any_affected_vert = false;
+ /* Need new loops in the recontruction because they must be contiguous in the new face. */
+ int lfirst = -1;
+ int num_loops = 0;
for (int l = mpoly.loopstart; l < mpoly.loopstart + mpoly.totloop; l++) {
- const int v = loops[l].v;
- const BevelVertexData *bvd = bevel_vertex_data(v);
- if (bvd != nullptr) {
- any_affected_vert = true;
- break;
+ const MLoop &mloop = loops[l];
+ int lnext = l < mpoly.loopstart + mpoly.totloop - 1 ? l + 1 : mpoly.loopstart;
+ const MLoop &mloop_next = loops[lnext];
+ int v1 = mloop.v;
+ int v2 = mloop_next.v;
+ const BevelVertexData *bvd1 = bevel_vertex_data(v1);
+ const BevelVertexData *bvd2 = bevel_vertex_data(v2);
+ int v1i, v1o, v2i, v2o;
+ std::tie(v1i, v1o) = attach_verts_for_beveled_vert(v1, mloop.e, bvd1);
+ std::tie(v2i, v2o) = attach_verts_for_beveled_vert(v2, mloop_next.e, bvd2);
+ if (lfirst == -1) {
+ int cnt;
+ std::tie(lfirst, cnt) = new_loops_for_beveled_vert(bvd1, v1i, v1o);
+ num_loops += cnt;
}
- }
- if (any_affected_vert) {
- /* We need to reconstruct f. We can't reuse unaffected loops since they won't be
- * contiguous.
- */
- int lfirst = -1;
- int totloop = 0;
- for (int l = mpoly.loopstart; l < mpoly.loopstart + mpoly.totloop; l++) {
- const MLoop &mloop = loops[l];
- const MLoop &mloop_next =
- loops[l == mpoly.loopstart + mpoly.totloop - 1 ? mpoly.loopstart : l + 1];
- int v1 = mloop.v;
- int v2 = mloop_next.v;
- int e = mloop.e;
- BevelVertexData *bvd1 = bevel_vertex_data(v1);
- BevelVertexData *bvd2 = bevel_vertex_data(v2);
- HalfEdge *he1 = bvd1 == nullptr ? nullptr : bvd1->find_half_edge(e);
- HalfEdge *he2 = bvd2 == nullptr ? nullptr : bvd2->find_half_edge(e);
- const BoundaryVert *bv1 = he1 == nullptr ? nullptr : &bvd1->boundary_vert(he1->bv_index);
- const BoundaryVert *bv2 = he2 == nullptr ? nullptr : &bvd2->boundary_vert(he2->bv_index);
-
- /* If v1 is beveled, we need to add the boundary connector from the next boundary vertex
- * CCW from bv1 (which is therefore the previous boundary vertex when going around our
- * current face) to bv1. This is the reverse of the connector from the current edge to
- * the next. Then after that, the new edge that replaces e. We assume the edge(s) for the
- * connector have already been made.
- */
- int lnew = -1;
- if (bvd1 != nullptr) {
- /* Temporary: for now assume only one edge in the connector. */
- int econn = bvd1->boundary_connector_edge(bv1->vc_index, 0);
- BLI_assert(econn != -1);
- int econn_v1, econn_v2;
- mesh_delta_.get_edge_verts(econn, &econn_v1, &econn_v2);
- BLI_assert(econn_v1 == bv1->mesh_index);
- lnew = mesh_delta_.new_loop(econn_v2, econn, l);
- if (l == mpoly.loopstart) {
- lfirst = lnew;
- }
- totloop++;
- /* Now we need an edge from bv1->mesh_index to either v2 (if v2 is not beveled)
- * or to bv2->mesh_index. But that edge may have been made already. If so,
- * we will find its mesh index in he2->mesh_index.
- * It is also possible we made the edge and stored it in he1->mesh_index,
- * while doing the adjacent face.
- */
- if (bvd2 == nullptr) {
- if (he1->mesh_index != -1) {
- e = he1->mesh_index;
- }
- else {
- e = mesh_delta_.new_edge(bv1->mesh_index, v2, mloop.e);
- }
- lnew = mesh_delta_.new_loop(bv1->mesh_index, e, l);
- }
- else {
- if (he1->mesh_index != -1) {
- e = he1->mesh_index;
- }
- else if (he2->mesh_index != -1) {
- e = he2->mesh_index;
- }
- else {
- e = mesh_delta_.new_edge(bv1->mesh_index, bv2->mesh_index, mloop.e);
- he2->mesh_index = e;
- }
- lnew = mesh_delta_.new_loop(bv1->mesh_index, e, l);
- }
- he1->mesh_index = e;
- }
- else if (bvd2 != nullptr) {
- /* v1 is not beveled and v2 is. */
- if (he2->mesh_index != -1) {
- e = he2->mesh_index;
- }
- else {
- e = mesh_delta_.new_edge(v1, bv2->mesh_index, mloop.e);
- he2->mesh_index = e;
- }
- lnew = mesh_delta_.new_loop(v1, e, l);
- }
- else {
- /* Neither v1 nor v2 is beveled, so we can use the existing e. */
- lnew = mesh_delta_.new_loop(v1, e, l);
- }
- totloop++;
-
- if (lfirst == -1) {
- lfirst = lnew;
- }
+ /* Invariant here: have made new loops for everything in previous part of mpoly,
+ * and up to and including mloop.v (if unbeveled) or the part of the boundary
+ * for mloop.v that replaces mloop.v. num_loops is the number of loops made so far. */
+ int lnew = new_loop_for_vert_pair(v1o, v2i);
+ if (lfirst == -1) {
+ lfirst = lnew;
}
- mesh_delta_.new_face(lfirst, totloop, f);
- /* Delete the old face (which also deletes its loops). */
- mesh_delta_.delete_face(f);
+ num_loops++;
+ std::pair<int, int> lnew_and_cnt = new_loops_for_beveled_vert(bvd2, v2i, v2o);
+ num_loops += lnew_and_cnt.second;
}
+ mesh_delta_.new_face(lfirst, num_loops, f);
+ /* Delete the old face (which also deletes its loops). */
+ mesh_delta_.delete_face(f);
}
}
@@ -1775,6 +1808,19 @@ void BevelData::calculate_edge_bevel()
}
const int tot_need_vert = need_vert.size();
bevel_vert_data_.reinitialize(tot_need_vert);
+ /* We want the vertices in a deterministic order, so loop through edges again, removing from
+ * need_vert as we go. */
+ int bvd_index = 0;
+ for (const int e : to_bevel) {
+ for (const int i : IndexRange(2)) {
+ const int v = i == 0 ? topo_->edge_v1(e) : topo_->edge_v2(e);
+ if (need_vert.contains(v)) {
+ bevel_vert_data_[bvd_index].construct_vertex_cap(v, *topo_);
+ need_vert.remove(v);
+ bvd_index++;
+ }
+ }
+ }
}
void BevelData::calculate_face_bevel()