Blender V4.3
sculpt_uv.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2002-2009 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10#include "MEM_guardedalloc.h"
11
12#include "BLI_ghash.h"
13#include "BLI_math_base_safe.h"
14#include "BLI_math_geom.h"
15#include "BLI_math_vector.h"
16#include "BLI_utildefines.h"
17
18#include "DNA_brush_types.h"
19#include "DNA_object_types.h"
20#include "DNA_scene_types.h"
21
22#include "BKE_brush.hh"
23#include "BKE_colortools.hh"
24#include "BKE_context.hh"
25#include "BKE_customdata.hh"
26#include "BKE_editmesh.hh"
27#include "BKE_image.hh"
28#include "BKE_mesh_mapping.hh"
29#include "BKE_paint.hh"
30
31#include "DEG_depsgraph.hh"
32
33#include "ED_image.hh"
34#include "ED_mesh.hh"
35#include "ED_screen.hh"
36#include "ED_uvedit.hh"
37
38#include "WM_api.hh"
39#include "WM_types.hh"
40
41#include "RNA_access.hh"
42#include "RNA_define.hh"
43#include "RNA_enum_types.hh"
44
45#include "paint_intern.hh"
46#include "uvedit_intern.hh"
47
48#include "UI_view2d.hh"
49
55
56enum {
60};
61
62/* When set, the UV element is on the boundary of the graph.
63 * i.e. Instead of a 2-dimensional laplace operator, use a 1-dimensional version.
64 * Visually, UV elements on the graph boundary appear as borders of the UV Island. */
65#define MARK_BOUNDARY 1
66
77
84
87 int uv;
88
90 float strength;
91
93 float initial_uv[2];
94};
95
106
148
149static void apply_sculpt_data_constraints(UvSculptData *sculptdata, float uv[2])
150{
151 if (!sculptdata->constrain_to_bounds) {
152 return;
153 }
154 float u = sculptdata->uv_base_offset[0];
155 float v = sculptdata->uv_base_offset[1];
156 uv[0] = clamp_f(uv[0], u, u + 1.0f);
157 uv[1] = clamp_f(uv[1], v, v + 1.0f);
158}
159
160static float calc_strength(const UvSculptData *sculptdata, float p, const float len)
161{
163 sculptdata->uvsculpt->strength_curve,
164 p,
165 len);
166
167 CLAMP(strength, 0.0f, 1.0f);
168
169 return strength;
170}
171
172/*********** Improved Laplacian Relaxation Operator ************************/
173/* original code by Raul Fernandez Hernandez "farsthary" *
174 * adapted to uv smoothing by Antony Riakiatakis *
175 ***************************************************************************/
176
178 float sum_co[2], p[2], b[2], sum_b[2];
180};
181
183 const int cd_loop_uv_offset,
184 const float mouse_coord[2],
185 const float alpha,
186 const float radius_sq,
187 const float aspectRatio)
188{
189 float diff[2];
190 int i;
191 const float radius = sqrtf(radius_sq);
192
193 Temp_UVData *tmp_uvdata = (Temp_UVData *)MEM_callocN(
194 sculptdata->totalUniqueUvs * sizeof(Temp_UVData), "Temporal data");
195
196 /* counting neighbors */
197 for (i = 0; i < sculptdata->totalUvEdges; i++) {
198 UvEdge *tmpedge = sculptdata->uvedges + i;
199 tmp_uvdata[tmpedge->uv1].ncounter++;
200 tmp_uvdata[tmpedge->uv2].ncounter++;
201
202 add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv);
203 add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv);
204 }
205
206 for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
207 copy_v2_v2(diff, tmp_uvdata[i].sum_co);
208 mul_v2_fl(diff, 1.0f / tmp_uvdata[i].ncounter);
209 copy_v2_v2(tmp_uvdata[i].p, diff);
210
211 tmp_uvdata[i].b[0] = diff[0] - sculptdata->uv[i].uv[0];
212 tmp_uvdata[i].b[1] = diff[1] - sculptdata->uv[i].uv[1];
213 }
214
215 for (i = 0; i < sculptdata->totalUvEdges; i++) {
216 UvEdge *tmpedge = sculptdata->uvedges + i;
217 add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_b, tmp_uvdata[tmpedge->uv2].b);
218 add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_b, tmp_uvdata[tmpedge->uv1].b);
219 }
220
221 for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
222 if (sculptdata->uv[i].is_locked) {
223 continue;
224 }
225
226 sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord);
227 diff[1] /= aspectRatio;
228 float dist = dot_v2v2(diff, diff);
229 if (dist <= radius_sq) {
231 float strength;
232 strength = alpha * calc_strength(sculptdata, sqrtf(dist), radius);
233
234 sculptdata->uv[i].uv[0] = (1.0f - strength) * sculptdata->uv[i].uv[0] +
235 strength *
236 (tmp_uvdata[i].p[0] -
237 0.5f * (tmp_uvdata[i].b[0] +
238 tmp_uvdata[i].sum_b[0] / tmp_uvdata[i].ncounter));
239 sculptdata->uv[i].uv[1] = (1.0f - strength) * sculptdata->uv[i].uv[1] +
240 strength *
241 (tmp_uvdata[i].p[1] -
242 0.5f * (tmp_uvdata[i].b[1] +
243 tmp_uvdata[i].sum_b[1] / tmp_uvdata[i].ncounter));
244
245 apply_sculpt_data_constraints(sculptdata, sculptdata->uv[i].uv);
246
247 for (element = sculptdata->uv[i].element; element; element = element->next) {
248 if (element->separate && element != sculptdata->uv[i].element) {
249 break;
250 }
251 float(*luv)[2] = BM_ELEM_CD_GET_FLOAT2_P(element->l, cd_loop_uv_offset);
252 copy_v2_v2(*luv, sculptdata->uv[i].uv);
253 }
254 }
255 }
256
257 MEM_SAFE_FREE(tmp_uvdata);
258}
259
260/* Legacy version which only does laplacian relaxation.
261 * Probably a little faster as it caches UvEdges.
262 * Mostly preserved for comparison with `HC_relaxation_iteration_uv`.
263 * Once the HC method has been merged into `relaxation_iteration_uv`,
264 * all the `HC_*` and `laplacian_*` specific functions can probably be removed.
265 */
266
268 const int cd_loop_uv_offset,
269 const float mouse_coord[2],
270 const float alpha,
271 const float radius_sq,
272 const float aspectRatio)
273{
274 float diff[2];
275 int i;
276 const float radius = sqrtf(radius_sq);
277
278 Temp_UVData *tmp_uvdata = (Temp_UVData *)MEM_callocN(
279 sculptdata->totalUniqueUvs * sizeof(Temp_UVData), "Temporal data");
280
281 /* counting neighbors */
282 for (i = 0; i < sculptdata->totalUvEdges; i++) {
283 UvEdge *tmpedge = sculptdata->uvedges + i;
284 bool code1 = sculptdata->uv[sculptdata->uvedges[i].uv1].is_boundary;
285 bool code2 = sculptdata->uv[sculptdata->uvedges[i].uv2].is_boundary;
286 if (code1 || (code1 == code2)) {
287 tmp_uvdata[tmpedge->uv2].ncounter++;
288 add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv);
289 }
290 if (code2 || (code1 == code2)) {
291 tmp_uvdata[tmpedge->uv1].ncounter++;
292 add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv);
293 }
294 }
295
296 /* Original Laplacian algorithm included removal of normal component of translation.
297 * here it is not needed since we translate along the UV plane always. */
298 for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
299 copy_v2_v2(tmp_uvdata[i].p, tmp_uvdata[i].sum_co);
300 mul_v2_fl(tmp_uvdata[i].p, 1.0f / tmp_uvdata[i].ncounter);
301 }
302
303 for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
304 if (sculptdata->uv[i].is_locked) {
305 continue;
306 }
307
308 sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord);
309 diff[1] /= aspectRatio;
310 float dist = dot_v2v2(diff, diff);
311 if (dist <= radius_sq) {
313 float strength;
314 strength = alpha * calc_strength(sculptdata, sqrtf(dist), radius);
315
316 sculptdata->uv[i].uv[0] = (1.0f - strength) * sculptdata->uv[i].uv[0] +
317 strength * tmp_uvdata[i].p[0];
318 sculptdata->uv[i].uv[1] = (1.0f - strength) * sculptdata->uv[i].uv[1] +
319 strength * tmp_uvdata[i].p[1];
320
321 apply_sculpt_data_constraints(sculptdata, sculptdata->uv[i].uv);
322
323 for (element = sculptdata->uv[i].element; element; element = element->next) {
324 if (element->separate && element != sculptdata->uv[i].element) {
325 break;
326 }
327
328 float(*luv)[2] = BM_ELEM_CD_GET_FLOAT2_P(element->l, cd_loop_uv_offset);
329 copy_v2_v2(*luv, sculptdata->uv[i].uv);
330 }
331 }
332 }
333
334 MEM_SAFE_FREE(tmp_uvdata);
335}
336
337static void add_weighted_edge(float (*delta_buf)[3],
338 const UvElement *storage,
339 const UvElement *ele_next,
340 const UvElement *ele_prev,
341 const float luv_next[2],
342 const float luv_prev[2],
343 const float weight)
344{
345 float delta[2];
346 sub_v2_v2v2(delta, luv_next, luv_prev);
347
348 bool code1 = (ele_prev->flag & MARK_BOUNDARY);
349 bool code2 = (ele_next->flag & MARK_BOUNDARY);
350 if (code1 || (code1 == code2)) {
351 int index_next = ele_next - storage;
352 delta_buf[index_next][0] -= delta[0] * weight;
353 delta_buf[index_next][1] -= delta[1] * weight;
354 delta_buf[index_next][2] += fabsf(weight);
355 }
356 if (code2 || (code1 == code2)) {
357 int index_prev = ele_prev - storage;
358 delta_buf[index_prev][0] += delta[0] * weight;
359 delta_buf[index_prev][1] += delta[1] * weight;
360 delta_buf[index_prev][2] += fabsf(weight);
361 }
362}
363
364static float tri_weight_v3(int method, const float *v1, const float *v2, const float *v3)
365{
366 switch (method) {
369 return 1.0f;
371 return cotangent_tri_weight_v3(v1, v2, v3);
372 default:
374 }
375 return 0.0f;
376}
377
378static void relaxation_iteration_uv(UvSculptData *sculptdata,
379 const int cd_loop_uv_offset,
380 const float mouse_coord[2],
381 const float alpha,
382 const float radius_sq,
383 const float aspect_ratio,
384 const int method)
385{
386 if (method == UV_SCULPT_BRUSH_TYPE_RELAX_HC) {
388 sculptdata, cd_loop_uv_offset, mouse_coord, alpha, radius_sq, aspect_ratio);
389 return;
390 }
393 sculptdata, cd_loop_uv_offset, mouse_coord, alpha, radius_sq, aspect_ratio);
394 return;
395 }
396
397 UvElement **head_table = BM_uv_element_map_ensure_head_table(sculptdata->elementMap);
398
399 const int total_uvs = sculptdata->elementMap->total_uvs;
400 float(*delta_buf)[3] = (float(*)[3])MEM_callocN(total_uvs * sizeof(float[3]), __func__);
401
402 const UvElement *storage = sculptdata->elementMap->storage;
403 for (int j = 0; j < total_uvs; j++) {
404 const UvElement *ele_curr = storage + j;
405 const UvElement *ele_next = BM_uv_element_get(sculptdata->elementMap, ele_curr->l->next);
406 const UvElement *ele_prev = BM_uv_element_get(sculptdata->elementMap, ele_curr->l->prev);
407
408 const float *v_curr_co = ele_curr->l->v->co;
409 const float *v_prev_co = ele_prev->l->v->co;
410 const float *v_next_co = ele_next->l->v->co;
411
412 const float(*luv_curr)[2] = BM_ELEM_CD_GET_FLOAT2_P(ele_curr->l, cd_loop_uv_offset);
413 const float(*luv_next)[2] = BM_ELEM_CD_GET_FLOAT2_P(ele_next->l, cd_loop_uv_offset);
414 const float(*luv_prev)[2] = BM_ELEM_CD_GET_FLOAT2_P(ele_prev->l, cd_loop_uv_offset);
415
416 const UvElement *head_curr = head_table[ele_curr - sculptdata->elementMap->storage];
417 const UvElement *head_next = head_table[ele_next - sculptdata->elementMap->storage];
418 const UvElement *head_prev = head_table[ele_prev - sculptdata->elementMap->storage];
419
420 /* If the mesh is triangulated with no boundaries, only one edge is required. */
421 const float weight_curr = tri_weight_v3(method, v_curr_co, v_prev_co, v_next_co);
422 add_weighted_edge(delta_buf, storage, head_next, head_prev, *luv_next, *luv_prev, weight_curr);
423
424 /* Triangulated with a boundary? We need the incoming edges to solve the boundary. */
425 const float weight_prev = tri_weight_v3(method, v_prev_co, v_curr_co, v_next_co);
426 add_weighted_edge(delta_buf, storage, head_next, head_curr, *luv_next, *luv_curr, weight_prev);
427
429 /* Laplacian method has zero weights on virtual edges. */
430 continue;
431 }
432
433 /* Meshes with quads (or other n-gons) need "virtual" edges too. */
434 const float weight_next = tri_weight_v3(method, v_next_co, v_curr_co, v_prev_co);
435 add_weighted_edge(delta_buf, storage, head_prev, head_curr, *luv_prev, *luv_curr, weight_next);
436 }
437
438 for (int i = 0; i < sculptdata->totalUniqueUvs; i++) {
439 UvAdjacencyElement *adj_el = &sculptdata->uv[i];
440 if (adj_el->is_locked) {
441 continue; /* Locked UVs can't move. */
442 }
443
444 /* Is UV within influence? */
445 float diff[2];
446 sub_v2_v2v2(diff, adj_el->uv, mouse_coord);
447 diff[1] /= aspect_ratio;
448 const float dist_sq = len_squared_v2(diff);
449 if (dist_sq > radius_sq) {
450 continue;
451 }
452 const float strength = alpha * calc_strength(sculptdata, sqrtf(dist_sq), sqrtf(radius_sq));
453
454 const float *delta_sum = delta_buf[adj_el->element - storage];
455
456 {
457 const float(*luv)[2] = BM_ELEM_CD_GET_FLOAT2_P(adj_el->element->l, cd_loop_uv_offset);
458 BLI_assert(adj_el->uv == (float *)luv); /* Only true for head. */
459 adj_el->uv[0] = (*luv)[0] + strength * safe_divide(delta_sum[0], delta_sum[2]);
460 adj_el->uv[1] = (*luv)[1] + strength * safe_divide(delta_sum[1], delta_sum[2]);
461 apply_sculpt_data_constraints(sculptdata, adj_el->uv);
462 }
463
464 /* Copy UV co-ordinates to all UvElements. */
465 UvElement *tail = adj_el->element;
466 while (tail) {
467 float(*luv)[2] = BM_ELEM_CD_GET_FLOAT2_P(tail->l, cd_loop_uv_offset);
468 copy_v2_v2(*luv, adj_el->uv);
469 tail = tail->next;
470 if (tail && tail->separate) {
471 break;
472 }
473 }
474 }
475
476 MEM_SAFE_FREE(delta_buf);
477}
478
480 wmOperator *op,
481 const wmEvent *event,
482 Object *obedit)
483{
484 ARegion *region = CTX_wm_region(C);
486 UvSculptData *sculptdata = (UvSculptData *)op->customdata;
487 eBrushUVSculptTool tool = eBrushUVSculptTool(sculptdata->tool);
488 int invert = sculptdata->invert ? -1 : 1;
489 float alpha = sculptdata->uvsculpt->strength;
490
491 float co[2];
492 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
493
495
496 int width, height;
497 ED_space_image_get_size(sima, &width, &height);
498
499 float zoomx, zoomy;
500 ED_space_image_get_zoom(sima, region, &zoomx, &zoomy);
501
502 const float radius = sculptdata->uvsculpt->size / (width * zoomx);
503 float aspectRatio = width / float(height);
504
505 /* We will compare squares to save some computation */
506 const float radius_sq = radius * radius;
507
508 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_PROP_FLOAT2);
509
510 switch (tool) {
512 int i;
513 alpha *= invert;
514 for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
515 if (sculptdata->uv[i].is_locked) {
516 continue;
517 }
518
519 float diff[2];
520 sub_v2_v2v2(diff, sculptdata->uv[i].uv, co);
521 diff[1] /= aspectRatio;
522 float dist = dot_v2v2(diff, diff);
523 if (dist <= radius_sq) {
525 float strength;
526 strength = alpha * calc_strength(sculptdata, sqrtf(dist), radius);
528
529 sculptdata->uv[i].uv[0] -= strength * diff[0] * 0.001f;
530 sculptdata->uv[i].uv[1] -= strength * diff[1] * 0.001f;
531
532 apply_sculpt_data_constraints(sculptdata, sculptdata->uv[i].uv);
533
534 for (element = sculptdata->uv[i].element; element; element = element->next) {
535 if (element->separate && element != sculptdata->uv[i].element) {
536 break;
537 }
538 float(*luv)[2] = BM_ELEM_CD_GET_FLOAT2_P(element->l, cd_loop_uv_offset);
539 copy_v2_v2(*luv, sculptdata->uv[i].uv);
540 }
541 }
542 }
543 break;
544 }
546 relaxation_iteration_uv(sculptdata,
547 cd_loop_uv_offset,
548 co,
549 alpha,
550 radius_sq,
551 aspectRatio,
552 RNA_enum_get(op->ptr, "relax_method"));
553 break;
554 }
556 int i;
557 float diff[2];
558 sub_v2_v2v2(diff, co, sculptdata->initial_stroke->init_coord);
559
560 for (i = 0; i < sculptdata->initial_stroke->totalInitialSelected; i++) {
562 int uvindex = sculptdata->initial_stroke->initialSelection[i].uv;
563 float strength = sculptdata->initial_stroke->initialSelection[i].strength;
564 sculptdata->uv[uvindex].uv[0] =
565 sculptdata->initial_stroke->initialSelection[i].initial_uv[0] + strength * diff[0];
566 sculptdata->uv[uvindex].uv[1] =
567 sculptdata->initial_stroke->initialSelection[i].initial_uv[1] + strength * diff[1];
568
569 apply_sculpt_data_constraints(sculptdata, sculptdata->uv[uvindex].uv);
570
571 for (element = sculptdata->uv[uvindex].element; element; element = element->next) {
572 if (element->separate && element != sculptdata->uv[uvindex].element) {
573 break;
574 }
575 float(*luv)[2] = BM_ELEM_CD_GET_FLOAT2_P(element->l, cd_loop_uv_offset);
576 copy_v2_v2(*luv, sculptdata->uv[uvindex].uv);
577 }
578 }
579 if (sima->flag & SI_LIVE_UNWRAP) {
581 }
582 break;
583 }
584 }
585}
586
588{
590 if (sima->flag & SI_LIVE_UNWRAP) {
592 }
593 UvSculptData *data = static_cast<UvSculptData *>(op->customdata);
594 if (data->timer) {
596 }
597 BM_uv_element_map_free(data->elementMap);
598 data->elementMap = nullptr;
599 MEM_SAFE_FREE(data->uv);
600 MEM_SAFE_FREE(data->uvedges);
601 if (data->initial_stroke) {
602 MEM_SAFE_FREE(data->initial_stroke->initialSelection);
603 MEM_SAFE_FREE(data->initial_stroke);
604 }
605
606 MEM_SAFE_FREE(data);
607 op->customdata = nullptr;
608}
609
611 BMLoop *l,
612 int island_index,
613 const bool do_islands)
614{
615 UvElement *element = BM_uv_element_get(map, l);
616 if (!element || (do_islands && element->island != island_index)) {
617 return -1;
618 }
619 return element - map->storage;
620}
621
622static uint uv_edge_hash(const void *key)
623{
624 const UvEdge *edge = static_cast<const UvEdge *>(key);
625 return (BLI_ghashutil_uinthash(edge->uv2) + BLI_ghashutil_uinthash(edge->uv1));
626}
627
628static bool uv_edge_compare(const void *a, const void *b)
629{
630 const UvEdge *edge1 = static_cast<const UvEdge *>(a);
631 const UvEdge *edge2 = static_cast<const UvEdge *>(b);
632
633 if ((edge1->uv1 == edge2->uv1) && (edge1->uv2 == edge2->uv2)) {
634 return false;
635 }
636 return true;
637}
638
639static void set_element_flag(UvElement *element, const int flag)
640{
641 while (element) {
642 element->flag |= flag;
643 element = element->next;
644 if (!element || element->separate) {
645 break;
646 }
647 }
648}
649
651{
652 Scene *scene = CTX_data_scene(C);
653 Object *obedit = CTX_data_edit_object(C);
654 ToolSettings *ts = scene->toolsettings;
655 UvSculptData *data = MEM_cnew<UvSculptData>(__func__);
657 BMesh *bm = em->bm;
658
659 op->customdata = data;
660
662
663 if (!data) {
664 return nullptr;
665 }
666
667 ARegion *region = CTX_wm_region(C);
668 float co[2];
669 BMFace *efa;
670 float(*luv)[2];
671 BMLoop *l;
672 BMIter iter, liter;
673
674 GHashIterator gh_iter;
675
676 bool do_island_optimization = !(ts->uv_sculpt_settings & UV_SCULPT_ALL_ISLANDS);
677 int island_index = 0;
678 if (STREQ(op->type->idname, "SCULPT_OT_uv_sculpt_relax")) {
679 data->tool = UV_SCULPT_BRUSH_TYPE_RELAX;
680 }
681 else if (STREQ(op->type->idname, "SCULPT_OT_uv_sculpt_grab")) {
682 data->tool = UV_SCULPT_BRUSH_TYPE_GRAB;
683 }
684 else {
685 data->tool = UV_SCULPT_BRUSH_TYPE_PINCH;
686 }
687 data->invert = RNA_boolean_get(op->ptr, "use_invert");
688
689 data->uvsculpt = &ts->uvsculpt;
690
691 /* Winding was added to island detection in 5197aa04c6bd
692 * However the sculpt tools can flip faces, potentially creating orphaned islands.
693 * See #100132 */
694 const bool use_winding = false;
695 const bool use_seams = true;
696 data->elementMap = BM_uv_element_map_create(
697 bm, scene, false, use_winding, use_seams, do_island_optimization);
698
699 if (!data->elementMap) {
701 return nullptr;
702 }
703
704 /* Mouse coordinates, useful for some functions like grab and sculpt all islands */
705 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
706
707 /* We need to find the active island here. */
708 if (do_island_optimization) {
709 UvNearestHit hit = uv_nearest_hit_init_max(&region->v2d);
710 uv_find_nearest_vert(scene, obedit, co, 0.0f, &hit);
711
712 UvElement *element = BM_uv_element_get(data->elementMap, hit.l);
713 if (element) {
714 island_index = element->island;
715 }
716 }
717
718 /* Count 'unique' UVs */
719 int unique_uvs = data->elementMap->total_unique_uvs;
720 if (do_island_optimization) {
721 unique_uvs = data->elementMap->island_total_unique_uvs[island_index];
722 }
723
724 /* Allocate the unique uv buffers */
725 data->uv = MEM_cnew_array<UvAdjacencyElement>(unique_uvs, __func__);
726 /* Holds, for each UvElement in elementMap, an index of its unique UV. */
727 int *uniqueUv = static_cast<int *>(
728 MEM_mallocN(sizeof(*uniqueUv) * data->elementMap->total_uvs, __func__));
729 GHash *edgeHash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "uv_brush_edge_hash");
730 /* we have at most totalUVs edges */
731 UvEdge *edges = MEM_cnew_array<UvEdge>(data->elementMap->total_uvs, __func__);
732 if (!data->uv || !uniqueUv || !edgeHash || !edges) {
733 MEM_SAFE_FREE(edges);
734 MEM_SAFE_FREE(uniqueUv);
735 if (edgeHash) {
736 BLI_ghash_free(edgeHash, nullptr, nullptr);
737 }
739 return nullptr;
740 }
741
742 data->totalUniqueUvs = unique_uvs;
743 /* Index for the UvElements. */
744 int counter = -1;
745
746 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
747 /* initialize the unique UVs */
748 for (int i = 0; i < bm->totvert; i++) {
749 UvElement *element = data->elementMap->vertex[i];
750 for (; element; element = element->next) {
751 if (element->separate) {
752 if (do_island_optimization && (element->island != island_index)) {
753 /* skip this uv if not on the active island */
754 for (; element->next && !(element->next->separate); element = element->next) {
755 /* pass */
756 }
757 continue;
758 }
759
760 luv = BM_ELEM_CD_GET_FLOAT2_P(element->l, offsets.uv);
761
762 counter++;
763 data->uv[counter].element = element;
764 data->uv[counter].uv = *luv;
765 if (data->tool != UV_SCULPT_BRUSH_TYPE_GRAB) {
766 if (BM_ELEM_CD_GET_BOOL(element->l, offsets.pin)) {
767 data->uv[counter].is_locked = true;
768 }
769 }
770 }
771 /* Pointer arithmetic to the rescue, as always :). */
772 uniqueUv[element - data->elementMap->storage] = counter;
773 }
774 }
775 BLI_assert(counter + 1 == unique_uvs);
776
777 /* Now, on to generate our uv connectivity data */
778 counter = 0;
779 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
780 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
782 data->elementMap, l, island_index, do_island_optimization);
784 data->elementMap, l->next, island_index, do_island_optimization);
785
786 /* Skip edge if not found(unlikely) or not on valid island */
787 if (itmp1 == -1 || itmp2 == -1) {
788 continue;
789 }
790
791 int offset1 = uniqueUv[itmp1];
792 int offset2 = uniqueUv[itmp2];
793
794 /* Using an order policy, sort UVs according to address space.
795 * This avoids having two different UvEdges with the same UVs on different positions. */
796 if (offset1 < offset2) {
797 edges[counter].uv1 = offset1;
798 edges[counter].uv2 = offset2;
799 }
800 else {
801 edges[counter].uv1 = offset2;
802 edges[counter].uv2 = offset1;
803 }
804 UvEdge *prev_edge = static_cast<UvEdge *>(BLI_ghash_lookup(edgeHash, &edges[counter]));
805 if (prev_edge) {
806 prev_edge->is_interior = true;
807 edges[counter].is_interior = true;
808 }
809 else {
810 BLI_ghash_insert(edgeHash, &edges[counter], &edges[counter]);
811 }
812 counter++;
813 }
814 }
815
816 MEM_SAFE_FREE(uniqueUv);
817
818 /* Allocate connectivity data, we allocate edges once */
819 data->uvedges = MEM_cnew_array<UvEdge>(BLI_ghash_len(edgeHash), __func__);
820 if (!data->uvedges) {
821 BLI_ghash_free(edgeHash, nullptr, nullptr);
822 MEM_SAFE_FREE(edges);
824 return nullptr;
825 }
826
827 /* fill the edges with data */
828 {
829 int i = 0;
830 GHASH_ITER (gh_iter, edgeHash) {
831 data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter));
832 }
833 data->totalUvEdges = BLI_ghash_len(edgeHash);
834 }
835
836 /* cleanup temporary stuff */
837 BLI_ghash_free(edgeHash, nullptr, nullptr);
838 MEM_SAFE_FREE(edges);
839
840 /* transfer boundary edge property to UVs */
841 for (int i = 0; i < data->totalUvEdges; i++) {
842 if (!data->uvedges[i].is_interior) {
843 data->uv[data->uvedges[i].uv1].is_boundary = true;
844 data->uv[data->uvedges[i].uv2].is_boundary = true;
846 data->uv[data->uvedges[i].uv1].is_locked = true;
847 data->uv[data->uvedges[i].uv2].is_locked = true;
848 }
849 set_element_flag(data->uv[data->uvedges[i].uv1].element, MARK_BOUNDARY);
850 set_element_flag(data->uv[data->uvedges[i].uv2].element, MARK_BOUNDARY);
851 }
852 }
853
855 data->constrain_to_bounds = (sima->flag & SI_CLIP_UV);
856 BKE_image_find_nearest_tile_with_offset(sima->image, co, data->uv_base_offset);
857
858 /* Allocate initial selection for grab tool */
859 if (data->tool == UV_SCULPT_BRUSH_TYPE_GRAB) {
860 float alpha = data->uvsculpt->strength;
861 float radius = data->uvsculpt->size;
862 int width, height;
863 ED_space_image_get_size(sima, &width, &height);
864 float zoomx, zoomy;
865 ED_space_image_get_zoom(sima, region, &zoomx, &zoomy);
866
867 float aspectRatio = width / float(height);
868 radius /= (width * zoomx);
869 const float radius_sq = radius * radius;
870
871 /* Allocate selection stack */
872 data->initial_stroke = static_cast<UVInitialStroke *>(
873 MEM_mallocN(sizeof(*data->initial_stroke), __func__));
874 if (!data->initial_stroke) {
876 }
877 data->initial_stroke->initialSelection = static_cast<UVInitialStrokeElement *>(MEM_mallocN(
878 sizeof(*data->initial_stroke->initialSelection) * data->totalUniqueUvs, __func__));
879 if (!data->initial_stroke->initialSelection) {
881 }
882
883 copy_v2_v2(data->initial_stroke->init_coord, co);
884
885 counter = 0;
886 for (int i = 0; i < data->totalUniqueUvs; i++) {
887 if (data->uv[i].is_locked) {
888 continue;
889 }
890
891 float diff[2];
892 sub_v2_v2v2(diff, data->uv[i].uv, co);
893 diff[1] /= aspectRatio;
894 float dist = dot_v2v2(diff, diff);
895 if (dist <= radius_sq) {
896 float strength;
897 strength = alpha * calc_strength(data, sqrtf(dist), radius);
898
899 data->initial_stroke->initialSelection[counter].uv = i;
900 data->initial_stroke->initialSelection[counter].strength = strength;
901 copy_v2_v2(data->initial_stroke->initialSelection[counter].initial_uv, data->uv[i].uv);
902 counter++;
903 }
904 }
905
906 data->initial_stroke->totalInitialSelected = counter;
907 if (sima->flag & SI_LIVE_UNWRAP) {
908 wmWindow *win_modal = CTX_wm_window(C);
909 ED_uvedit_live_unwrap_begin(scene, obedit, win_modal);
910 }
911 }
912
913 return static_cast<UvSculptData *>(op->customdata);
914}
915
916static int uv_sculpt_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
917{
919 Object *obedit = CTX_data_edit_object(C);
920
921 if (!(data = uv_sculpt_stroke_init(C, op, event))) {
922 return OPERATOR_CANCELLED;
923 }
924
925 uv_sculpt_stroke_apply(C, op, event, obedit);
926
927 data->timer = WM_event_timer_add(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.001f);
928
929 if (!data->timer) {
931 return OPERATOR_CANCELLED;
932 }
934
936}
937
938static int uv_sculpt_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
939{
940 UvSculptData *data = (UvSculptData *)op->customdata;
941 Object *obedit = CTX_data_edit_object(C);
942
943 switch (event->type) {
944 case LEFTMOUSE:
945 case MIDDLEMOUSE:
946 case RIGHTMOUSE:
948 return OPERATOR_FINISHED;
949
950 case MOUSEMOVE:
952 uv_sculpt_stroke_apply(C, op, event, obedit);
953 break;
954 case TIMER:
955 if (event->customdata == data->timer) {
956 uv_sculpt_stroke_apply(C, op, event, obedit);
957 }
958 break;
959 default:
961 }
962
965 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
967}
968
970{
971 PropertyRNA *prop;
972
973 prop = RNA_def_boolean(
974 ot->srna, "use_invert", false, "Invert", "Invert action for the duration of the stroke");
976}
977
979{
980 ot->name = "Grab UVs";
981 ot->description = "Grab UVs";
982 ot->idname = "SCULPT_OT_uv_sculpt_grab";
983
987
989
991}
992
994{
995 ot->name = "Relax UVs";
996 ot->description = "Relax UVs";
997 ot->idname = "SCULPT_OT_uv_sculpt_relax";
998
1002
1004
1006
1007 static const EnumPropertyItem relax_method_items[] = {
1009 "LAPLACIAN",
1010 0,
1011 "Laplacian",
1012 "Use Laplacian method for relaxation"},
1013 {UV_SCULPT_BRUSH_TYPE_RELAX_HC, "HC", 0, "HC", "Use HC method for relaxation"},
1015 "COTAN",
1016 0,
1017 "Geometry",
1018 "Use Geometry (cotangent) relaxation, making UVs follow the underlying 3D geometry"},
1019 {0, nullptr, 0, nullptr, nullptr},
1020 };
1021
1023 "relax_method",
1024 relax_method_items,
1026 "Relax Method",
1027 "Algorithm used for UV relaxation");
1028}
1029
1031{
1032 ot->name = "Pinch UVs";
1033 ot->description = "Pinch UVs";
1034 ot->idname = "SCULPT_OT_uv_sculpt_pinch";
1035
1039
1041
1043}
float BKE_brush_curve_strength(eBrushCurvePreset preset, const CurveMapping *cumap, float distance, float brush_radius)
Definition brush.cc:1388
void BKE_curvemapping_init(CurveMapping *cumap)
SpaceImage * CTX_wm_space_image(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
int BKE_image_find_nearest_tile_with_offset(const Image *image, const float co[2], float r_uv_offset[2]) ATTR_NONNULL(2
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:299
unsigned int BLI_ghashutil_uinthash(unsigned int key)
#define GHASH_ITER(gh_iter_, ghash_)
Definition BLI_ghash.h:322
GHash * BLI_ghash_new(GHashHashFP hashfp, GHashCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:686
unsigned int BLI_ghash_len(const GHash *gh) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:702
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:731
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.c:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.c:860
MINLINE float clamp_f(float value, float min, float max)
MINLINE float safe_divide(float a, float b)
float cotangent_tri_weight_v3(const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:196
MINLINE float len_squared_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
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 n[2])
unsigned int uint
#define CLAMP(a, b, c)
#define STREQ(a, b)
void DEG_id_tag_update(ID *id, unsigned int flags)
eBrushCurvePreset
@ CURVE_PRESET_SMOOTH
@ CD_PROP_FLOAT2
Object is a sort of wrapper for general info.
@ UV_SCULPT_ALL_ISLANDS
@ UV_SCULPT_LOCK_BORDERS
@ SI_CLIP_UV
@ SI_LIVE_UNWRAP
@ OPERATOR_RUNNING_MODAL
void ED_space_image_get_size(SpaceImage *sima, int *r_width, int *r_height)
void ED_space_image_get_zoom(SpaceImage *sima, const ARegion *region, float *r_zoomx, float *r_zoomy)
UvElement * BM_uv_element_get(const UvElementMap *element_map, const BMLoop *l)
void BM_uv_element_map_free(UvElementMap *element_map)
UvElementMap * BM_uv_element_map_create(BMesh *bm, const Scene *scene, bool uv_selected, bool use_winding, bool use_seams, bool do_islands)
UvElement ** BM_uv_element_map_ensure_head_table(UvElementMap *element_map)
bool ED_operator_uvedit_space_image(bContext *C)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit, struct wmWindow *win_modal)
void ED_uvedit_live_unwrap_re_solve()
void ED_uvedit_live_unwrap_end(bool cancel)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
PropertyFlag
Definition RNA_types.hh:201
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1663
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
#define BM_ELEM_CD_GET_BOOL(ele, offset)
#define BM_ELEM_CD_GET_FLOAT2_P(ele, offset)
#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 BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert * v
BMUVOffsets BM_uv_map_get_offsets(const BMesh *bm)
local_group_size(16, 16) .push_constant(Type b
#define fabsf(x)
#define sqrtf(x)
int len
draw_view in_light_buf[] float
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
CCL_NAMESPACE_BEGIN ccl_device float invert(float color, float factor)
Definition invert.h:9
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void SCULPT_OT_uv_sculpt_relax(wmOperatorType *ot)
Definition sculpt_uv.cc:993
static int uv_sculpt_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
Definition sculpt_uv.cc:938
static void laplacian_relaxation_iteration_uv(UvSculptData *sculptdata, const int cd_loop_uv_offset, const float mouse_coord[2], const float alpha, const float radius_sq, const float aspectRatio)
Definition sculpt_uv.cc:267
static void register_common_props(wmOperatorType *ot)
Definition sculpt_uv.cc:969
@ UV_SCULPT_BRUSH_TYPE_RELAX_HC
Definition sculpt_uv.cc:58
@ UV_SCULPT_BRUSH_TYPE_RELAX_COTAN
Definition sculpt_uv.cc:59
@ UV_SCULPT_BRUSH_TYPE_RELAX_LAPLACIAN
Definition sculpt_uv.cc:57
static UvSculptData * uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wmEvent *event)
Definition sculpt_uv.cc:650
static void relaxation_iteration_uv(UvSculptData *sculptdata, const int cd_loop_uv_offset, const float mouse_coord[2], const float alpha, const float radius_sq, const float aspect_ratio, const int method)
Definition sculpt_uv.cc:378
static void add_weighted_edge(float(*delta_buf)[3], const UvElement *storage, const UvElement *ele_next, const UvElement *ele_prev, const float luv_next[2], const float luv_prev[2], const float weight)
Definition sculpt_uv.cc:337
static bool uv_edge_compare(const void *a, const void *b)
Definition sculpt_uv.cc:628
static int uv_sculpt_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition sculpt_uv.cc:916
eBrushUVSculptTool
Definition sculpt_uv.cc:50
@ UV_SCULPT_BRUSH_TYPE_GRAB
Definition sculpt_uv.cc:51
@ UV_SCULPT_BRUSH_TYPE_PINCH
Definition sculpt_uv.cc:53
@ UV_SCULPT_BRUSH_TYPE_RELAX
Definition sculpt_uv.cc:52
static float tri_weight_v3(int method, const float *v1, const float *v2, const float *v3)
Definition sculpt_uv.cc:364
static void apply_sculpt_data_constraints(UvSculptData *sculptdata, float uv[2])
Definition sculpt_uv.cc:149
static int uv_element_offset_from_face_get(UvElementMap *map, BMLoop *l, int island_index, const bool do_islands)
Definition sculpt_uv.cc:610
static void HC_relaxation_iteration_uv(UvSculptData *sculptdata, const int cd_loop_uv_offset, const float mouse_coord[2], const float alpha, const float radius_sq, const float aspectRatio)
Definition sculpt_uv.cc:182
static void uv_sculpt_stroke_apply(bContext *C, wmOperator *op, const wmEvent *event, Object *obedit)
Definition sculpt_uv.cc:479
static uint uv_edge_hash(const void *key)
Definition sculpt_uv.cc:622
void SCULPT_OT_uv_sculpt_pinch(wmOperatorType *ot)
#define MARK_BOUNDARY
Definition sculpt_uv.cc:65
static void uv_sculpt_stroke_exit(bContext *C, wmOperator *op)
Definition sculpt_uv.cc:587
static void set_element_flag(UvElement *element, const int flag)
Definition sculpt_uv.cc:639
void SCULPT_OT_uv_sculpt_grab(wmOperatorType *ot)
Definition sculpt_uv.cc:978
static float calc_strength(const UvSculptData *sculptdata, float p, const float len)
Definition sculpt_uv.cc:160
struct BMVert * v
struct BMLoop * prev
struct BMLoop * next
float co[3]
int totvert
CustomData ldata
Definition DNA_ID.h:413
struct Image * image
float p[2]
Definition sculpt_uv.cc:178
float b[2]
Definition sculpt_uv.cc:178
float sum_b[2]
Definition sculpt_uv.cc:178
float sum_co[2]
Definition sculpt_uv.cc:178
float init_coord[2]
Definition sculpt_uv.cc:104
UVInitialStrokeElement * initialSelection
Definition sculpt_uv.cc:98
UvElement * element
Definition sculpt_uv.cc:69
uint uv1
Definition sculpt_uv.cc:79
bool is_interior
Definition sculpt_uv.cc:82
uint uv2
Definition sculpt_uv.cc:80
UvElement * storage
unsigned char flag
UvElement * next
float uv_base_offset[2]
Definition sculpt_uv.cc:146
bool constrain_to_bounds
Definition sculpt_uv.cc:143
UVInitialStroke * initial_stroke
Definition sculpt_uv.cc:125
UvElementMap * elementMap
Definition sculpt_uv.cc:131
UvAdjacencyElement * uv
Definition sculpt_uv.cc:113
UvEdge * uvedges
Definition sculpt_uv.cc:119
wmTimer * timer
Definition sculpt_uv.cc:128
UvSculpt * uvsculpt
Definition sculpt_uv.cc:134
int8_t curve_preset
struct CurveMapping * strength_curve
int mval[2]
Definition WM_types.hh:728
short type
Definition WM_types.hh:722
void * customdata
Definition WM_types.hh:772
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct wmOperatorType * type
struct PointerRNA * ptr
bool uv_find_nearest_vert(Scene *scene, Object *obedit, const float co[2], float penalty_dist, UvNearestHit *hit)
UvNearestHit uv_nearest_hit_init_max(const View2D *v2d)
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.cc:4125
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const int event_type, const double time_step)
uint8_t flag
Definition wm_window.cc:138