Blender V4.3
sculpt_geodesic.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cmath>
10#include <cstdlib>
11
12#include "MEM_guardedalloc.h"
13
14#include "BLI_linklist_stack.h"
15#include "BLI_math_geom.h"
16#include "BLI_math_vector.h"
17#include "BLI_task.h"
18
19#include "DNA_brush_types.h"
20#include "DNA_mesh_types.h"
21#include "DNA_object_types.h"
22
23#include "BKE_ccg.hh"
24#include "BKE_context.hh"
25#include "BKE_mesh.hh"
26#include "BKE_mesh_mapping.hh"
27#include "BKE_object.hh"
28#include "BKE_paint.hh"
29#include "BKE_pbvh_api.hh"
30
31#include "paint_intern.hh"
32#include "sculpt_geodesic.hh"
33#include "sculpt_intern.hh"
34
35#include "bmesh.hh"
36
37#define SCULPT_GEODESIC_VERTEX_NONE -1
38
40
41/* Propagate distance from v1 and v2 to v0. */
43 const int v0,
44 const int v1,
45 const int v2,
47 const Set<int> &initial_verts)
48{
49 if (initial_verts.contains(v0)) {
50 return false;
51 }
52
53 BLI_assert(dists[v1] != FLT_MAX);
54 if (dists[v0] <= dists[v1]) {
55 return false;
56 }
57
58 float dist0;
60 BLI_assert(dists[v2] != FLT_MAX);
61 if (dists[v0] <= dists[v2]) {
62 return false;
63 }
65 vert_positions[v0], vert_positions[v1], vert_positions[v2], dists[v1], dists[v2]);
66 }
67 else {
68 float vec[3];
69 sub_v3_v3v3(vec, vert_positions[v1], vert_positions[v0]);
70 dist0 = dists[v1] + len_v3(vec);
71 }
72
73 if (dist0 < dists[v0]) {
74 dists[v0] = dist0;
75 return true;
76 }
77
78 return false;
79}
80
82 const Span<int2> edges,
83 const OffsetIndices<int> faces,
84 const Span<int> corner_verts,
85 const GroupedSpan<int> vert_to_edge_map,
86 const GroupedSpan<int> edge_to_face_map,
87 const Span<bool> hide_poly,
88 const Set<int> &initial_verts,
89 const float limit_radius)
90{
91 const float limit_radius_sq = limit_radius * limit_radius;
92
93 Array<float> dists(vert_positions.size());
94 BitVector<> edge_tag(edges.size());
95
96 /* Both contain edge indices encoded as *void. */
97 BLI_LINKSTACK_DECLARE(queue, void *);
98 BLI_LINKSTACK_DECLARE(queue_next, void *);
99
100 BLI_LINKSTACK_INIT(queue);
101 BLI_LINKSTACK_INIT(queue_next);
102
103 for (const int i : vert_positions.index_range()) {
104 if (initial_verts.contains(i)) {
105 dists[i] = 0.0f;
106 }
107 else {
108 dists[i] = FLT_MAX;
109 }
110 }
111
112 /* Masks vertices that are further than limit radius from an initial vertex. As there is no need
113 * to define a distance to them the algorithm can stop earlier by skipping them. */
114 BitVector<> affected_vert(vert_positions.size());
115
116 if (limit_radius == FLT_MAX) {
117 /* In this case, no need to loop through all initial vertices to check distances as they are
118 * all going to be affected. */
119 affected_vert.fill(true);
120 }
121 else {
122 /* This is an O(n^2) loop used to limit the geodesic distance calculation to a radius. When
123 * this optimization is needed, it is expected for the tool to request the distance to a low
124 * number of vertices (usually just 1 or 2). */
125 for (const int v : initial_verts) {
126 const float *v_co = vert_positions[v];
127 for (const int i : vert_positions.index_range()) {
128 if (len_squared_v3v3(v_co, vert_positions[i]) <= limit_radius_sq) {
129 affected_vert[i].set();
130 }
131 }
132 }
133 }
134
135 /* Add edges adjacent to an initial vertex to the queue. */
136 for (const int i : edges.index_range()) {
137 const int v1 = edges[i][0];
138 const int v2 = edges[i][1];
139 if (!affected_vert[v1] && !affected_vert[v2]) {
140 continue;
141 }
142 if (dists[v1] != FLT_MAX || dists[v2] != FLT_MAX) {
144 }
145 }
146
147 do {
148 while (BLI_LINKSTACK_SIZE(queue)) {
149 const int e = POINTER_AS_INT(BLI_LINKSTACK_POP(queue));
150 int v1 = edges[e][0];
151 int v2 = edges[e][1];
152
153 if (dists[v1] == FLT_MAX || dists[v2] == FLT_MAX) {
154 if (dists[v1] > dists[v2]) {
155 std::swap(v1, v2);
156 }
158 vert_positions, v2, v1, SCULPT_GEODESIC_VERTEX_NONE, dists, initial_verts);
159 }
160
161 for (const int face : edge_to_face_map[e]) {
162 if (!hide_poly.is_empty() && hide_poly[face]) {
163 continue;
164 }
165 for (const int v_other : corner_verts.slice(faces[face])) {
166 if (ELEM(v_other, v1, v2)) {
167 continue;
168 }
170 vert_positions, v_other, v1, v2, dists, initial_verts))
171 {
172 for (const int e_other : vert_to_edge_map[v_other]) {
173 int ev_other;
174 if (edges[e_other][0] == v_other) {
175 ev_other = edges[e_other][1];
176 }
177 else {
178 ev_other = edges[e_other][0];
179 }
180
181 if (e_other != e && !edge_tag[e_other] &&
182 (edge_to_face_map[e_other].is_empty() || dists[ev_other] != FLT_MAX))
183 {
184 if (affected_vert[v_other] || affected_vert[ev_other]) {
185 edge_tag[e_other].set();
186 BLI_LINKSTACK_PUSH(queue_next, POINTER_FROM_INT(e_other));
187 }
188 }
189 }
190 }
191 }
192 }
193 }
194
195 for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) {
196 const int e = POINTER_AS_INT(lnk->link);
197 edge_tag[e].reset();
198 }
199
200 BLI_LINKSTACK_SWAP(queue, queue_next);
201
202 } while (BLI_LINKSTACK_SIZE(queue));
203
204 BLI_LINKSTACK_FREE(queue);
205 BLI_LINKSTACK_FREE(queue_next);
206
207 return dists;
208}
209
210} // namespace blender::ed::sculpt_paint::geodesic
General operations, lookup, etc. for blender objects.
A BVH for high poly meshes.
#define BLI_assert(a)
Definition BLI_assert.h:50
float geodesic_distance_propagate_across_triangle(const float v0[3], const float v1[3], const float v2[3], float dist1, float dist2)
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define ELEM(...)
Object is a sort of wrapper for general info.
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
bool contains(const Key &key) const
Definition BLI_set.hh:291
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
void fill(const bool value)
Array< float > distances_create(const Span< float3 > vert_positions, const Span< int2 > edges, const OffsetIndices< int > faces, const Span< int > corner_verts, const GroupedSpan< int > vert_to_edge_map, const GroupedSpan< int > edge_to_face_map, const Span< bool > hide_poly, const Set< int > &initial_verts, const float limit_radius)
static bool sculpt_geodesic_mesh_test_dist_add(Span< float3 > vert_positions, const int v0, const int v1, const int v2, MutableSpan< float > dists, const Set< int > &initial_verts)
#define SCULPT_GEODESIC_VERTEX_NONE
#define FLT_MAX
Definition stdcycles.h:14
struct LinkNode * next