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) 2001-2002 by NaN Holding BV.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup edtransform
22 */
23
24 #include "DNA_meshdata_types.h"
25
26 #include "MEM_guardedalloc.h"
27
28 #include "BLI_bitmap.h"
29 #include "BLI_linklist_stack.h"
30 #include "BLI_math.h"
31
32 #include "BKE_context.h"
33 #include "BKE_editmesh.h"
34 #include "BKE_mesh_mapping.h"
35
36 #include "ED_image.h"
37 #include "ED_mesh.h"
38 #include "ED_uvedit.h"
39
40 #include "WM_api.h" /* for WM_event_add_notifier to deal with stabilization nodes */
41
42 #include "transform.h"
43 #include "transform_convert.h"
44
45 /* -------------------------------------------------------------------- */
46 /** \name UVs Transform Creation
47 *
48 * \{ */
49
UVsToTransData(const float aspect[2],TransData * td,TransData2D * td2d,float * uv,const float * center,float calc_dist,bool selected)50 static void UVsToTransData(const float aspect[2],
51 TransData *td,
52 TransData2D *td2d,
53 float *uv,
54 const float *center,
55 float calc_dist,
56 bool selected)
57 {
58 /* UV coords are scaled by aspects. this is needed for rotations and
59 * proportional editing to be consistent with the stretched UV coords
60 * that are displayed. this also means that for display and number-input,
61 * and when the UV coords are flushed, these are converted each time. */
62 td2d->loc[0] = uv[0] * aspect[0];
63 td2d->loc[1] = uv[1] * aspect[1];
64 td2d->loc[2] = 0.0f;
65 td2d->loc2d = uv;
66
67 td->flag = 0;
68 td->loc = td2d->loc;
69 copy_v2_v2(td->center, center ? center : td->loc);
70 td->center[2] = 0.0f;
71 copy_v3_v3(td->iloc, td->loc);
72
73 memset(td->axismtx, 0, sizeof(td->axismtx));
74 td->axismtx[2][2] = 1.0f;
75
76 td->ext = NULL;
77 td->val = NULL;
78
79 if (selected) {
80 td->flag |= TD_SELECTED;
81 td->dist = 0.0;
82 }
83 else {
84 td->dist = calc_dist;
85 }
86 unit_m3(td->mtx);
87 unit_m3(td->smtx);
88 }
89
90 /**
91 * \param dists: Store the closest connected distance to selected vertices.
92 */
uv_set_connectivity_distance(BMesh * bm,float * dists,const float aspect[2])93 static void uv_set_connectivity_distance(BMesh *bm, float *dists, const float aspect[2])
94 {
95 /* Mostly copied from #editmesh_set_connectivity_distance. */
96 BLI_LINKSTACK_DECLARE(queue, BMLoop *);
97
98 /* Any BM_ELEM_TAG'd loop is added to 'queue_next', this makes sure that we don't add things
99 * twice. */
100 BLI_LINKSTACK_DECLARE(queue_next, BMLoop *);
101
102 BLI_LINKSTACK_INIT(queue);
103 BLI_LINKSTACK_INIT(queue_next);
104
105 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
106 BMIter fiter, liter;
107 BMVert *f;
108 BMLoop *l;
109
110 BM_mesh_elem_index_ensure(bm, BM_LOOP);
111
112 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
113 /* Visible faces was tagged in #createTransUVs. */
114 if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
115 continue;
116 }
117
118 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
119 float dist;
120 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
121
122 bool uv_vert_sel = luv->flag & MLOOPUV_VERTSEL;
123
124 if (uv_vert_sel) {
125 BLI_LINKSTACK_PUSH(queue, l);
126 dist = 0.0f;
127 }
128 else {
129 dist = FLT_MAX;
130 }
131
132 /* Make sure all loops are in a clean tag state. */
133 BLI_assert(BM_elem_flag_test(l, BM_ELEM_TAG) == 0);
134
135 int loop_idx = BM_elem_index_get(l);
136
137 dists[loop_idx] = dist;
138 }
139 }
140
141 /* Need to be very careful of feedback loops here, store previous dist's to avoid feedback. */
142 float *dists_prev = MEM_dupallocN(dists);
143
144 do {
145 while ((l = BLI_LINKSTACK_POP(queue))) {
146 BLI_assert(dists[BM_elem_index_get(l)] != FLT_MAX);
147
148 BMLoop *l_other, *l_connected;
149 BMIter l_connected_iter;
150
151 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
152 float l_uv[2];
153
154 copy_v2_v2(l_uv, luv->uv);
155 mul_v2_v2(l_uv, aspect);
156
157 BM_ITER_ELEM (l_other, &liter, l->f, BM_LOOPS_OF_FACE) {
158 if (l_other == l) {
159 continue;
160 }
161 float other_uv[2], edge_vec[2];
162 MLoopUV *luv_other = BM_ELEM_CD_GET_VOID_P(l_other, cd_loop_uv_offset);
163
164 copy_v2_v2(other_uv, luv_other->uv);
165 mul_v2_v2(other_uv, aspect);
166
167 sub_v2_v2v2(edge_vec, l_uv, other_uv);
168
169 const int i = BM_elem_index_get(l);
170 const int i_other = BM_elem_index_get(l_other);
171 float dist = len_v2(edge_vec) + dists_prev[i];
172
173 if (dist < dists[i_other]) {
174 dists[i_other] = dist;
175 }
176 else {
177 /* The face loop already has a shorter path to it. */
178 continue;
179 }
180
181 bool other_vert_sel, connected_vert_sel;
182
183 other_vert_sel = luv_other->flag & MLOOPUV_VERTSEL;
184
185 BM_ITER_ELEM (l_connected, &l_connected_iter, l_other->v, BM_LOOPS_OF_VERT) {
186 if (l_connected == l_other) {
187 continue;
188 }
189 /* Visible faces was tagged in #createTransUVs. */
190 if (!BM_elem_flag_test(l_connected->f, BM_ELEM_TAG)) {
191 continue;
192 }
193
194 MLoopUV *luv_connected = BM_ELEM_CD_GET_VOID_P(l_connected, cd_loop_uv_offset);
195 connected_vert_sel = luv_connected->flag & MLOOPUV_VERTSEL;
196
197 /* Check if this loop is connected in UV space.
198 * If the uv loops share the same selection state (if not, they are not connected as
199 * they have been ripped or other edit commands have separated them). */
200 bool connected = other_vert_sel == connected_vert_sel &&
201 equals_v2v2(luv_other->uv, luv_connected->uv);
202 if (!connected) {
203 continue;
204 }
205
206 /* The loop vert is occupying the same space, so it has the same distance. */
207 const int i_connected = BM_elem_index_get(l_connected);
208 dists[i_connected] = dist;
209
210 if (BM_elem_flag_test(l_connected, BM_ELEM_TAG) == 0) {
211 BM_elem_flag_enable(l_connected, BM_ELEM_TAG);
212 BLI_LINKSTACK_PUSH(queue_next, l_connected);
213 }
214 }
215 }
216 }
217
218 /* Clear elem flags for the next loop. */
219 for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) {
220 BMLoop *l_link = lnk->link;
221 const int i = BM_elem_index_get(l_link);
222
223 BM_elem_flag_disable(l_link, BM_ELEM_TAG);
224
225 /* Store all new dist values. */
226 dists_prev[i] = dists[i];
227 }
228
229 BLI_LINKSTACK_SWAP(queue, queue_next);
230
231 } while (BLI_LINKSTACK_SIZE(queue));
232
233 #ifndef NDEBUG
234 /* Check that we didn't leave any loops tagged */
235 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
236 /* Visible faces was tagged in #createTransUVs. */
237 if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
238 continue;
239 }
240
241 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
242 BLI_assert(BM_elem_flag_test(l, BM_ELEM_TAG) == 0);
243 }
244 }
245 #endif
246
247 BLI_LINKSTACK_FREE(queue);
248 BLI_LINKSTACK_FREE(queue_next);
249
250 MEM_freeN(dists_prev);
251 }
252
createTransUVs(bContext * C,TransInfo * t)253 void createTransUVs(bContext *C, TransInfo *t)
254 {
255 SpaceImage *sima = CTX_wm_space_image(C);
256 Scene *scene = t->scene;
257 ToolSettings *ts = CTX_data_tool_settings(C);
258
259 const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
260 const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0;
261 const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS);
262
263 FOREACH_TRANS_DATA_CONTAINER (t, tc) {
264
265 TransData *td = NULL;
266 TransData2D *td2d = NULL;
267 BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
268 BMFace *efa;
269 BMIter iter, liter;
270 UvElementMap *elementmap = NULL;
271 struct {
272 float co[2];
273 int co_num;
274 } *island_center = NULL;
275 int count = 0, countsel = 0;
276 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
277
278 if (!ED_space_image_show_uvedit(sima, tc->obedit)) {
279 continue;
280 }
281
282 /* count */
283 if (is_island_center) {
284 /* create element map with island information */
285 const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0;
286 elementmap = BM_uv_element_map_create(em->bm, scene, use_facesel, true, false, true);
287 if (elementmap == NULL) {
288 continue;
289 }
290
291 island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__);
292 }
293
294 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
295 BMLoop *l;
296
297 if (!uvedit_face_visible_test(scene, efa)) {
298 BM_elem_flag_disable(efa, BM_ELEM_TAG);
299 continue;
300 }
301
302 BM_elem_flag_enable(efa, BM_ELEM_TAG);
303 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
304 /* Make sure that the loop element flag is cleared for when we use it in
305 * uv_set_connectivity_distance later. */
306 BM_elem_flag_disable(l, BM_ELEM_TAG);
307 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
308 countsel++;
309
310 if (island_center) {
311 UvElement *element = BM_uv_element_get(elementmap, efa, l);
312
313 if (element->flag == false) {
314 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
315 add_v2_v2(island_center[element->island].co, luv->uv);
316 island_center[element->island].co_num++;
317 element->flag = true;
318 }
319 }
320 }
321
322 if (is_prop_edit) {
323 count++;
324 }
325 }
326 }
327
328 float *prop_dists = NULL;
329
330 /* Support other objects using PET to adjust these, unless connected is enabled. */
331 if (((is_prop_edit && !is_prop_connected) ? count : countsel) == 0) {
332 goto finally;
333 }
334
335 if (is_island_center) {
336 int i;
337
338 for (i = 0; i < elementmap->totalIslands; i++) {
339 mul_v2_fl(island_center[i].co, 1.0f / island_center[i].co_num);
340 mul_v2_v2(island_center[i].co, t->aspect);
341 }
342 }
343
344 tc->data_len = (is_prop_edit) ? count : countsel;
345 tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(UV Editing)");
346 /* for each 2d uv coord a 3d vector is allocated, so that they can be
347 * treated just as if they were 3d verts */
348 tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransObData2D(UV Editing)");
349
350 if (sima->flag & SI_CLIP_UV) {
351 t->flag |= T_CLIP_UV;
352 }
353
354 td = tc->data;
355 td2d = tc->data_2d;
356
357 if (is_prop_connected) {
358 prop_dists = MEM_callocN(em->bm->totloop * sizeof(float), "TransObPropDists(UV Editing)");
359
360 uv_set_connectivity_distance(em->bm, prop_dists, t->aspect);
361 }
362
363 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
364 BMLoop *l;
365
366 if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) {
367 continue;
368 }
369
370 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
371 const bool selected = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
372 MLoopUV *luv;
373 const float *center = NULL;
374 float prop_distance = FLT_MAX;
375
376 if (!is_prop_edit && !selected) {
377 continue;
378 }
379
380 if (is_prop_connected) {
381 const int idx = BM_elem_index_get(l);
382 prop_distance = prop_dists[idx];
383 }
384
385 if (is_island_center) {
386 UvElement *element = BM_uv_element_get(elementmap, efa, l);
387 if (element) {
388 center = island_center[element->island].co;
389 }
390 }
391
392 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
393 UVsToTransData(t->aspect, td++, td2d++, luv->uv, center, prop_distance, selected);
394 }
395 }
396
397 if (sima->flag & SI_LIVE_UNWRAP) {
398 ED_uvedit_live_unwrap_begin(t->scene, tc->obedit);
399 }
400
401 finally:
402 if (is_prop_connected) {
403 MEM_SAFE_FREE(prop_dists);
404 }
405 if (is_island_center) {
406 BM_uv_element_map_free(elementmap);
407
408 MEM_freeN(island_center);
409 }
410 }
411 }
412
413 /** \} */
414
415 /* -------------------------------------------------------------------- */
416 /** \name UVs Transform Flush
417 *
418 * \{ */
419
flushTransUVs(TransInfo * t)420 static void flushTransUVs(TransInfo *t)
421 {
422 SpaceImage *sima = t->area->spacedata.first;
423 const bool use_pixel_snap = ((sima->pixel_snap_mode != SI_PIXEL_SNAP_DISABLED) &&
424 (t->state != TRANS_CANCEL));
425
426 FOREACH_TRANS_DATA_CONTAINER (t, tc) {
427 TransData2D *td;
428 int a;
429 float aspect_inv[2], size[2];
430
431 aspect_inv[0] = 1.0f / t->aspect[0];
432 aspect_inv[1] = 1.0f / t->aspect[1];
433
434 if (use_pixel_snap) {
435 int size_i[2];
436 ED_space_image_get_size(sima, &size_i[0], &size_i[1]);
437 size[0] = size_i[0];
438 size[1] = size_i[1];
439 }
440
441 /* flush to 2d vector from internally used 3d vector */
442 for (a = 0, td = tc->data_2d; a < tc->data_len; a++, td++) {
443 td->loc2d[0] = td->loc[0] * aspect_inv[0];
444 td->loc2d[1] = td->loc[1] * aspect_inv[1];
445
446 if (use_pixel_snap) {
447 td->loc2d[0] *= size[0];
448 td->loc2d[1] *= size[1];
449
450 switch (sima->pixel_snap_mode) {
451 case SI_PIXEL_SNAP_CENTER:
452 td->loc2d[0] = roundf(td->loc2d[0] - 0.5f) + 0.5f;
453 td->loc2d[1] = roundf(td->loc2d[1] - 0.5f) + 0.5f;
454 break;
455 case SI_PIXEL_SNAP_CORNER:
456 td->loc2d[0] = roundf(td->loc2d[0]);
457 td->loc2d[1] = roundf(td->loc2d[1]);
458 break;
459 }
460
461 td->loc2d[0] /= size[0];
462 td->loc2d[1] /= size[1];
463 }
464 }
465 }
466 }
467
468 /* helper for recalcData() - for Image Editor transforms */
recalcData_uv(TransInfo * t)469 void recalcData_uv(TransInfo *t)
470 {
471 SpaceImage *sima = t->area->spacedata.first;
472
473 flushTransUVs(t);
474 if (sima->flag & SI_LIVE_UNWRAP) {
475 ED_uvedit_live_unwrap_re_solve();
476 }
477
478 FOREACH_TRANS_DATA_CONTAINER (t, tc) {
479 if (tc->data_len) {
480 DEG_id_tag_update(tc->obedit->data, 0);
481 }
482 }
483 }
484
485 /** \} */
486