Blender  V3.3
sculpt_uv.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2002-2009 Blender Foundation. All rights reserved. */
3 
9 #include "MEM_guardedalloc.h"
10 
11 #include "BLI_ghash.h"
12 #include "BLI_math.h"
13 #include "BLI_utildefines.h"
14 
15 #include "DNA_brush_types.h"
16 #include "DNA_meshdata_types.h"
17 #include "DNA_object_types.h"
18 #include "DNA_scene_types.h"
19 
20 #include "BKE_brush.h"
21 #include "BKE_colortools.h"
22 #include "BKE_context.h"
23 #include "BKE_customdata.h"
24 #include "BKE_editmesh.h"
25 #include "BKE_mesh_mapping.h"
26 #include "BKE_paint.h"
27 
28 #include "DEG_depsgraph.h"
29 
30 #include "ED_image.h"
31 #include "ED_mesh.h"
32 #include "ED_screen.h"
33 
34 #include "WM_api.h"
35 #include "WM_types.h"
36 
37 #include "RNA_access.h"
38 #include "RNA_define.h"
39 
40 #include "paint_intern.h"
41 #include "uvedit_intern.h"
42 
43 #include "UI_view2d.h"
44 
45 #define MARK_BOUNDARY 1
46 
47 typedef struct UvAdjacencyElement {
48  /* pointer to original uvelement */
50  /* uv pointer for convenience. Caution, this points to the original UVs! */
51  float *uv;
52  /* general use flag (Used to check if Element is boundary here) */
53  char flag;
55 
56 typedef struct UvEdge {
59  /* general use flag
60  * (Used to check if edge is boundary here, and propagates to adjacency elements) */
61  char flag;
63 
64 typedef struct UVInitialStrokeElement {
65  /* index to unique uv */
66  int uv;
67 
68  /* strength of brush on initial position */
69  float strength;
70 
71  /* initial uv position */
72  float initial_uv[2];
74 
75 typedef struct UVInitialStroke {
76  /* Initial Selection,for grab brushes for instance */
78 
79  /* Total initially selected UV's. */
81 
82  /* initial mouse coordinates */
83  float init_coord[2];
85 
86 /* custom data for uv smoothing brush */
87 typedef struct UvSculptData {
88  /* Contains the first of each set of coincident UV's.
89  * These will be used to perform smoothing on and propagate the changes
90  * to their coincident UV's */
92 
93  /* ...Is what it says */
95 
96  /* Edges used for adjacency info, used with laplacian smoothing */
98 
99  /* need I say more? */
101 
102  /* data for initial stroke, used by tools like grab */
104 
105  /* timer to be used for airbrush-type brush */
107 
108  /* to determine quickly adjacent UV's */
110 
111  /* uvsmooth Paint for fast reference */
113 
114  /* tool to use. duplicating here to change if modifier keys are pressed */
115  char tool;
116 
117  /* store invert flag here */
118  char invert;
120 
121 /*********** Improved Laplacian Relaxation Operator ************************/
122 /* original code by Raul Fernandez Hernandez "farsthary" *
123  * adapted to uv smoothing by Antony Riakiatakis *
124  ***************************************************************************/
125 
126 typedef struct Temp_UvData {
127  float sum_co[2], p[2], b[2], sum_b[2];
128  int ncounter;
130 
132  UvSculptData *sculptdata,
133  const float mouse_coord[2],
134  float alpha,
135  float radius,
136  float aspectRatio)
137 {
138  Temp_UVData *tmp_uvdata;
139  float diff[2];
140  int i;
141  float radius_root = sqrtf(radius);
142  Brush *brush = BKE_paint_brush(sculptdata->uvsculpt);
143 
144  tmp_uvdata = (Temp_UVData *)MEM_callocN(sculptdata->totalUniqueUvs * sizeof(Temp_UVData),
145  "Temporal data");
146 
147  /* counting neighbors */
148  for (i = 0; i < sculptdata->totalUvEdges; i++) {
149  UvEdge *tmpedge = sculptdata->uvedges + i;
150  tmp_uvdata[tmpedge->uv1].ncounter++;
151  tmp_uvdata[tmpedge->uv2].ncounter++;
152 
153  add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv);
154  add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv);
155  }
156 
157  for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
158  copy_v2_v2(diff, tmp_uvdata[i].sum_co);
159  mul_v2_fl(diff, 1.0f / tmp_uvdata[i].ncounter);
160  copy_v2_v2(tmp_uvdata[i].p, diff);
161 
162  tmp_uvdata[i].b[0] = diff[0] - sculptdata->uv[i].uv[0];
163  tmp_uvdata[i].b[1] = diff[1] - sculptdata->uv[i].uv[1];
164  }
165 
166  for (i = 0; i < sculptdata->totalUvEdges; i++) {
167  UvEdge *tmpedge = sculptdata->uvedges + i;
168  add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_b, tmp_uvdata[tmpedge->uv2].b);
169  add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_b, tmp_uvdata[tmpedge->uv1].b);
170  }
171 
172  for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
173  float dist;
174  /* This is supposed to happen only if "Pin Edges" is on,
175  * since we have initialization on stroke start.
176  * If ever uv brushes get their own mode we should check for toolsettings option too. */
177  if (sculptdata->uv[i].flag & MARK_BOUNDARY) {
178  continue;
179  }
180 
181  sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord);
182  diff[1] /= aspectRatio;
183  if ((dist = dot_v2v2(diff, diff)) <= radius) {
185  float strength;
186  strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root);
187 
188  sculptdata->uv[i].uv[0] = (1.0f - strength) * sculptdata->uv[i].uv[0] +
189  strength *
190  (tmp_uvdata[i].p[0] -
191  0.5f * (tmp_uvdata[i].b[0] +
192  tmp_uvdata[i].sum_b[0] / tmp_uvdata[i].ncounter));
193  sculptdata->uv[i].uv[1] = (1.0f - strength) * sculptdata->uv[i].uv[1] +
194  strength *
195  (tmp_uvdata[i].p[1] -
196  0.5f * (tmp_uvdata[i].b[1] +
197  tmp_uvdata[i].sum_b[1] / tmp_uvdata[i].ncounter));
198 
199  for (element = sculptdata->uv[i].element; element; element = element->next) {
200  MLoopUV *luv;
201  BMLoop *l;
202 
203  if (element->separate && element != sculptdata->uv[i].element) {
204  break;
205  }
206 
207  l = element->l;
209  copy_v2_v2(luv->uv, sculptdata->uv[i].uv);
210  }
211  }
212  }
213 
214  MEM_SAFE_FREE(tmp_uvdata);
215 }
216 
218  UvSculptData *sculptdata,
219  const float mouse_coord[2],
220  float alpha,
221  float radius,
222  float aspectRatio)
223 {
224  Temp_UVData *tmp_uvdata;
225  float diff[2];
226  int i;
227  float radius_root = sqrtf(radius);
228  Brush *brush = BKE_paint_brush(sculptdata->uvsculpt);
229 
230  tmp_uvdata = (Temp_UVData *)MEM_callocN(sculptdata->totalUniqueUvs * sizeof(Temp_UVData),
231  "Temporal data");
232 
233  /* counting neighbors */
234  for (i = 0; i < sculptdata->totalUvEdges; i++) {
235  UvEdge *tmpedge = sculptdata->uvedges + i;
236  tmp_uvdata[tmpedge->uv1].ncounter++;
237  tmp_uvdata[tmpedge->uv2].ncounter++;
238 
239  add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv);
240  add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv);
241  }
242 
243  /* Original Laplacian algorithm included removal of normal component of translation.
244  * here it is not needed since we translate along the UV plane always. */
245  for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
246  copy_v2_v2(tmp_uvdata[i].p, tmp_uvdata[i].sum_co);
247  mul_v2_fl(tmp_uvdata[i].p, 1.0f / tmp_uvdata[i].ncounter);
248  }
249 
250  for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
251  float dist;
252  /* This is supposed to happen only if "Pin Edges" is on,
253  * since we have initialization on stroke start.
254  * If ever uv brushes get their own mode we should check for toolsettings option too. */
255  if (sculptdata->uv[i].flag & MARK_BOUNDARY) {
256  continue;
257  }
258 
259  sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord);
260  diff[1] /= aspectRatio;
261  if ((dist = dot_v2v2(diff, diff)) <= radius) {
263  float strength;
264  strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root);
265 
266  sculptdata->uv[i].uv[0] = (1.0f - strength) * sculptdata->uv[i].uv[0] +
267  strength * tmp_uvdata[i].p[0];
268  sculptdata->uv[i].uv[1] = (1.0f - strength) * sculptdata->uv[i].uv[1] +
269  strength * tmp_uvdata[i].p[1];
270 
271  for (element = sculptdata->uv[i].element; element; element = element->next) {
272  MLoopUV *luv;
273  BMLoop *l;
274 
275  if (element->separate && element != sculptdata->uv[i].element) {
276  break;
277  }
278 
279  l = element->l;
281  copy_v2_v2(luv->uv, sculptdata->uv[i].uv);
282  }
283  }
284  }
285 
286  MEM_SAFE_FREE(tmp_uvdata);
287 }
288 
290  wmOperator *op,
291  const wmEvent *event,
292  Object *obedit)
293 {
294  float co[2], radius, radius_root;
296  ARegion *region = CTX_wm_region(C);
297  BMEditMesh *em = BKE_editmesh_from_object(obedit);
298  uint tool;
299  UvSculptData *sculptdata = (UvSculptData *)op->customdata;
300  SpaceImage *sima;
301  int invert;
302  int width, height;
303  float aspectRatio;
304  float alpha, zoomx, zoomy;
305  Brush *brush = BKE_paint_brush(sculptdata->uvsculpt);
306  ToolSettings *toolsettings = CTX_data_tool_settings(C);
307  tool = sculptdata->tool;
308  invert = sculptdata->invert ? -1 : 1;
309  alpha = BKE_brush_alpha_get(scene, brush);
310  UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
311 
312  sima = CTX_wm_space_image(C);
314  ED_space_image_get_zoom(sima, region, &zoomx, &zoomy);
315 
316  radius = BKE_brush_size_get(scene, brush) / (width * zoomx);
317  aspectRatio = width / (float)height;
318 
319  /* We will compare squares to save some computation */
320  radius = radius * radius;
321  radius_root = sqrtf(radius);
322 
323  /*
324  * Pinch Tool
325  */
326  if (tool == UV_SCULPT_TOOL_PINCH) {
327  int i;
328  alpha *= invert;
329  for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
330  float dist, diff[2];
331  /* This is supposed to happen only if "Lock Borders" is on,
332  * since we have initialization on stroke start.
333  * If ever uv brushes get their own mode we should check for toolsettings option too. */
334  if (sculptdata->uv[i].flag & MARK_BOUNDARY) {
335  continue;
336  }
337 
338  sub_v2_v2v2(diff, sculptdata->uv[i].uv, co);
339  diff[1] /= aspectRatio;
340  if ((dist = dot_v2v2(diff, diff)) <= radius) {
342  float strength;
343  strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root);
345 
346  sculptdata->uv[i].uv[0] -= strength * diff[0] * 0.001f;
347  sculptdata->uv[i].uv[1] -= strength * diff[1] * 0.001f;
348 
349  for (element = sculptdata->uv[i].element; element; element = element->next) {
350  MLoopUV *luv;
351  BMLoop *l;
352 
353  if (element->separate && element != sculptdata->uv[i].element) {
354  break;
355  }
356 
357  l = element->l;
359  copy_v2_v2(luv->uv, sculptdata->uv[i].uv);
360  }
361  }
362  }
363  }
364 
365  /*
366  * Smooth Tool
367  */
368  else if (tool == UV_SCULPT_TOOL_RELAX) {
369  uint method = toolsettings->uv_relax_method;
370  if (method == UV_SCULPT_TOOL_RELAX_HC) {
371  HC_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio);
372  }
373  else {
374  laplacian_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio);
375  }
376  }
377 
378  /*
379  * Grab Tool
380  */
381  else if (tool == UV_SCULPT_TOOL_GRAB) {
382  int i;
383  float diff[2];
384  sub_v2_v2v2(diff, co, sculptdata->initial_stroke->init_coord);
385 
386  for (i = 0; i < sculptdata->initial_stroke->totalInitialSelected; i++) {
388  int uvindex = sculptdata->initial_stroke->initialSelection[i].uv;
389  float strength = sculptdata->initial_stroke->initialSelection[i].strength;
390  sculptdata->uv[uvindex].uv[0] =
391  sculptdata->initial_stroke->initialSelection[i].initial_uv[0] + strength * diff[0];
392  sculptdata->uv[uvindex].uv[1] =
393  sculptdata->initial_stroke->initialSelection[i].initial_uv[1] + strength * diff[1];
394 
395  for (element = sculptdata->uv[uvindex].element; element; element = element->next) {
396  MLoopUV *luv;
397  BMLoop *l;
398 
399  if (element->separate && element != sculptdata->uv[uvindex].element) {
400  break;
401  }
402 
403  l = element->l;
405  copy_v2_v2(luv->uv, sculptdata->uv[uvindex].uv);
406  }
407  }
408  }
409 }
410 
412 {
414  if (data->timer) {
416  }
417  if (data->elementMap) {
418  BM_uv_element_map_free(data->elementMap);
419  }
420  MEM_SAFE_FREE(data->uv);
421  MEM_SAFE_FREE(data->uvedges);
422  if (data->initial_stroke) {
423  MEM_SAFE_FREE(data->initial_stroke->initialSelection);
424  MEM_SAFE_FREE(data->initial_stroke);
425  }
426 
428  op->customdata = NULL;
429 }
430 
432  UvElementMap *map, BMFace *efa, BMLoop *l, int island_index, const bool doIslands)
433 {
435  if (!element || (doIslands && element->island != island_index)) {
436  return -1;
437  }
438  return element - map->buf;
439 }
440 
441 static uint uv_edge_hash(const void *key)
442 {
443  const UvEdge *edge = key;
444  return (BLI_ghashutil_uinthash(edge->uv2) + BLI_ghashutil_uinthash(edge->uv1));
445 }
446 
447 static bool uv_edge_compare(const void *a, const void *b)
448 {
449  const UvEdge *edge1 = a;
450  const UvEdge *edge2 = b;
451 
452  if ((edge1->uv1 == edge2->uv1) && (edge1->uv2 == edge2->uv2)) {
453  return false;
454  }
455  return true;
456 }
457 
459 {
461  Object *obedit = CTX_data_edit_object(C);
463  UvSculptData *data = MEM_callocN(sizeof(*data), "UV Smooth Brush Data");
464  BMEditMesh *em = BKE_editmesh_from_object(obedit);
465  BMesh *bm = em->bm;
466 
467  op->customdata = data;
468 
470 
471  if (data) {
472  int counter = 0, i;
473  ARegion *region = CTX_wm_region(C);
474  float co[2];
475  BMFace *efa;
476  MLoopUV *luv;
477  BMLoop *l;
478  BMIter iter, liter;
479 
480  UvEdge *edges;
481  GHash *edgeHash;
482  GHashIterator gh_iter;
483 
484  bool do_island_optimization = !(ts->uv_sculpt_settings & UV_SCULPT_ALL_ISLANDS);
485  int island_index = 0;
486  /* Holds, for each UvElement in elementMap, an index of its unique UV. */
487  int *uniqueUv;
488  data->tool = (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_SMOOTH) ?
491  data->invert = (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_INVERT) ? 1 : 0;
492 
493  data->uvsculpt = &ts->uvsculpt->paint;
494 
495  if (do_island_optimization) {
496  /* We will need island information */
497  data->elementMap = BM_uv_element_map_create(bm, scene, false, true, true);
498  }
499  else {
500  data->elementMap = BM_uv_element_map_create(bm, scene, false, true, false);
501  }
502 
503  if (!data->elementMap) {
505  return NULL;
506  }
507 
508  /* Mouse coordinates, useful for some functions like grab and sculpt all islands */
509  UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
510 
511  /* we need to find the active island here */
512  if (do_island_optimization) {
514  UvNearestHit hit = UV_NEAREST_HIT_INIT_MAX(&region->v2d);
515  uv_find_nearest_vert(scene, obedit, co, 0.0f, &hit);
516 
517  element = BM_uv_element_get(data->elementMap, hit.efa, hit.l);
518  island_index = element->island;
519  }
520 
521  /* Count 'unique' UV's */
522  for (i = 0; i < data->elementMap->totalUVs; i++) {
523  if (data->elementMap->buf[i].separate &&
524  (!do_island_optimization || data->elementMap->buf[i].island == island_index)) {
525  counter++;
526  }
527  }
528 
529  /* Allocate the unique uv buffers */
530  data->uv = MEM_mallocN(sizeof(*data->uv) * counter, "uv_brush_unique_uvs");
531  uniqueUv = MEM_mallocN(sizeof(*uniqueUv) * data->elementMap->totalUVs,
532  "uv_brush_unique_uv_map");
533  edgeHash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "uv_brush_edge_hash");
534  /* we have at most totalUVs edges */
535  edges = MEM_mallocN(sizeof(*edges) * data->elementMap->totalUVs, "uv_brush_all_edges");
536  if (!data->uv || !uniqueUv || !edgeHash || !edges) {
537  MEM_SAFE_FREE(edges);
538  MEM_SAFE_FREE(uniqueUv);
539  if (edgeHash) {
540  BLI_ghash_free(edgeHash, NULL, NULL);
541  }
543  return NULL;
544  }
545 
546  data->totalUniqueUvs = counter;
547  /* So that we can use this as index for the UvElements */
548  counter = -1;
549  /* initialize the unique UVs */
550  for (i = 0; i < bm->totvert; i++) {
551  UvElement *element = data->elementMap->vert[i];
552  for (; element; element = element->next) {
553  if (element->separate) {
554  if (do_island_optimization && (element->island != island_index)) {
555  /* skip this uv if not on the active island */
556  for (; element->next && !(element->next->separate); element = element->next) {
557  /* pass */
558  }
559  continue;
560  }
561 
562  l = element->l;
564 
565  counter++;
566  data->uv[counter].element = element;
567  data->uv[counter].flag = 0;
568  data->uv[counter].uv = luv->uv;
569  }
570  /* Pointer arithmetic to the rescue, as always :). */
571  uniqueUv[element - data->elementMap->buf] = counter;
572  }
573  }
574 
575  /* Now, on to generate our uv connectivity data */
576  counter = 0;
577  BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
578  BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
579  int offset1, itmp1 = uv_element_offset_from_face_get(
580  data->elementMap, efa, l, island_index, do_island_optimization);
581  int offset2, itmp2 = uv_element_offset_from_face_get(
582  data->elementMap, efa, l->next, island_index, do_island_optimization);
583  char *flag;
584 
585  /* Skip edge if not found(unlikely) or not on valid island */
586  if (itmp1 == -1 || itmp2 == -1) {
587  continue;
588  }
589 
590  offset1 = uniqueUv[itmp1];
591  offset2 = uniqueUv[itmp2];
592 
593  edges[counter].flag = 0;
594  /* Using an order policy, sort UV's according to address space.
595  * This avoids having two different UvEdges with the same UV's on different positions. */
596  if (offset1 < offset2) {
597  edges[counter].uv1 = offset1;
598  edges[counter].uv2 = offset2;
599  }
600  else {
601  edges[counter].uv1 = offset2;
602  edges[counter].uv2 = offset1;
603  }
604  /* Hack! Set the value of the key to its flag.
605  * Now we can set the flag when an edge exists twice :) */
606  flag = BLI_ghash_lookup(edgeHash, &edges[counter]);
607  if (flag) {
608  *flag = 1;
609  }
610  else {
611  /* Hack mentioned */
612  BLI_ghash_insert(edgeHash, &edges[counter], &edges[counter].flag);
613  }
614  counter++;
615  }
616  }
617 
618  MEM_SAFE_FREE(uniqueUv);
619 
620  /* Allocate connectivity data, we allocate edges once */
621  data->uvedges = MEM_mallocN(sizeof(*data->uvedges) * BLI_ghash_len(edgeHash),
622  "uv_brush_edge_connectivity_data");
623  if (!data->uvedges) {
624  BLI_ghash_free(edgeHash, NULL, NULL);
625  MEM_SAFE_FREE(edges);
627  return NULL;
628  }
629 
630  /* fill the edges with data */
631  i = 0;
632  GHASH_ITER (gh_iter, edgeHash) {
633  data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter));
634  }
635  data->totalUvEdges = BLI_ghash_len(edgeHash);
636 
637  /* cleanup temporary stuff */
638  BLI_ghash_free(edgeHash, NULL, NULL);
639  MEM_SAFE_FREE(edges);
640 
641  /* transfer boundary edge property to UV's */
643  for (i = 0; i < data->totalUvEdges; i++) {
644  if (!data->uvedges[i].flag) {
645  data->uv[data->uvedges[i].uv1].flag |= MARK_BOUNDARY;
646  data->uv[data->uvedges[i].uv2].flag |= MARK_BOUNDARY;
647  }
648  }
649  }
650 
651  /* Allocate initial selection for grab tool */
652  if (data->tool == UV_SCULPT_TOOL_GRAB) {
653  float radius, radius_root;
654  UvSculptData *sculptdata = (UvSculptData *)op->customdata;
655  SpaceImage *sima;
656  int width, height;
657  float aspectRatio;
658  float alpha, zoomx, zoomy;
659  Brush *brush = BKE_paint_brush(sculptdata->uvsculpt);
660 
661  alpha = BKE_brush_alpha_get(scene, brush);
662 
663  radius = BKE_brush_size_get(scene, brush);
664  sima = CTX_wm_space_image(C);
666  ED_space_image_get_zoom(sima, region, &zoomx, &zoomy);
667 
668  aspectRatio = width / (float)height;
669  radius /= (width * zoomx);
670  radius = radius * radius;
671  radius_root = sqrtf(radius);
672 
673  /* Allocate selection stack */
674  data->initial_stroke = MEM_mallocN(sizeof(*data->initial_stroke),
675  "uv_sculpt_initial_stroke");
676  if (!data->initial_stroke) {
678  }
679  data->initial_stroke->initialSelection = MEM_mallocN(
680  sizeof(*data->initial_stroke->initialSelection) * data->totalUniqueUvs,
681  "uv_sculpt_initial_selection");
682  if (!data->initial_stroke->initialSelection) {
684  }
685 
686  copy_v2_v2(data->initial_stroke->init_coord, co);
687 
688  counter = 0;
689 
690  for (i = 0; i < data->totalUniqueUvs; i++) {
691  float dist, diff[2];
692  if (data->uv[i].flag & MARK_BOUNDARY) {
693  continue;
694  }
695 
696  sub_v2_v2v2(diff, data->uv[i].uv, co);
697  diff[1] /= aspectRatio;
698  if ((dist = dot_v2v2(diff, diff)) <= radius) {
699  float strength;
700  strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root);
701 
702  data->initial_stroke->initialSelection[counter].uv = i;
703  data->initial_stroke->initialSelection[counter].strength = strength;
704  copy_v2_v2(data->initial_stroke->initialSelection[counter].initial_uv, data->uv[i].uv);
705  counter++;
706  }
707  }
708 
709  data->initial_stroke->totalInitialSelected = counter;
710  }
711  }
712 
713  return op->customdata;
714 }
715 
716 static int uv_sculpt_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
717 {
719  Object *obedit = CTX_data_edit_object(C);
720 
721  if (!(data = uv_sculpt_stroke_init(C, op, event))) {
722  return OPERATOR_CANCELLED;
723  }
724 
725  uv_sculpt_stroke_apply(C, op, event, obedit);
726 
728 
729  if (!data->timer) {
731  return OPERATOR_CANCELLED;
732  }
734 
735  return OPERATOR_RUNNING_MODAL;
736 }
737 
738 static int uv_sculpt_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
739 {
741  Object *obedit = CTX_data_edit_object(C);
742 
743  switch (event->type) {
744  case LEFTMOUSE:
745  case MIDDLEMOUSE:
746  case RIGHTMOUSE:
748  return OPERATOR_FINISHED;
749 
750  case MOUSEMOVE:
751  case INBETWEEN_MOUSEMOVE:
752  uv_sculpt_stroke_apply(C, op, event, obedit);
753  break;
754  case TIMER:
755  if (event->customdata == data->timer) {
756  uv_sculpt_stroke_apply(C, op, event, obedit);
757  }
758  break;
759  default:
760  return OPERATOR_RUNNING_MODAL;
761  }
762 
764  WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
765  DEG_id_tag_update(obedit->data, 0);
766  return OPERATOR_RUNNING_MODAL;
767 }
768 
770 {
772  /* While these values could be initialized on demand,
773  * the only case this would be useful is running from the operator search popup.
774  * This is such a corner case that it's simpler to check a brush has already been created
775  * (something the tool system ensures). */
778  Brush *brush = BKE_paint_brush(&ts->uvsculpt->paint);
779  if (brush != NULL) {
780  return true;
781  }
782  }
783  return false;
784 }
785 
787 {
788  static const EnumPropertyItem stroke_mode_items[] = {
789  {BRUSH_STROKE_NORMAL, "NORMAL", 0, "Regular", "Apply brush normally"},
791  "INVERT",
792  0,
793  "Invert",
794  "Invert action of brush for duration of stroke"},
796  "RELAX",
797  0,
798  "Relax",
799  "Switch brush to relax mode for duration of stroke"},
800  {0},
801  };
802 
803  /* identifiers */
804  ot->name = "Sculpt UVs";
805  ot->description = "Sculpt UVs using a brush";
806  ot->idname = "SCULPT_OT_uv_sculpt_stroke";
807 
808  /* api callbacks */
812 
813  /* flags */
815 
816  /* props */
817  RNA_def_enum(ot->srna, "mode", stroke_mode_items, BRUSH_STROKE_NORMAL, "Mode", "Stroke Mode");
818 }
typedef float(TangentPoint)[2]
float BKE_brush_curve_strength_clamped(const struct Brush *br, float p, float len)
float BKE_brush_alpha_get(const struct Scene *scene, const struct Brush *brush)
int BKE_brush_size_get(const struct Scene *scene, const struct Brush *brush)
void BKE_curvemapping_init(struct CurveMapping *cumap)
Definition: colortools.c:1235
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
struct Object * CTX_data_edit_object(const bContext *C)
Definition: context.c:1370
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:713
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:749
struct SpaceImage * CTX_wm_space_image(const bContext *C)
Definition: context.c:824
struct ToolSettings * CTX_data_tool_settings(const bContext *C)
Definition: context.c:1282
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:723
CustomData interface, see also DNA_customdata_types.h.
void * CustomData_bmesh_get(const struct CustomData *data, void *block, int type)
BMEditMesh * BKE_editmesh_from_object(struct Object *ob)
Return the BMEditMesh for a given object.
Definition: editmesh.c:58
struct Brush * BKE_paint_brush(struct Paint *paint)
Definition: paint.c:607
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.h:298
unsigned int BLI_ghashutil_uinthash(unsigned int key)
#define GHASH_ITER(gh_iter_, ghash_)
Definition: BLI_ghash.h:321
GHash * BLI_ghash_new(GHashHashFP hashfp, GHashCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:689
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:734
unsigned int BLI_ghash_len(const GHash *gh) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:705
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition: BLI_ghash.c:710
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition: BLI_ghash.c:863
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v2(float r[2])
unsigned int uint
Definition: BLI_sys_types.h:67
void DEG_id_tag_update(struct ID *id, int flag)
@ UV_SCULPT_TOOL_GRAB
@ UV_SCULPT_TOOL_RELAX
@ UV_SCULPT_TOOL_PINCH
@ CD_MLOOPUV
Object is a sort of wrapper for general info.
#define UV_SCULPT_TOOL_RELAX_HC
#define UV_SCULPT_LOCK_BORDERS
#define UV_SCULPT_ALL_ISLANDS
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
void ED_space_image_get_zoom(struct SpaceImage *sima, const struct ARegion *region, float *r_zoomx, float *r_zoomy)
void ED_space_image_get_size(struct SpaceImage *sima, int *r_width, int *r_height)
Definition: image_edit.c:201
struct UvElementMap * BM_uv_element_map_create(struct BMesh *bm, const struct Scene *scene, bool uv_selected, bool use_winding, bool do_islands)
struct UvElement * BM_uv_element_get(struct UvElementMap *map, struct BMFace *efa, struct BMLoop *l)
void BM_uv_element_map_free(struct UvElementMap *element_map)
bool ED_operator_uvedit_space_image(struct bContext *C)
Definition: screen_ops.c:564
void ED_region_tag_redraw(struct ARegion *region)
Definition: area.c:655
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define C
Definition: RandGen.cpp:25
void UI_view2d_region_to_view(const struct View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
@ OPTYPE_UNDO
Definition: WM_types.h:148
@ OPTYPE_REGISTER
Definition: WM_types.h:146
#define NC_GEOM
Definition: WM_types.h:343
#define ND_DATA
Definition: WM_types.h:456
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_FACE
ATTR_WARN_UNUSED_RESULT BMesh * bm
ATTR_WARN_UNUSED_RESULT const void * element
ATTR_WARN_UNUSED_RESULT const BMLoop * l
Scene scene
CCL_NAMESPACE_BEGIN ccl_device float invert(float color, float factor)
Definition: invert.h:8
ccl_gpu_kernel_postfix ccl_global int * counter
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
#define sqrtf(x)
Definition: metal/compat.h:243
static unsigned a[3]
Definition: RandGen.cpp:78
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt=1)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
SocketIndexByIdentifierMap * map
@ BRUSH_STROKE_SMOOTH
Definition: paint_intern.h:451
@ BRUSH_STROKE_NORMAL
Definition: paint_intern.h:449
@ BRUSH_STROKE_INVERT
Definition: paint_intern.h:450
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5004
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, int default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3783
static void HC_relaxation_iteration_uv(BMEditMesh *em, UvSculptData *sculptdata, const float mouse_coord[2], float alpha, float radius, float aspectRatio)
Definition: sculpt_uv.c:131
static bool uv_sculpt_stroke_poll(bContext *C)
Definition: sculpt_uv.c:769
static UvSculptData * uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wmEvent *event)
Definition: sculpt_uv.c:458
struct UVInitialStroke UVInitialStroke
struct Temp_UvData Temp_UVData
static int uv_sculpt_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
Definition: sculpt_uv.c:738
struct UvEdge UvEdge
void SCULPT_OT_uv_sculpt_stroke(wmOperatorType *ot)
Definition: sculpt_uv.c:786
struct UvAdjacencyElement UvAdjacencyElement
static bool uv_edge_compare(const void *a, const void *b)
Definition: sculpt_uv.c:447
static int uv_sculpt_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition: sculpt_uv.c:716
struct UvSculptData UvSculptData
static void laplacian_relaxation_iteration_uv(BMEditMesh *em, UvSculptData *sculptdata, const float mouse_coord[2], float alpha, float radius, float aspectRatio)
Definition: sculpt_uv.c:217
static void uv_sculpt_stroke_apply(bContext *C, wmOperator *op, const wmEvent *event, Object *obedit)
Definition: sculpt_uv.c:289
static uint uv_edge_hash(const void *key)
Definition: sculpt_uv.c:441
static int uv_element_offset_from_face_get(UvElementMap *map, BMFace *efa, BMLoop *l, int island_index, const bool doIslands)
Definition: sculpt_uv.c:431
#define MARK_BOUNDARY
Definition: sculpt_uv.c:45
static void uv_sculpt_stroke_exit(bContext *C, wmOperator *op)
Definition: sculpt_uv.c:411
struct UVInitialStrokeElement UVInitialStrokeElement
struct BMesh * bm
Definition: BKE_editmesh.h:40
void * data
Definition: bmesh_class.h:51
BMHeader head
Definition: bmesh_class.h:145
struct BMLoop * next
Definition: bmesh_class.h:233
int totvert
Definition: bmesh_class.h:297
CustomData ldata
Definition: bmesh_class.h:337
char uv_sculpt_tool
struct CurveMapping * curve
struct Brush * brush
struct ToolSettings * toolsettings
float sum_co[2]
Definition: sculpt_uv.c:127
int ncounter
Definition: sculpt_uv.c:128
float p[2]
Definition: sculpt_uv.c:127
float sum_b[2]
Definition: sculpt_uv.c:127
float b[2]
Definition: sculpt_uv.c:127
UvSculpt * uvsculpt
float init_coord[2]
Definition: sculpt_uv.c:83
int totalInitialSelected
Definition: sculpt_uv.c:80
UVInitialStrokeElement * initialSelection
Definition: sculpt_uv.c:77
UvElement * element
Definition: sculpt_uv.c:49
char flag
Definition: sculpt_uv.c:61
uint uv1
Definition: sculpt_uv.c:57
uint uv2
Definition: sculpt_uv.c:58
struct BMLoop * l
Definition: uvedit_intern.h:27
struct BMFace * efa
Definition: uvedit_intern.h:26
int totalUniqueUvs
Definition: sculpt_uv.c:94
UVInitialStroke * initial_stroke
Definition: sculpt_uv.c:103
Paint * uvsculpt
Definition: sculpt_uv.c:112
UvElementMap * elementMap
Definition: sculpt_uv.c:109
UvAdjacencyElement * uv
Definition: sculpt_uv.c:91
int totalUvEdges
Definition: sculpt_uv.c:100
UvEdge * uvedges
Definition: sculpt_uv.c:97
wmTimer * timer
Definition: sculpt_uv.c:106
int mval[2]
Definition: WM_types.h:684
short type
Definition: WM_types.h:678
void * customdata
Definition: WM_types.h:715
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:919
const char * name
Definition: WM_types.h:888
int(* modal)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:935
const char * idname
Definition: WM_types.h:890
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:943
struct StructRNA * srna
Definition: WM_types.h:969
const char * description
Definition: WM_types.h:893
struct PointerRNA * ptr
bool uv_find_nearest_vert(struct Scene *scene, struct Object *obedit, const float co[2], float penalty_dist, struct UvNearestHit *hit)
#define UV_NEAREST_HIT_INIT_MAX(v2d)
Definition: uvedit_intern.h:49
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ RIGHTMOUSE
@ TIMER
@ MOUSEMOVE
@ LEFTMOUSE
@ MIDDLEMOUSE
@ INBETWEEN_MOUSEMOVE
wmOperatorType * ot
Definition: wm_files.c:3479
void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
Definition: wm_window.c:1682
wmTimer * WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
Definition: wm_window.c:1630