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
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenkernel/BKE_uv_islands.hh867
1 files changed, 867 insertions, 0 deletions
diff --git a/source/blender/blenkernel/BKE_uv_islands.hh b/source/blender/blenkernel/BKE_uv_islands.hh
new file mode 100644
index 00000000000..6fdd3976d74
--- /dev/null
+++ b/source/blender/blenkernel/BKE_uv_islands.hh
@@ -0,0 +1,867 @@
+
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include <fstream>
+#include <optional>
+
+#include "BLI_array.hh"
+#include "BLI_edgehash.h"
+#include "BLI_float3x3.hh"
+#include "BLI_map.hh"
+#include "BLI_math.h"
+#include "BLI_math_vec_types.hh"
+#include "BLI_rect.h"
+#include "BLI_vector.hh"
+#include "BLI_vector_list.hh"
+
+#include "DNA_meshdata_types.h"
+
+#include "PIL_time_utildefines.h"
+
+namespace blender::bke::uv_islands {
+/*
+ * When enabled various parts of the code would generate an SVG file to visual see how the
+ * algorithm makes decisions.
+ *
+ * TODO: These will be removed before this patch will land in master.
+ */
+//#define DEBUG_SVG
+//#define VALIDATE
+
+struct MeshEdge;
+struct MeshPrimitive;
+struct UVBorder;
+struct UVEdge;
+struct UVIslands;
+struct UVIslandsMask;
+struct UVPrimitive;
+struct UVPrimitiveEdge;
+struct UVVertex;
+
+struct MeshVertex {
+ int64_t v;
+ Vector<MeshEdge *> edges;
+};
+
+struct MeshUVVert {
+ MeshVertex *vertex;
+ float2 uv;
+ int64_t loop;
+};
+
+struct MeshEdge {
+ MeshVertex *vert1;
+ MeshVertex *vert2;
+ Vector<MeshPrimitive *> primitives;
+};
+
+/** Represents a triangle in 3d space (MLoopTri) */
+struct MeshPrimitive {
+ int64_t index;
+ int64_t poly;
+ Vector<MeshEdge *, 3> edges;
+ Vector<MeshUVVert, 3> vertices;
+
+ /**
+ * UV island this primitive belongs to. This is used to speed up the initial uv island
+ * extraction, but should not be used when extending uv islands.
+ */
+ int64_t uv_island_id;
+
+ MeshUVVert *get_other_uv_vertex(const MeshVertex *v1, const MeshVertex *v2)
+ {
+ BLI_assert(vertices[0].vertex == v1 || vertices[1].vertex == v1 || vertices[2].vertex == v1);
+ BLI_assert(vertices[0].vertex == v2 || vertices[1].vertex == v2 || vertices[2].vertex == v2);
+ for (MeshUVVert &uv_vertex : vertices) {
+ if (uv_vertex.vertex != v1 && uv_vertex.vertex != v2) {
+ return &uv_vertex;
+ }
+ }
+ return nullptr;
+ }
+
+ rctf uv_bounds() const;
+
+ bool has_shared_uv_edge(const MeshPrimitive *other) const
+ {
+ int shared_uv_verts = 0;
+ for (const MeshUVVert &vert : vertices) {
+ for (const MeshUVVert &other_vert : other->vertices) {
+ if (vert.uv == other_vert.uv) {
+ shared_uv_verts += 1;
+ }
+ }
+ }
+ return shared_uv_verts >= 2;
+ }
+};
+
+/**
+ * MeshData contains input geometry data converted in a list of primitives, edges and vertices for
+ * quick access for both local space and uv space.
+ */
+struct MeshData {
+ public:
+ const MLoopTri *looptri;
+ const int64_t looptri_len;
+ const int64_t vert_len;
+ const MLoop *mloop;
+ const MLoopUV *mloopuv;
+
+ public:
+ Vector<MeshPrimitive> primitives;
+ Vector<MeshEdge> edges;
+ Vector<MeshVertex> vertices;
+ /** Total number of uv islands detected. */
+ int64_t uv_island_len;
+
+ explicit MeshData(const MLoopTri *looptri,
+ const int64_t looptri_len,
+ const int64_t vert_len,
+ const MLoop *mloop,
+ const MLoopUV *mloopuv)
+ : looptri(looptri),
+ looptri_len(looptri_len),
+ vert_len(vert_len),
+ mloop(mloop),
+ mloopuv(mloopuv)
+ {
+ TIMEIT_START(init_mesh_data);
+ init_vertices();
+ init_primitives();
+ init_edges();
+ init_primitive_uv_island_ids();
+
+#ifdef VALIDATE
+ for (const MeshVertex &v : vertices) {
+ printf("Vert {v%lld}\n", v.v);
+ for (const MeshEdge *e : v.edges) {
+ printf("-Edge {v%lld v%lld}\n", e->vert1->v, e->vert2->v);
+ for (const MeshPrimitive *p : e->primitives) {
+ printf(" -Prim {p%lld, v%lld v%lld v%lld}\n",
+ p->index,
+ p->vertices[0].vertex->v,
+ p->vertices[1].vertex->v,
+ p->vertices[2].vertex->v);
+ }
+ }
+ }
+#endif
+ TIMEIT_END(init_mesh_data);
+ }
+
+ void init_vertices()
+ {
+ vertices.reserve(vert_len);
+ for (int64_t i = 0; i < vert_len; i++) {
+ MeshVertex vert;
+ vert.v = i;
+ vertices.append(vert);
+ }
+ }
+
+ void init_primitives()
+ {
+ primitives.reserve(looptri_len);
+ for (int64_t i = 0; i < looptri_len; i++) {
+ const MLoopTri &tri = looptri[i];
+ MeshPrimitive primitive;
+ primitive.index = i;
+ primitive.poly = tri.poly;
+
+ for (int j = 0; j < 3; j++) {
+ MeshUVVert uv_vert;
+ uv_vert.loop = tri.tri[j];
+ uv_vert.vertex = &vertices[mloop[uv_vert.loop].v];
+ uv_vert.uv = mloopuv[uv_vert.loop].uv;
+ primitive.vertices.append(uv_vert);
+ }
+ primitives.append(primitive);
+ }
+ }
+
+ void init_edges()
+ {
+ edges.reserve(looptri_len * 2);
+ EdgeHash *eh = BLI_edgehash_new_ex(__func__, looptri_len * 3);
+ for (int64_t i = 0; i < looptri_len; i++) {
+ const MLoopTri &tri = looptri[i];
+ MeshPrimitive &primitive = primitives[i];
+ for (int j = 0; j < 3; j++) {
+ int v1 = mloop[tri.tri[j]].v;
+ int v2 = mloop[tri.tri[(j + 1) % 3]].v;
+ /* TODO: Use lookup_ptr to be able to store edge 0. */
+ void *v = BLI_edgehash_lookup(eh, v1, v2);
+ int64_t edge_index;
+ if (v == nullptr) {
+ edge_index = edges.size();
+ BLI_edgehash_insert(eh, v1, v2, POINTER_FROM_INT(edge_index + 1));
+ MeshEdge edge;
+ edge.vert1 = &vertices[v1];
+ edge.vert2 = &vertices[v2];
+ edges.append(edge);
+ MeshEdge *edge_ptr = &edges.last();
+ vertices[v1].edges.append(edge_ptr);
+ vertices[v2].edges.append(edge_ptr);
+ }
+ else {
+ edge_index = POINTER_AS_INT(v) - 1;
+ }
+
+ MeshEdge *edge = &edges[edge_index];
+ edge->primitives.append(&primitive);
+ primitive.edges.append(edge);
+ }
+ }
+ BLI_edgehash_free(eh, nullptr);
+ }
+
+ static const int64_t INVALID_UV_ISLAND_ID = -1;
+ /**
+ * NOTE: doesn't support weird topology where unconnected mesh primitives share the same uv
+ * island. For a accurate implementation we should use implement an uv_prim_lookup.
+ */
+ static void extract_uv_neighbors(Vector<MeshPrimitive *> &prims_to_add, MeshPrimitive *primitive)
+ {
+ for (MeshEdge *edge : primitive->edges) {
+ for (MeshPrimitive *other_primitive : edge->primitives) {
+ if (primitive == other_primitive) {
+ continue;
+ }
+ if (other_primitive->uv_island_id != MeshData::INVALID_UV_ISLAND_ID) {
+ continue;
+ }
+
+ if (primitive->has_shared_uv_edge(other_primitive)) {
+ prims_to_add.append(other_primitive);
+ }
+ }
+ }
+ }
+
+ void init_primitive_uv_island_ids()
+ {
+ for (MeshPrimitive &primitive : primitives) {
+ primitive.uv_island_id = INVALID_UV_ISLAND_ID;
+ }
+
+ int64_t uv_island_id = 0;
+ Vector<MeshPrimitive *> prims_to_add;
+ for (MeshPrimitive &primitive : primitives) {
+ /* Early exit when uv island id is already extracted during uv neighbor extractions. */
+ if (primitive.uv_island_id != INVALID_UV_ISLAND_ID) {
+ continue;
+ }
+
+ prims_to_add.append(&primitive);
+ while (!prims_to_add.is_empty()) {
+ MeshPrimitive *primitive = prims_to_add.pop_last();
+ primitive->uv_island_id = uv_island_id;
+ extract_uv_neighbors(prims_to_add, primitive);
+ }
+ uv_island_id++;
+ }
+ uv_island_len = uv_island_id;
+ }
+};
+
+struct UVVertex {
+ MeshVertex *vertex;
+ /* Position in uv space. */
+ float2 uv;
+
+ /* uv edges that share this UVVertex. */
+ Vector<UVEdge *> uv_edges;
+
+ struct {
+ bool is_border : 1;
+ bool is_extended : 1;
+ } flags;
+
+ explicit UVVertex()
+ {
+ flags.is_border = false;
+ flags.is_extended = false;
+ }
+
+ explicit UVVertex(const MeshUVVert &vert) : vertex(vert.vertex), uv(vert.uv)
+ {
+ flags.is_border = false;
+ flags.is_extended = false;
+ }
+};
+
+struct UVEdge {
+ std::array<UVVertex *, 2> vertices;
+ Vector<UVPrimitive *, 2> uv_primitives;
+
+ bool has_shared_edge(const MeshUVVert &v1, const MeshUVVert &v2) const
+ {
+ return (vertices[0]->uv == v1.uv && vertices[1]->uv == v2.uv) ||
+ (vertices[0]->uv == v2.uv && vertices[1]->uv == v1.uv);
+ }
+
+ bool has_shared_edge(const UVVertex &v1, const UVVertex &v2) const
+ {
+ return (vertices[0]->uv == v1.uv && vertices[1]->uv == v2.uv) ||
+ (vertices[0]->uv == v2.uv && vertices[1]->uv == v1.uv);
+ }
+
+ bool has_shared_edge(const UVEdge &other) const
+ {
+ return has_shared_edge(*other.vertices[0], *other.vertices[1]);
+ }
+
+ bool has_same_vertices(const MeshVertex &vert1, const MeshVertex &vert2) const
+ {
+ return (vertices[0]->vertex == &vert1 && vertices[1]->vertex == &vert2) ||
+ (vertices[0]->vertex == &vert2 && vertices[1]->vertex == &vert1);
+ }
+
+ bool has_same_uv_vertices(const UVEdge &other) const
+ {
+ return has_shared_edge(other) &&
+ has_same_vertices(*other.vertices[0]->vertex, *other.vertices[1]->vertex);
+ ;
+ }
+
+ bool has_same_vertices(const MeshEdge &edge) const
+ {
+ return has_same_vertices(*edge.vert1, *edge.vert2);
+ }
+
+ bool is_border_edge() const
+ {
+ return uv_primitives.size() == 1;
+ }
+
+ void append_to_uv_vertices()
+ {
+ for (UVVertex *vertex : vertices) {
+ vertex->uv_edges.append_non_duplicates(this);
+ }
+ }
+
+ UVVertex *get_other_uv_vertex(const MeshVertex *vertex)
+ {
+ if (vertices[0]->vertex == vertex) {
+ return vertices[1];
+ }
+ return vertices[0];
+ }
+};
+
+struct UVPrimitive {
+ /**
+ * Index of the primitive in the original mesh.
+ */
+ MeshPrimitive *primitive;
+ Vector<UVEdge *, 3> edges;
+
+ explicit UVPrimitive(MeshPrimitive *primitive) : primitive(primitive)
+ {
+ }
+
+ void append_to_uv_edges()
+ {
+ for (UVEdge *uv_edge : edges) {
+ uv_edge->uv_primitives.append_non_duplicates(this);
+ }
+ }
+ void append_to_uv_vertices()
+ {
+ for (UVEdge *uv_edge : edges) {
+ uv_edge->append_to_uv_vertices();
+ }
+ }
+
+ Vector<std::pair<UVEdge *, UVEdge *>> shared_edges(UVPrimitive &other)
+ {
+ Vector<std::pair<UVEdge *, UVEdge *>> result;
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ if (edges[i]->has_shared_edge(*other.edges[j])) {
+ result.append(std::pair<UVEdge *, UVEdge *>(edges[i], other.edges[j]));
+ }
+ }
+ }
+ return result;
+ }
+
+ bool has_shared_edge(const UVPrimitive &other) const
+ {
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ if (edges[i]->has_shared_edge(*other.edges[j])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ bool has_shared_edge(const MeshPrimitive &primitive) const
+ {
+ for (const UVEdge *uv_edge : edges) {
+ const MeshUVVert *v1 = &primitive.vertices.last();
+ for (int i = 0; i < primitive.vertices.size(); i++) {
+ const MeshUVVert *v2 = &primitive.vertices[i];
+ if (uv_edge->has_shared_edge(*v1, *v2)) {
+ return true;
+ }
+ v1 = v2;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the UVVertex in the order that the verts are ordered in the MeshPrimitive.
+ */
+ const UVVertex *get_uv_vertex(const uint8_t mesh_vert_index) const
+ {
+ const MeshVertex *mesh_vertex = primitive->vertices[mesh_vert_index].vertex;
+ for (const UVEdge *uv_edge : edges) {
+ for (const UVVertex *uv_vert : uv_edge->vertices) {
+ if (uv_vert->vertex == mesh_vertex) {
+ return uv_vert;
+ }
+ }
+ }
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+
+ /**
+ * Get the UVEdge that share the given uv coordinates.
+ * Will assert when no UVEdge found.
+ */
+ UVEdge *get_uv_edge(const float2 uv1, const float2 uv2) const
+ {
+ for (UVEdge *uv_edge : edges) {
+ const float2 &e1 = uv_edge->vertices[0]->uv;
+ const float2 &e2 = uv_edge->vertices[1]->uv;
+ if ((e1 == uv1 && e2 == uv2) || (e1 == uv2 && e2 == uv1)) {
+ return uv_edge;
+ }
+ }
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+
+ UVEdge *get_uv_edge(const MeshVertex *v1, const MeshVertex *v2) const
+ {
+ for (UVEdge *uv_edge : edges) {
+ const MeshVertex *e1 = uv_edge->vertices[0]->vertex;
+ const MeshVertex *e2 = uv_edge->vertices[1]->vertex;
+ if ((e1 == v1 && e2 == v2) || (e1 == v2 && e2 == v1)) {
+ return uv_edge;
+ }
+ }
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+
+ const bool contains_uv_vertex(const UVVertex *uv_vertex) const
+ {
+ for (UVEdge *edge : edges) {
+ if (std::find(edge->vertices.begin(), edge->vertices.end(), uv_vertex) !=
+ edge->vertices.end()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ const UVVertex *get_other_uv_vertex(const UVVertex *v1, const UVVertex *v2) const
+ {
+ BLI_assert(contains_uv_vertex(v1));
+ BLI_assert(contains_uv_vertex(v2));
+
+ for (const UVEdge *edge : edges) {
+ for (const UVVertex *uv_vertex : edge->vertices) {
+ if (uv_vertex != v1 && uv_vertex != v2) {
+ return uv_vertex;
+ }
+ }
+ }
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+
+ UVBorder extract_border() const;
+};
+
+struct UVBorderEdge {
+ UVEdge *edge;
+ bool tag = false;
+ UVPrimitive *uv_primitive;
+ /* Should the vertices of the edge be evaluated in reverse order. */
+ bool reverse_order = false;
+
+ int64_t index = -1;
+ int64_t prev_index = -1;
+ int64_t next_index = -1;
+ int64_t border_index = -1;
+
+ explicit UVBorderEdge(UVEdge *edge, UVPrimitive *uv_primitive)
+ : edge(edge), uv_primitive(uv_primitive)
+ {
+ }
+
+ UVVertex *get_uv_vertex(int index)
+ {
+ int actual_index = reverse_order ? 1 - index : index;
+ return edge->vertices[actual_index];
+ }
+
+ const UVVertex *get_uv_vertex(int index) const
+ {
+ int actual_index = reverse_order ? 1 - index : index;
+ return edge->vertices[actual_index];
+ }
+
+ /**
+ * Get the uv vertex from the primitive that is not part of the edge.
+ */
+ const UVVertex *get_other_uv_vertex() const
+ {
+ return uv_primitive->get_other_uv_vertex(edge->vertices[0], edge->vertices[1]);
+ }
+
+ float length() const
+ {
+ return len_v2v2(edge->vertices[0]->uv, edge->vertices[1]->uv);
+ }
+};
+
+struct UVBorderCorner {
+ UVBorderEdge *first;
+ UVBorderEdge *second;
+ float angle;
+
+ UVBorderCorner(UVBorderEdge *first, UVBorderEdge *second, float angle)
+ : first(first), second(second), angle(angle)
+ {
+ }
+
+ /**
+ * Calculate a uv coordinate between the edges of the corner.
+ *
+ * 'min_uv_distance' is the minimum distance between the corner and the
+ * resulting uv coordinate. The distance is in uv space.
+ */
+ float2 uv(float factor, float min_uv_distance);
+};
+
+struct UVBorder {
+ /** Ordered list of UV Verts of the border of this island. */
+ // TODO: support multiple rings + order (CW, CCW)
+ Vector<UVBorderEdge> edges;
+
+ /**
+ * Check if the border is counter clock wise from its island.
+ */
+ bool is_ccw() const;
+
+ /**
+ * Flip the order of the verts, changing the order between CW and CCW.
+ */
+ void flip();
+
+ /**
+ * Calculate the outside angle of the given vert.
+ */
+ float outside_angle(const UVBorderEdge &vert) const;
+
+ void update_indexes(uint64_t border_index);
+
+ static std::optional<UVBorder> extract_from_edges(Vector<UVBorderEdge> &edges);
+
+#ifdef VALIDATE
+ void validate() const;
+#endif
+
+ /** Remove edge from the border. updates the indexes. */
+ void remove(int64_t index)
+ {
+ /* Could read the border_index from any border edge as they are consistent. */
+ uint64_t border_index = edges[0].border_index;
+ edges.remove(index);
+ update_indexes(border_index);
+ }
+};
+
+struct UVIsland {
+ VectorList<UVVertex> uv_vertices;
+ VectorList<UVEdge> uv_edges;
+ VectorList<UVPrimitive> uv_primitives;
+ /**
+ * List of borders of this island. There can be multiple borders per island as a border could
+ * be completely encapsulated by another one.
+ */
+ Vector<UVBorder> borders;
+
+ /**
+ * Key is mesh vert index, Value is list of UVVertices that refer to the mesh vertex with that
+ * index. Map is used internally to quickly lookup similar UVVertices.
+ */
+ Map<int64_t, Vector<UVVertex *>> uv_vertex_lookup;
+
+ UVVertex *lookup(const UVVertex &vertex)
+ {
+ int64_t vert_index = vertex.vertex->v;
+ Vector<UVVertex *> &vertices = uv_vertex_lookup.lookup_or_add_default(vert_index);
+ for (UVVertex *v : vertices) {
+ if (v->uv == vertex.uv) {
+ return v;
+ }
+ }
+ return nullptr;
+ }
+
+ UVVertex *lookup_or_create(const UVVertex &vertex)
+ {
+ UVVertex *found_vertex = lookup(vertex);
+ if (found_vertex != nullptr) {
+ return found_vertex;
+ }
+
+ uv_vertices.append(vertex);
+ UVVertex *result = &uv_vertices.last();
+ result->uv_edges.clear();
+ /* v is already a key. Ensured by UVIsland::lookup in this method. */
+ uv_vertex_lookup.lookup(vertex.vertex->v).append(result);
+ return result;
+ }
+
+ UVEdge *lookup(const UVEdge &edge)
+ {
+ UVVertex *found_vertex = lookup(*edge.vertices[0]);
+ if (found_vertex == nullptr) {
+ return nullptr;
+ }
+ for (UVEdge *e : found_vertex->uv_edges) {
+ UVVertex *other_vertex = e->get_other_uv_vertex(found_vertex->vertex);
+ if (other_vertex->vertex == edge.vertices[1]->vertex &&
+ other_vertex->uv == edge.vertices[1]->uv) {
+ return e;
+ }
+ }
+ return nullptr;
+ }
+
+ UVEdge *lookup_or_create(const UVEdge &edge)
+ {
+ UVEdge *found_edge = lookup(edge);
+ if (found_edge != nullptr) {
+ return found_edge;
+ }
+
+ uv_edges.append(edge);
+ UVEdge *result = &uv_edges.last();
+ result->uv_primitives.clear();
+ return result;
+ }
+
+ /** Initialize the border attribute. */
+ void extract_borders();
+ /** Iterative extend border to fit the mask. */
+ void extend_border(const UVIslandsMask &mask, const short island_index);
+
+ private:
+ void append(const UVPrimitive &primitive)
+ {
+ uv_primitives.append(primitive);
+ UVPrimitive *new_prim_ptr = &uv_primitives.last();
+ for (int i = 0; i < 3; i++) {
+ UVEdge *other_edge = primitive.edges[i];
+ UVEdge uv_edge_template;
+ uv_edge_template.vertices[0] = lookup_or_create(*other_edge->vertices[0]);
+ uv_edge_template.vertices[1] = lookup_or_create(*other_edge->vertices[1]);
+ new_prim_ptr->edges[i] = lookup_or_create(uv_edge_template);
+ new_prim_ptr->edges[i]->append_to_uv_vertices();
+ new_prim_ptr->edges[i]->uv_primitives.append(new_prim_ptr);
+ }
+ }
+
+ public:
+ bool has_shared_edge(const UVPrimitive &primitive) const
+ {
+ for (const VectorList<UVPrimitive>::UsedVector &prims : uv_primitives) {
+ for (const UVPrimitive &prim : prims) {
+ if (prim.has_shared_edge(primitive)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ bool has_shared_edge(const MeshPrimitive &primitive) const
+ {
+ for (const VectorList<UVPrimitive>::UsedVector &primitives : uv_primitives) {
+ for (const UVPrimitive &prim : primitives) {
+ if (prim.has_shared_edge(primitive)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ const void extend_border(const UVPrimitive &primitive)
+ {
+ for (const VectorList<UVPrimitive>::UsedVector &primitives : uv_primitives) {
+ for (const UVPrimitive &prim : primitives) {
+ if (prim.has_shared_edge(primitive)) {
+ append(primitive);
+ }
+ }
+ }
+ }
+
+#ifdef VALIDATE
+ void validate_primitives() const
+ {
+ /* Each UVPrimitive that points to the same mesh primitive should contain the same mesh
+ * vertices (and can be retrieved in the same order). */
+ for (const VectorList<UVPrimitive>::UsedVector &primitives : uv_primitives) {
+ for (const UVPrimitive &prim_a : primitives) {
+ validate_primitive(prim_a);
+ }
+ }
+ }
+ void validate_primitive(const UVPrimitive &prim_a) const
+ {
+ /* Each UVPrimitive that points to the same mesh primitive should contain the same mesh
+ * vertices (and can be retrieved in the same order). */
+ for (const VectorList<UVPrimitive>::UsedVector &primitives : uv_primitives) {
+ for (const UVPrimitive &prim_b : primitives) {
+ if (prim_a.primitive != prim_b.primitive) {
+ continue;
+ }
+ for (int i = 0; i < 3; i++) {
+ const UVVertex *v1 = prim_a.get_uv_vertex(i);
+ const UVVertex *v2 = prim_b.get_uv_vertex(i);
+ BLI_assert(v1->vertex->v == v2->vertex->v);
+ }
+ }
+ }
+ }
+
+ void validate_border() const
+ {
+ /* Each UVPrimitive that points to the same mesh primitive should contain the same mesh
+ * vertices (and can be retrieved in the same order). */
+ for (const VectorList<UVPrimitive>::UsedVector &primitives_a : uv_primitives) {
+ for (const UVPrimitive &prim_a : primitives_a) {
+ for (const VectorList<UVPrimitive>::UsedVector &primitives_b : uv_primitives) {
+ for (const UVPrimitive &prim_b : primitives_b) {
+ if (prim_a.primitive != prim_b.primitive) {
+ continue;
+ }
+ for (int i = 0; i < 3; i++) {
+ const UVVertex *v1 = prim_a.get_uv_vertex(i);
+ const UVVertex *v2 = prim_b.get_uv_vertex(i);
+ BLI_assert(v1->vertex->v == v2->vertex->v);
+ }
+ }
+ }
+ }
+ }
+ for (const UVBorder &border : borders) {
+ border.validate();
+ }
+ }
+#endif
+};
+
+#ifdef DEBUG_SVG
+/* Debug functions to export to a SVG file. */
+void svg_header(std::ostream &ss);
+void svg(std::ostream &ss, const UVIsland &islands, int step);
+void svg(std::ostream &ss, const UVIslands &islands, int step);
+void svg(std::ostream &ss, const UVPrimitive &primitive);
+void svg(std::ostream &ss, const UVPrimitive &primitive, int step);
+void svg(std::ostream &ss, const UVIslandsMask &mask, int step);
+void svg(std::ostream &ss, const UVBorder &border, int step);
+void svg_footer(std::ostream &ss);
+#endif
+
+struct UVIslands {
+ Vector<UVIsland> islands;
+
+ explicit UVIslands(MeshData &mesh_data);
+
+ void extract_borders();
+ void extend_borders(const UVIslandsMask &islands_mask);
+
+#ifdef VALIDATE
+ private:
+ bool validate() const
+ {
+ /* After operations it is not allowed that islands share any edges. In that case it should
+ * already be merged. */
+ for (int i = 0; i < islands.size() - 1; i++) {
+ for (int j = i + 1; j < islands.size(); j++) {
+ for (const VectorList<UVPrimitive>::UsedVector &primitives : islands[j].uv_primitives) {
+ for (const UVPrimitive &prim : primitives) {
+ if (islands[i].has_shared_edge(prim)) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }
+#endif
+};
+
+/** Mask to find the index of the UVIsland for a given UV coordinate. */
+struct UVIslandsMask {
+
+ /** Mask for each udim tile. */
+ struct Tile {
+ float2 udim_offset;
+ ushort2 tile_resolution;
+ ushort2 mask_resolution;
+ Array<uint16_t> mask;
+
+ Tile(float2 udim_offset, ushort2 tile_resolution);
+
+ bool is_masked(const uint16_t island_index, const float2 uv) const;
+ bool contains(const float2 uv) const;
+ float get_pixel_size_in_uv_space() const;
+ };
+
+ Vector<Tile> tiles;
+
+ void add_tile(float2 udim_offset, ushort2 resolution);
+
+ /**
+ * Find a tile containing the given uv coordinate.
+ */
+ const Tile *find_tile(const float2 uv) const;
+
+ /**
+ * Is the given uv coordinate part of the given island_index mask.
+ *
+ * true - part of the island mask.
+ * false - not part of the island mask.
+ */
+ bool is_masked(const uint16_t island_index, const float2 uv) const;
+
+ /**
+ * Add the given UVIslands to the mask. Tiles should be added beforehand using the 'add_tile'
+ * method.
+ */
+ void add(const UVIslands &islands);
+
+ void dilate(int max_iterations);
+};
+
+} // namespace blender::bke::uv_islands