1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2020 Blender Foundation.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup edsculpt
22  */
23 
24 #include "MEM_guardedalloc.h"
25 
26 #include "BLI_blenlib.h"
27 #include "BLI_edgehash.h"
28 #include "BLI_math.h"
29 #include "BLI_task.h"
30 
31 #include "DNA_brush_types.h"
32 #include "DNA_mesh_types.h"
33 #include "DNA_meshdata_types.h"
34 #include "DNA_object_types.h"
35 
36 #include "BKE_brush.h"
37 #include "BKE_ccg.h"
38 #include "BKE_colortools.h"
39 #include "BKE_context.h"
40 #include "BKE_mesh.h"
41 #include "BKE_multires.h"
42 #include "BKE_node.h"
43 #include "BKE_object.h"
44 #include "BKE_paint.h"
45 #include "BKE_pbvh.h"
46 #include "BKE_scene.h"
47 
48 #include "paint_intern.h"
49 #include "sculpt_intern.h"
50 
51 #include "GPU_immediate.h"
52 #include "GPU_immediate_util.h"
53 #include "GPU_matrix.h"
54 #include "GPU_state.h"
55 
56 #include "bmesh.h"
57 
58 #include <math.h>
59 #include <stdlib.h>
60 
61 #define BOUNDARY_VERTEX_NONE -1
62 #define BOUNDARY_STEPS_NONE -1
63 
64 typedef struct BoundaryInitialVertexFloodFillData {
65   int initial_vertex;
66   int boundary_initial_vertex_steps;
67   int boundary_initial_vertex;
68   int *floodfill_steps;
69   float radius_sq;
70 } BoundaryInitialVertexFloodFillData;
71 
boundary_initial_vertex_floodfill_cb(SculptSession * ss,int from_v,int to_v,bool is_duplicate,void * userdata)72 static bool boundary_initial_vertex_floodfill_cb(
73     SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
74 {
75   BoundaryInitialVertexFloodFillData *data = userdata;
76 
77   if (!SCULPT_vertex_visible_get(ss, to_v)) {
78     return false;
79   }
80 
81   if (!is_duplicate) {
82     data->floodfill_steps[to_v] = data->floodfill_steps[from_v] + 1;
83   }
84   else {
85     data->floodfill_steps[to_v] = data->floodfill_steps[from_v];
86   }
87 
88   if (SCULPT_vertex_is_boundary(ss, to_v)) {
89     if (data->floodfill_steps[to_v] < data->boundary_initial_vertex_steps) {
90       data->boundary_initial_vertex_steps = data->floodfill_steps[to_v];
91       data->boundary_initial_vertex = to_v;
92     }
93   }
94 
95   const float len_sq = len_squared_v3v3(SCULPT_vertex_co_get(ss, data->initial_vertex),
96                                         SCULPT_vertex_co_get(ss, to_v));
97   return len_sq < data->radius_sq;
98 }
99 
100 /* From a vertex index anywhere in the mesh, returns the closest vertex in a mesh boundary inside
101  * the given radius, if it exists. */
sculpt_boundary_get_closest_boundary_vertex(SculptSession * ss,const int initial_vertex,const float radius)102 static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss,
103                                                        const int initial_vertex,
104                                                        const float radius)
105 {
106 
107   if (SCULPT_vertex_is_boundary(ss, initial_vertex)) {
108     return initial_vertex;
109   }
110 
111   SculptFloodFill flood;
112   SCULPT_floodfill_init(ss, &flood);
113   SCULPT_floodfill_add_initial(&flood, initial_vertex);
114 
115   BoundaryInitialVertexFloodFillData fdata = {
116       .initial_vertex = initial_vertex,
117       .boundary_initial_vertex = BOUNDARY_VERTEX_NONE,
118       .boundary_initial_vertex_steps = INT_MAX,
119       .radius_sq = radius * radius,
120   };
121 
122   fdata.floodfill_steps = MEM_calloc_arrayN(
123       SCULPT_vertex_count_get(ss), sizeof(int), "floodfill steps");
124 
125   SCULPT_floodfill_execute(ss, &flood, boundary_initial_vertex_floodfill_cb, &fdata);
126   SCULPT_floodfill_free(&flood);
127 
128   MEM_freeN(fdata.floodfill_steps);
129   return fdata.boundary_initial_vertex;
130 }
131 
132 /* Used to allocate the memory of the boundary index arrays. This was decided considered the most
133  * common use cases for the brush deformers, taking into account how many vertices those
134  * deformations usually need in the boundary. */
135 static int BOUNDARY_INDICES_BLOCK_SIZE = 300;
136 
sculpt_boundary_index_add(SculptBoundary * boundary,const int new_index,const float distance,GSet * included_vertices)137 static void sculpt_boundary_index_add(SculptBoundary *boundary,
138                                       const int new_index,
139                                       const float distance,
140                                       GSet *included_vertices)
141 {
142 
143   boundary->vertices[boundary->num_vertices] = new_index;
144   if (boundary->distance) {
145     boundary->distance[new_index] = distance;
146   }
147   if (included_vertices) {
148     BLI_gset_add(included_vertices, POINTER_FROM_INT(new_index));
149   }
150   boundary->num_vertices++;
151   if (boundary->num_vertices >= boundary->vertices_capacity) {
152     boundary->vertices_capacity += BOUNDARY_INDICES_BLOCK_SIZE;
153     boundary->vertices = MEM_reallocN_id(
154         boundary->vertices, boundary->vertices_capacity * sizeof(int), "boundary indices");
155   }
156 };
157 
sculpt_boundary_preview_edge_add(SculptBoundary * boundary,const int v1,const int v2)158 static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int v1, const int v2)
159 {
160 
161   boundary->edges[boundary->num_edges].v1 = v1;
162   boundary->edges[boundary->num_edges].v2 = v2;
163   boundary->num_edges++;
164 
165   if (boundary->num_edges >= boundary->edges_capacity) {
166     boundary->edges_capacity += BOUNDARY_INDICES_BLOCK_SIZE;
167     boundary->edges = MEM_reallocN_id(boundary->edges,
168                                       boundary->edges_capacity * sizeof(SculptBoundaryPreviewEdge),
169                                       "boundary edges");
170   }
171 };
172 
173 /**
174  * This function is used to check where the propagation should stop when calculating the boundary,
175  * as well as to check if the initial vertex is valid.
176  */
sculpt_boundary_is_vertex_in_editable_boundary(SculptSession * ss,const int initial_vertex)177 static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss,
178                                                            const int initial_vertex)
179 {
180 
181   if (!SCULPT_vertex_visible_get(ss, initial_vertex)) {
182     return false;
183   }
184 
185   int neighbor_count = 0;
186   int boundary_vertex_count = 0;
187   SculptVertexNeighborIter ni;
188   SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, initial_vertex, ni) {
189     if (SCULPT_vertex_visible_get(ss, ni.index)) {
190       neighbor_count++;
191       if (SCULPT_vertex_is_boundary(ss, ni.index)) {
192         boundary_vertex_count++;
193       }
194     }
195   }
196   SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
197 
198   /* Corners are ambiguous as it can't be decide which boundary should be active. The flood fill
199    * should also stop at corners. */
200   if (neighbor_count <= 2) {
201     return false;
202   }
203 
204   /* Non manifold geometry in the mesh boundary.
205    * The deformation result will be unpredictable and not very useful. */
206   if (boundary_vertex_count > 2) {
207     return false;
208   }
209 
210   return true;
211 }
212 
213 /* Flood fill that adds to the boundary data all the vertices from a boundary and its duplicates.
214  */
215 
216 typedef struct BoundaryFloodFillData {
217   SculptBoundary *boundary;
218   GSet *included_vertices;
219   EdgeSet *preview_edges;
220 
221   int last_visited_vertex;
222 
223 } BoundaryFloodFillData;
224 
boundary_floodfill_cb(SculptSession * ss,int from_v,int to_v,bool is_duplicate,void * userdata)225 static bool boundary_floodfill_cb(
226     SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
227 {
228   BoundaryFloodFillData *data = userdata;
229   SculptBoundary *boundary = data->boundary;
230   if (SCULPT_vertex_is_boundary(ss, to_v)) {
231     const float edge_len = len_v3v3(SCULPT_vertex_co_get(ss, from_v),
232                                     SCULPT_vertex_co_get(ss, to_v));
233     const float distance_boundary_to_dst = boundary->distance ?
234                                                boundary->distance[from_v] + edge_len :
235                                                0.0f;
236     sculpt_boundary_index_add(boundary, to_v, distance_boundary_to_dst, data->included_vertices);
237     if (!is_duplicate) {
238       sculpt_boundary_preview_edge_add(boundary, from_v, to_v);
239     }
240     return sculpt_boundary_is_vertex_in_editable_boundary(ss, to_v);
241   }
242   return false;
243 }
244 
sculpt_boundary_indices_init(SculptSession * ss,SculptBoundary * boundary,const bool init_boundary_distances,const int initial_boundary_index)245 static void sculpt_boundary_indices_init(SculptSession *ss,
246                                          SculptBoundary *boundary,
247                                          const bool init_boundary_distances,
248                                          const int initial_boundary_index)
249 {
250 
251   const int totvert = SCULPT_vertex_count_get(ss);
252   boundary->vertices = MEM_malloc_arrayN(
253       BOUNDARY_INDICES_BLOCK_SIZE, sizeof(int), "boundary indices");
254   if (init_boundary_distances) {
255     boundary->distance = MEM_calloc_arrayN(totvert, sizeof(float), "boundary distances");
256   }
257   boundary->edges = MEM_malloc_arrayN(
258       BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge), "boundary edges");
259 
260   GSet *included_vertices = BLI_gset_int_new_ex("included vertices", BOUNDARY_INDICES_BLOCK_SIZE);
261   SculptFloodFill flood;
262   SCULPT_floodfill_init(ss, &flood);
263 
264   boundary->initial_vertex = initial_boundary_index;
265   copy_v3_v3(boundary->initial_vertex_position,
266              SCULPT_vertex_co_get(ss, boundary->initial_vertex));
267   sculpt_boundary_index_add(boundary, initial_boundary_index, 0.0f, included_vertices);
268   SCULPT_floodfill_add_initial(&flood, initial_boundary_index);
269 
270   BoundaryFloodFillData fdata = {
271       .boundary = boundary,
272       .included_vertices = included_vertices,
273       .last_visited_vertex = BOUNDARY_VERTEX_NONE,
274 
275   };
276 
277   SCULPT_floodfill_execute(ss, &flood, boundary_floodfill_cb, &fdata);
278   SCULPT_floodfill_free(&flood);
279 
280   /* Check if the boundary loops into itself and add the extra preview edge to close the loop. */
281   if (fdata.last_visited_vertex != BOUNDARY_VERTEX_NONE &&
282       sculpt_boundary_is_vertex_in_editable_boundary(ss, fdata.last_visited_vertex)) {
283     SculptVertexNeighborIter ni;
284     SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, fdata.last_visited_vertex, ni) {
285       if (BLI_gset_haskey(included_vertices, POINTER_FROM_INT(ni.index)) &&
286           sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.index)) {
287         sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.index);
288         boundary->forms_loop = true;
289       }
290     }
291     SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
292   }
293 
294   BLI_gset_free(included_vertices, NULL);
295 }
296 
297 /**
298  * This functions initializes all data needed to calculate falloffs and deformation from the
299  * boundary into the mesh into a #SculptBoundaryEditInfo array. This includes how many steps are
300  * needed to go from a boundary vertex to an interior vertex and which vertex of the boundary is
301  * the closest one.
302  */
sculpt_boundary_edit_data_init(SculptSession * ss,SculptBoundary * boundary,const int initial_vertex,const float radius)303 static void sculpt_boundary_edit_data_init(SculptSession *ss,
304                                            SculptBoundary *boundary,
305                                            const int initial_vertex,
306                                            const float radius)
307 {
308   const int totvert = SCULPT_vertex_count_get(ss);
309 
310   const bool has_duplicates = BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS;
311 
312   boundary->edit_info = MEM_malloc_arrayN(
313       totvert, sizeof(SculptBoundaryEditInfo), "Boundary edit info");
314 
315   for (int i = 0; i < totvert; i++) {
316     boundary->edit_info[i].original_vertex = BOUNDARY_VERTEX_NONE;
317     boundary->edit_info[i].num_propagation_steps = BOUNDARY_STEPS_NONE;
318   }
319 
320   GSQueue *current_iteration = BLI_gsqueue_new(sizeof(int));
321   GSQueue *next_iteration = BLI_gsqueue_new(sizeof(int));
322 
323   /* Initialized the first iteration with the vertices already in the boundary. This is propagation
324    * step 0. */
325   BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices");
326   for (int i = 0; i < boundary->num_vertices; i++) {
327     boundary->edit_info[boundary->vertices[i]].original_vertex = boundary->vertices[i];
328     boundary->edit_info[boundary->vertices[i]].num_propagation_steps = 0;
329 
330     /* This ensures that all duplicate vertices in the boundary have the same original_vertex
331      * index, so the deformation for them will be the same. */
332     if (has_duplicates) {
333       SculptVertexNeighborIter ni_duplis;
334       SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, boundary->vertices[i], ni_duplis) {
335         if (ni_duplis.is_duplicate) {
336           boundary->edit_info[ni_duplis.index].original_vertex = boundary->vertices[i];
337         }
338       }
339       SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis);
340     }
341 
342     BLI_gsqueue_push(current_iteration, &boundary->vertices[i]);
343   }
344 
345   int num_propagation_steps = 0;
346   float accum_distance = 0.0f;
347 
348   while (true) {
349     /* Stop adding steps to edit info. This happens when a steps is further away from the boundary
350      * than the brush radius or when the entire mesh was already processed. */
351     if (accum_distance > radius || BLI_gsqueue_is_empty(current_iteration)) {
352       boundary->max_propagation_steps = num_propagation_steps;
353       break;
354     }
355 
356     while (!BLI_gsqueue_is_empty(current_iteration)) {
357       int from_v;
358       BLI_gsqueue_pop(current_iteration, &from_v);
359 
360       SculptVertexNeighborIter ni;
361       SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) {
362         const bool is_visible = SCULPT_vertex_visible_get(ss, ni.index);
363         if (is_visible &&
364             boundary->edit_info[ni.index].num_propagation_steps == BOUNDARY_STEPS_NONE) {
365           boundary->edit_info[ni.index].original_vertex =
366               boundary->edit_info[from_v].original_vertex;
367 
368           BLI_BITMAP_ENABLE(visited_vertices, ni.index);
369 
370           if (ni.is_duplicate) {
371             /* Grids duplicates handling. */
372             boundary->edit_info[ni.index].num_propagation_steps =
373                 boundary->edit_info[from_v].num_propagation_steps;
374           }
375           else {
376             boundary->edit_info[ni.index].num_propagation_steps =
377                 boundary->edit_info[from_v].num_propagation_steps + 1;
378 
379             BLI_gsqueue_push(next_iteration, &ni.index);
380 
381             /* When copying the data to the neighbor for the next iteration, it has to be copied to
382              * all its duplicates too. This is because it is not possible to know if the updated
383              * neighbor or one if its uninitialized duplicates is going to come first in order to
384              * copy the data in the from_v neighbor iterator. */
385             if (has_duplicates) {
386               SculptVertexNeighborIter ni_duplis;
387               SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.index, ni_duplis) {
388                 if (ni_duplis.is_duplicate) {
389                   boundary->edit_info[ni_duplis.index].original_vertex =
390                       boundary->edit_info[from_v].original_vertex;
391                   boundary->edit_info[ni_duplis.index].num_propagation_steps =
392                       boundary->edit_info[from_v].num_propagation_steps + 1;
393                 }
394               }
395               SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis);
396             }
397 
398             /* Check the distance using the vertex that was propagated from the initial vertex that
399              * was used to initialize the boundary. */
400             if (boundary->edit_info[from_v].original_vertex == initial_vertex) {
401               boundary->pivot_vertex = ni.index;
402               copy_v3_v3(boundary->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.index));
403               accum_distance += len_v3v3(SCULPT_vertex_co_get(ss, from_v),
404                                          SCULPT_vertex_co_get(ss, ni.index));
405             }
406           }
407         }
408       }
409       SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
410     }
411 
412     /* Copy the new vertices to the queue to be processed in the next iteration. */
413     while (!BLI_gsqueue_is_empty(next_iteration)) {
414       int next_v;
415       BLI_gsqueue_pop(next_iteration, &next_v);
416       BLI_gsqueue_push(current_iteration, &next_v);
417     }
418 
419     num_propagation_steps++;
420   }
421 
422   MEM_SAFE_FREE(visited_vertices);
423 
424   BLI_gsqueue_free(current_iteration);
425   BLI_gsqueue_free(next_iteration);
426 }
427 
428 /* This functions assigns a falloff factor to each one of the SculptBoundaryEditInfo structs based
429  * on the brush curve and its propagation steps. The falloff goes from the boundary into the mesh.
430  */
sculpt_boundary_falloff_factor_init(SculptSession * ss,SculptBoundary * boundary,Brush * brush,const float radius)431 static void sculpt_boundary_falloff_factor_init(SculptSession *ss,
432                                                 SculptBoundary *boundary,
433                                                 Brush *brush,
434                                                 const float radius)
435 {
436   const int totvert = SCULPT_vertex_count_get(ss);
437   BKE_curvemapping_init(brush->curve);
438 
439   for (int i = 0; i < totvert; i++) {
440     if (boundary->edit_info[i].num_propagation_steps != -1) {
441       boundary->edit_info[i].strength_factor = BKE_brush_curve_strength(
442           brush, boundary->edit_info[i].num_propagation_steps, boundary->max_propagation_steps);
443     }
444 
445     if (boundary->edit_info[i].original_vertex == boundary->initial_vertex) {
446       /* All vertices that are propagated from the original vertex won't be affected by the
447        * boundary falloff, so there is no need to calculate anything else. */
448       continue;
449     }
450 
451     if (!boundary->distance) {
452       /* There are falloff modes that do not require to modify the previously calculated falloff
453        * based on boundary distances. */
454       continue;
455     }
456 
457     const float boundary_distance = boundary->distance[boundary->edit_info[i].original_vertex];
458     float falloff_distance = 0.0f;
459     float direction = 1.0f;
460 
461     switch (brush->boundary_falloff_type) {
462       case BRUSH_BOUNDARY_FALLOFF_RADIUS:
463         falloff_distance = boundary_distance;
464         break;
465       case BRUSH_BOUNDARY_FALLOFF_LOOP: {
466         const int div = boundary_distance / radius;
467         const float mod = fmodf(boundary_distance, radius);
468         falloff_distance = div % 2 == 0 ? mod : radius - mod;
469       } break;
470       case BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT: {
471         const int div = boundary_distance / radius;
472         const float mod = fmodf(boundary_distance, radius);
473         falloff_distance = div % 2 == 0 ? mod : radius - mod;
474         /* Inverts the faloff in the intervals 1 2 5 6 9 10 ... */
475         if (((div - 1) & 2) == 0) {
476           direction = -1.0f;
477         }
478       } break;
479       case BRUSH_BOUNDARY_FALLOFF_CONSTANT:
480         /* For constant falloff distances are not allocated, so this should never happen. */
481         BLI_assert(false);
482     }
483 
484     boundary->edit_info[i].strength_factor *= direction * BKE_brush_curve_strength(
485                                                               brush, falloff_distance, radius);
486   }
487 }
488 
489 /* Main function to get SculptBoundary data both for brush deformation and viewport preview. Can
490  * return NULL if there is no boundary from the given vertex using the given radius. */
SCULPT_boundary_data_init(Object * object,Brush * brush,const int initial_vertex,const float radius)491 SculptBoundary *SCULPT_boundary_data_init(Object *object,
492                                           Brush *brush,
493                                           const int initial_vertex,
494                                           const float radius)
495 {
496   SculptSession *ss = object->sculpt;
497 
498   if (initial_vertex == BOUNDARY_VERTEX_NONE) {
499     return NULL;
500   }
501 
502   SCULPT_vertex_random_access_ensure(ss);
503   SCULPT_boundary_info_ensure(object);
504 
505   const int boundary_initial_vertex = sculpt_boundary_get_closest_boundary_vertex(
506       ss, initial_vertex, radius);
507 
508   if (boundary_initial_vertex == BOUNDARY_VERTEX_NONE) {
509     return NULL;
510   }
511 
512   /* Starting from a vertex that is the limit of a boundary is ambiguous, so return NULL instead of
513    * forcing a random active boundary from a corner. */
514   if (!sculpt_boundary_is_vertex_in_editable_boundary(ss, initial_vertex)) {
515     return NULL;
516   }
517 
518   SculptBoundary *boundary = MEM_callocN(sizeof(SculptBoundary), "Boundary edit data");
519 
520   const bool init_boundary_distances = brush->boundary_falloff_type !=
521                                        BRUSH_BOUNDARY_FALLOFF_CONSTANT;
522   sculpt_boundary_indices_init(ss, boundary, init_boundary_distances, boundary_initial_vertex);
523 
524   const float boundary_radius = radius * (1.0f + brush->boundary_offset);
525   sculpt_boundary_edit_data_init(ss, boundary, boundary_initial_vertex, boundary_radius);
526 
527   return boundary;
528 }
529 
SCULPT_boundary_data_free(SculptBoundary * boundary)530 void SCULPT_boundary_data_free(SculptBoundary *boundary)
531 {
532   MEM_SAFE_FREE(boundary->vertices);
533   MEM_SAFE_FREE(boundary->distance);
534   MEM_SAFE_FREE(boundary->edit_info);
535   MEM_SAFE_FREE(boundary->bend.pivot_positions);
536   MEM_SAFE_FREE(boundary->bend.pivot_rotation_axis);
537   MEM_SAFE_FREE(boundary->slide.directions);
538   MEM_SAFE_FREE(boundary);
539 }
540 
541 /* These functions initialize the required vectors for the desired deformation using the
542  * SculptBoundaryEditInfo. They calculate the data using the vertices that have the
543  * max_propagation_steps value and them this data is copied to the rest of the vertices using the
544  * original vertex index. */
sculpt_boundary_bend_data_init(SculptSession * ss,SculptBoundary * boundary)545 static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *boundary)
546 {
547   const int totvert = SCULPT_vertex_count_get(ss);
548   boundary->bend.pivot_rotation_axis = MEM_calloc_arrayN(
549       totvert, 3 * sizeof(float), "pivot rotation axis");
550   boundary->bend.pivot_positions = MEM_calloc_arrayN(
551       totvert, 3 * sizeof(float), "pivot positions");
552 
553   for (int i = 0; i < totvert; i++) {
554     if (boundary->edit_info[i].num_propagation_steps == boundary->max_propagation_steps) {
555       float dir[3];
556       float normal[3];
557       SCULPT_vertex_normal_get(ss, i, normal);
558       sub_v3_v3v3(dir,
559                   SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex),
560                   SCULPT_vertex_co_get(ss, i));
561       cross_v3_v3v3(
562           boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex], dir, normal);
563       normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]);
564       copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex],
565                  SCULPT_vertex_co_get(ss, i));
566     }
567   }
568 
569   for (int i = 0; i < totvert; i++) {
570     if (boundary->edit_info[i].num_propagation_steps != BOUNDARY_STEPS_NONE) {
571       copy_v3_v3(boundary->bend.pivot_positions[i],
572                  boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex]);
573       copy_v3_v3(boundary->bend.pivot_rotation_axis[i],
574                  boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]);
575     }
576   }
577 }
578 
sculpt_boundary_slide_data_init(SculptSession * ss,SculptBoundary * boundary)579 static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *boundary)
580 {
581   const int totvert = SCULPT_vertex_count_get(ss);
582   boundary->slide.directions = MEM_calloc_arrayN(totvert, 3 * sizeof(float), "slide directions");
583 
584   for (int i = 0; i < totvert; i++) {
585     if (boundary->edit_info[i].num_propagation_steps == boundary->max_propagation_steps) {
586       sub_v3_v3v3(boundary->slide.directions[boundary->edit_info[i].original_vertex],
587                   SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex),
588                   SCULPT_vertex_co_get(ss, i));
589       normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex]);
590     }
591   }
592 
593   for (int i = 0; i < totvert; i++) {
594     if (boundary->edit_info[i].num_propagation_steps != BOUNDARY_STEPS_NONE) {
595       copy_v3_v3(boundary->slide.directions[i],
596                  boundary->slide.directions[boundary->edit_info[i].original_vertex]);
597     }
598   }
599 }
600 
sculpt_boundary_twist_data_init(SculptSession * ss,SculptBoundary * boundary)601 static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *boundary)
602 {
603   zero_v3(boundary->twist.pivot_position);
604   float(*poly_verts)[3] = MEM_malloc_arrayN(
605       boundary->num_vertices, sizeof(float) * 3, "poly verts");
606   for (int i = 0; i < boundary->num_vertices; i++) {
607     add_v3_v3(boundary->twist.pivot_position, SCULPT_vertex_co_get(ss, boundary->vertices[i]));
608     copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, boundary->vertices[i]));
609   }
610   mul_v3_fl(boundary->twist.pivot_position, 1.0f / boundary->num_vertices);
611   if (boundary->forms_loop) {
612     normal_poly_v3(boundary->twist.rotation_axis, poly_verts, boundary->num_vertices);
613   }
614   else {
615     sub_v3_v3v3(boundary->twist.rotation_axis,
616                 SCULPT_vertex_co_get(ss, boundary->pivot_vertex),
617                 SCULPT_vertex_co_get(ss, boundary->initial_vertex));
618     normalize_v3(boundary->twist.rotation_axis);
619   }
620   MEM_freeN(poly_verts);
621 }
622 
sculpt_boundary_displacement_from_grab_delta_get(SculptSession * ss,SculptBoundary * boundary)623 static float sculpt_boundary_displacement_from_grab_delta_get(SculptSession *ss,
624                                                               SculptBoundary *boundary)
625 {
626   float plane[4];
627   float pos[3];
628   float normal[3];
629   sub_v3_v3v3(normal, ss->cache->initial_location, boundary->initial_pivot_position);
630   normalize_v3(normal);
631   plane_from_point_normal_v3(plane, ss->cache->initial_location, normal);
632   add_v3_v3v3(pos, ss->cache->initial_location, ss->cache->grab_delta_symmetry);
633   return dist_signed_to_plane_v3(pos, plane);
634 }
635 
636 /* Deformation tasks callbacks. */
do_boundary_brush_bend_task_cb_ex(void * __restrict userdata,const int n,const TaskParallelTLS * __restrict UNUSED (tls))637 static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
638                                               const int n,
639                                               const TaskParallelTLS *__restrict UNUSED(tls))
640 {
641   SculptThreadedTaskData *data = userdata;
642   SculptSession *ss = data->ob->sculpt;
643   const int symm_area = ss->cache->mirror_symmetry_pass;
644   SculptBoundary *boundary = ss->cache->boundaries[symm_area];
645   const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob);
646   const Brush *brush = data->brush;
647 
648   const float strength = ss->cache->bstrength;
649 
650   PBVHVertexIter vd;
651   SculptOrigVertData orig_data;
652   SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
653 
654   const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
655   float angle_factor = disp / ss->cache->radius;
656   /* Angle Snapping when inverting the brush. */
657   if (ss->cache->invert) {
658     angle_factor = floorf(angle_factor * 10) / 10.0f;
659   }
660   const float angle = angle_factor * M_PI;
661 
662   BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
663   {
664 
665     if (boundary->edit_info[vd.index].num_propagation_steps != -1) {
666       SCULPT_orig_vert_data_update(&orig_data, &vd);
667       if (SCULPT_check_vertex_pivot_symmetry(
668               orig_data.co, boundary->initial_vertex_position, symm)) {
669         const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
670         float t_orig_co[3];
671         float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
672         sub_v3_v3v3(t_orig_co, orig_data.co, boundary->bend.pivot_positions[vd.index]);
673         rotate_v3_v3v3fl(target_co,
674                          t_orig_co,
675                          boundary->bend.pivot_rotation_axis[vd.index],
676                          angle * boundary->edit_info[vd.index].strength_factor * mask);
677         add_v3_v3(target_co, boundary->bend.pivot_positions[vd.index]);
678       }
679     }
680 
681     if (vd.mvert) {
682       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
683     }
684   }
685   BKE_pbvh_vertex_iter_end;
686 }
687 
do_boundary_brush_slide_task_cb_ex(void * __restrict userdata,const int n,const TaskParallelTLS * __restrict UNUSED (tls))688 static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata,
689                                                const int n,
690                                                const TaskParallelTLS *__restrict UNUSED(tls))
691 {
692   SculptThreadedTaskData *data = userdata;
693   SculptSession *ss = data->ob->sculpt;
694   const int symm_area = ss->cache->mirror_symmetry_pass;
695   SculptBoundary *boundary = ss->cache->boundaries[symm_area];
696   const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob);
697   const Brush *brush = data->brush;
698 
699   const float strength = ss->cache->bstrength;
700 
701   PBVHVertexIter vd;
702   SculptOrigVertData orig_data;
703   SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
704 
705   const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
706 
707   BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
708   {
709 
710     if (boundary->edit_info[vd.index].num_propagation_steps != -1) {
711       SCULPT_orig_vert_data_update(&orig_data, &vd);
712       if (SCULPT_check_vertex_pivot_symmetry(
713               orig_data.co, boundary->initial_vertex_position, symm)) {
714         const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
715         float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
716         madd_v3_v3v3fl(target_co,
717                        orig_data.co,
718                        boundary->slide.directions[vd.index],
719                        boundary->edit_info[vd.index].strength_factor * disp * mask * strength);
720       }
721     }
722 
723     if (vd.mvert) {
724       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
725     }
726   }
727   BKE_pbvh_vertex_iter_end;
728 }
729 
do_boundary_brush_inflate_task_cb_ex(void * __restrict userdata,const int n,const TaskParallelTLS * __restrict UNUSED (tls))730 static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata,
731                                                  const int n,
732                                                  const TaskParallelTLS *__restrict UNUSED(tls))
733 {
734   SculptThreadedTaskData *data = userdata;
735   SculptSession *ss = data->ob->sculpt;
736   const int symm_area = ss->cache->mirror_symmetry_pass;
737   SculptBoundary *boundary = ss->cache->boundaries[symm_area];
738   const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob);
739   const Brush *brush = data->brush;
740 
741   const float strength = ss->cache->bstrength;
742 
743   PBVHVertexIter vd;
744   SculptOrigVertData orig_data;
745   SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
746 
747   const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
748 
749   BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
750   {
751 
752     if (boundary->edit_info[vd.index].num_propagation_steps != -1) {
753       SCULPT_orig_vert_data_update(&orig_data, &vd);
754       if (SCULPT_check_vertex_pivot_symmetry(
755               orig_data.co, boundary->initial_vertex_position, symm)) {
756         const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
757         float normal[3];
758         normal_short_to_float_v3(normal, orig_data.no);
759         float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
760         madd_v3_v3v3fl(target_co,
761                        orig_data.co,
762                        normal,
763                        boundary->edit_info[vd.index].strength_factor * disp * mask * strength);
764       }
765     }
766 
767     if (vd.mvert) {
768       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
769     }
770   }
771   BKE_pbvh_vertex_iter_end;
772 }
773 
do_boundary_brush_grab_task_cb_ex(void * __restrict userdata,const int n,const TaskParallelTLS * __restrict UNUSED (tls))774 static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata,
775                                               const int n,
776                                               const TaskParallelTLS *__restrict UNUSED(tls))
777 {
778   SculptThreadedTaskData *data = userdata;
779   SculptSession *ss = data->ob->sculpt;
780   const int symm_area = ss->cache->mirror_symmetry_pass;
781   SculptBoundary *boundary = ss->cache->boundaries[symm_area];
782   const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob);
783   const Brush *brush = data->brush;
784 
785   const float strength = ss->cache->bstrength;
786 
787   PBVHVertexIter vd;
788   SculptOrigVertData orig_data;
789   SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
790 
791   BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
792   {
793 
794     if (boundary->edit_info[vd.index].num_propagation_steps != -1) {
795       SCULPT_orig_vert_data_update(&orig_data, &vd);
796       if (SCULPT_check_vertex_pivot_symmetry(
797               orig_data.co, boundary->initial_vertex_position, symm)) {
798         const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
799         float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
800         madd_v3_v3v3fl(target_co,
801                        orig_data.co,
802                        ss->cache->grab_delta_symmetry,
803                        boundary->edit_info[vd.index].strength_factor * mask * strength);
804       }
805     }
806 
807     if (vd.mvert) {
808       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
809     }
810   }
811   BKE_pbvh_vertex_iter_end;
812 }
813 
do_boundary_brush_twist_task_cb_ex(void * __restrict userdata,const int n,const TaskParallelTLS * __restrict UNUSED (tls))814 static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
815                                                const int n,
816                                                const TaskParallelTLS *__restrict UNUSED(tls))
817 {
818   SculptThreadedTaskData *data = userdata;
819   SculptSession *ss = data->ob->sculpt;
820   const int symm_area = ss->cache->mirror_symmetry_pass;
821   SculptBoundary *boundary = ss->cache->boundaries[symm_area];
822   const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob);
823   const Brush *brush = data->brush;
824 
825   const float strength = ss->cache->bstrength;
826 
827   PBVHVertexIter vd;
828   SculptOrigVertData orig_data;
829   SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
830 
831   const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
832   float angle_factor = disp / ss->cache->radius;
833   /* Angle Snapping when inverting the brush. */
834   if (ss->cache->invert) {
835     angle_factor = floorf(angle_factor * 10) / 10.0f;
836   }
837   const float angle = angle_factor * M_PI;
838 
839   BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
840   {
841 
842     if (boundary->edit_info[vd.index].num_propagation_steps != -1) {
843       SCULPT_orig_vert_data_update(&orig_data, &vd);
844       if (SCULPT_check_vertex_pivot_symmetry(
845               orig_data.co, boundary->initial_vertex_position, symm)) {
846         const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
847         float t_orig_co[3];
848         float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
849         sub_v3_v3v3(t_orig_co, orig_data.co, boundary->twist.pivot_position);
850         rotate_v3_v3v3fl(target_co,
851                          t_orig_co,
852                          boundary->twist.rotation_axis,
853                          angle * mask * boundary->edit_info[vd.index].strength_factor);
854         add_v3_v3(target_co, boundary->twist.pivot_position);
855       }
856     }
857 
858     if (vd.mvert) {
859       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
860     }
861   }
862   BKE_pbvh_vertex_iter_end;
863 }
864 
do_boundary_brush_smooth_task_cb_ex(void * __restrict userdata,const int n,const TaskParallelTLS * __restrict UNUSED (tls))865 static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata,
866                                                 const int n,
867                                                 const TaskParallelTLS *__restrict UNUSED(tls))
868 {
869   SculptThreadedTaskData *data = userdata;
870   SculptSession *ss = data->ob->sculpt;
871   const int symmetry_pass = ss->cache->mirror_symmetry_pass;
872   const SculptBoundary *boundary = ss->cache->boundaries[symmetry_pass];
873   const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob);
874   const Brush *brush = data->brush;
875 
876   const float strength = ss->cache->bstrength;
877 
878   PBVHVertexIter vd;
879   SculptOrigVertData orig_data;
880   SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
881 
882   BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
883   {
884     if (boundary->edit_info[vd.index].num_propagation_steps == -1) {
885       continue;
886     }
887 
888     SCULPT_orig_vert_data_update(&orig_data, &vd);
889     if (!SCULPT_check_vertex_pivot_symmetry(
890             orig_data.co, boundary->initial_vertex_position, symm)) {
891       continue;
892     }
893 
894     float coord_accum[3] = {0.0f, 0.0f, 0.0f};
895     int total_neighbors = 0;
896     const int current_propagation_steps = boundary->edit_info[vd.index].num_propagation_steps;
897     SculptVertexNeighborIter ni;
898     SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
899       if (current_propagation_steps == boundary->edit_info[ni.index].num_propagation_steps) {
900         add_v3_v3(coord_accum, SCULPT_vertex_co_get(ss, ni.index));
901         total_neighbors++;
902       }
903     }
904     SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
905 
906     if (total_neighbors == 0) {
907       continue;
908     }
909     float disp[3];
910     float avg[3];
911     const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
912     mul_v3_v3fl(avg, coord_accum, 1.0f / total_neighbors);
913     sub_v3_v3v3(disp, avg, vd.co);
914     float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
915     madd_v3_v3v3fl(
916         target_co, vd.co, disp, boundary->edit_info[vd.index].strength_factor * mask * strength);
917 
918     if (vd.mvert) {
919       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
920     }
921   }
922   BKE_pbvh_vertex_iter_end;
923 }
924 
925 /* Main Brush Function. */
SCULPT_do_boundary_brush(Sculpt * sd,Object * ob,PBVHNode ** nodes,int totnode)926 void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
927 {
928   SculptSession *ss = ob->sculpt;
929   Brush *brush = BKE_paint_brush(&sd->paint);
930 
931   const int symm_area = ss->cache->mirror_symmetry_pass;
932   if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
933 
934     int initial_vertex;
935     if (ss->cache->mirror_symmetry_pass == 0) {
936       initial_vertex = SCULPT_active_vertex_get(ss);
937     }
938     else {
939       float location[3];
940       flip_v3_v3(location, SCULPT_active_vertex_co_get(ss), symm_area);
941       initial_vertex = SCULPT_nearest_vertex_get(
942           sd, ob, location, ss->cache->radius_squared, false);
943     }
944 
945     ss->cache->boundaries[symm_area] = SCULPT_boundary_data_init(
946         ob, brush, initial_vertex, ss->cache->initial_radius);
947 
948     if (ss->cache->boundaries[symm_area]) {
949 
950       switch (brush->boundary_deform_type) {
951         case BRUSH_BOUNDARY_DEFORM_BEND:
952           sculpt_boundary_bend_data_init(ss, ss->cache->boundaries[symm_area]);
953           break;
954         case BRUSH_BOUNDARY_DEFORM_EXPAND:
955           sculpt_boundary_slide_data_init(ss, ss->cache->boundaries[symm_area]);
956           break;
957         case BRUSH_BOUNDARY_DEFORM_TWIST:
958           sculpt_boundary_twist_data_init(ss, ss->cache->boundaries[symm_area]);
959           break;
960         case BRUSH_BOUNDARY_DEFORM_INFLATE:
961         case BRUSH_BOUNDARY_DEFORM_GRAB:
962           /* Do nothing. These deform modes don't need any extra data to be precomputed. */
963           break;
964       }
965 
966       sculpt_boundary_falloff_factor_init(
967           ss, ss->cache->boundaries[symm_area], brush, ss->cache->initial_radius);
968     }
969   }
970 
971   /* No active boundary under the cursor. */
972   if (!ss->cache->boundaries[symm_area]) {
973     return;
974   }
975 
976   SculptThreadedTaskData data = {
977       .sd = sd,
978       .ob = ob,
979       .brush = brush,
980       .nodes = nodes,
981   };
982 
983   TaskParallelSettings settings;
984   BKE_pbvh_parallel_range_settings(&settings, true, totnode);
985 
986   switch (brush->boundary_deform_type) {
987     case BRUSH_BOUNDARY_DEFORM_BEND:
988       BLI_task_parallel_range(0, totnode, &data, do_boundary_brush_bend_task_cb_ex, &settings);
989       break;
990     case BRUSH_BOUNDARY_DEFORM_EXPAND:
991       BLI_task_parallel_range(0, totnode, &data, do_boundary_brush_slide_task_cb_ex, &settings);
992       break;
993     case BRUSH_BOUNDARY_DEFORM_INFLATE:
994       BLI_task_parallel_range(0, totnode, &data, do_boundary_brush_inflate_task_cb_ex, &settings);
995       break;
996     case BRUSH_BOUNDARY_DEFORM_GRAB:
997       BLI_task_parallel_range(0, totnode, &data, do_boundary_brush_grab_task_cb_ex, &settings);
998       break;
999     case BRUSH_BOUNDARY_DEFORM_TWIST:
1000       BLI_task_parallel_range(0, totnode, &data, do_boundary_brush_twist_task_cb_ex, &settings);
1001       break;
1002     case BRUSH_BOUNDARY_DEFORM_SMOOTH:
1003       BLI_task_parallel_range(0, totnode, &data, do_boundary_brush_smooth_task_cb_ex, &settings);
1004       break;
1005   }
1006 }
1007 
SCULPT_boundary_edges_preview_draw(const uint gpuattr,SculptSession * ss,const float outline_col[3],const float outline_alpha)1008 void SCULPT_boundary_edges_preview_draw(const uint gpuattr,
1009                                         SculptSession *ss,
1010                                         const float outline_col[3],
1011                                         const float outline_alpha)
1012 {
1013   if (!ss->boundary_preview) {
1014     return;
1015   }
1016   immUniformColor3fvAlpha(outline_col, outline_alpha);
1017   GPU_line_width(2.0f);
1018   immBegin(GPU_PRIM_LINES, ss->boundary_preview->num_edges * 2);
1019   for (int i = 0; i < ss->boundary_preview->num_edges; i++) {
1020     immVertex3fv(gpuattr, SCULPT_vertex_co_get(ss, ss->boundary_preview->edges[i].v1));
1021     immVertex3fv(gpuattr, SCULPT_vertex_co_get(ss, ss->boundary_preview->edges[i].v2));
1022   }
1023   immEnd();
1024 }
1025 
SCULPT_boundary_pivot_line_preview_draw(const uint gpuattr,SculptSession * ss)1026 void SCULPT_boundary_pivot_line_preview_draw(const uint gpuattr, SculptSession *ss)
1027 {
1028   if (!ss->boundary_preview) {
1029     return;
1030   }
1031   immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f);
1032   GPU_line_width(2.0f);
1033   immBegin(GPU_PRIM_LINES, 2);
1034   immVertex3fv(gpuattr, SCULPT_vertex_co_get(ss, ss->boundary_preview->pivot_vertex));
1035   immVertex3fv(gpuattr, SCULPT_vertex_co_get(ss, ss->boundary_preview->initial_vertex));
1036   immEnd();
1037 }
1038