Blender V4.3
transform_convert_mesh_uv.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "MEM_guardedalloc.h"
10
11#include "BLI_linklist_stack.h"
12#include "BLI_math_geom.h"
13#include "BLI_math_matrix.h"
14#include "BLI_math_vector.h"
15
16#include "BKE_context.hh"
17#include "BKE_customdata.hh"
18#include "BKE_editmesh.hh"
19#include "BKE_mesh_mapping.hh"
20
21#include "ED_image.hh"
22#include "ED_mesh.hh"
23#include "ED_uvedit.hh"
24
25#include "WM_api.hh" /* For #WM_event_add_notifier to deal with stabilization nodes. */
26
27#include "transform.hh"
28#include "transform_convert.hh"
29
30using namespace blender;
31
32/* -------------------------------------------------------------------- */
36static void UVsToTransData(const float aspect[2],
37 float *uv,
38 const float *center,
39 const float calc_dist,
40 const bool selected,
41 BMLoop *l,
42 TransData *r_td,
43 TransData2D *r_td2d)
44{
45 /* UV coords are scaled by aspects. this is needed for rotations and
46 * proportional editing to be consistent with the stretched UV coords
47 * that are displayed. this also means that for display and number-input,
48 * and when the UV coords are flushed, these are converted each time. */
49 r_td2d->loc[0] = uv[0] * aspect[0];
50 r_td2d->loc[1] = uv[1] * aspect[1];
51 r_td2d->loc[2] = 0.0f;
52 r_td2d->loc2d = uv;
53
54 r_td->flag = 0;
55 r_td->loc = r_td2d->loc;
56 copy_v2_v2(r_td->center, center ? center : r_td->loc);
57 r_td->center[2] = 0.0f;
58 copy_v3_v3(r_td->iloc, r_td->loc);
59
60 memset(r_td->axismtx, 0, sizeof(r_td->axismtx));
61 r_td->axismtx[2][2] = 1.0f;
62
63 r_td->ext = nullptr;
64 r_td->val = nullptr;
65
66 if (selected) {
67 r_td->flag |= TD_SELECTED;
68 r_td->dist = 0.0;
69 }
70 else {
71 r_td->dist = calc_dist;
72 }
73 unit_m3(r_td->mtx);
74 unit_m3(r_td->smtx);
75 r_td->extra = l;
76}
77
82 BMesh *bm,
83 float *dists,
84 const float aspect[2])
85{
86#define TMP_LOOP_SELECT_TAG BM_ELEM_TAG_ALT
87 /* Mostly copied from #transform_convert_mesh_connectivity_distance. */
89
90 /* Any BM_ELEM_TAG'd loop is added to 'queue_next', this makes sure that we don't add things
91 * twice. */
92 BLI_LINKSTACK_DECLARE(queue_next, BMLoop *);
93
94 BLI_LINKSTACK_INIT(queue);
95 BLI_LINKSTACK_INIT(queue_next);
96
97 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
98
99 BMIter fiter, liter;
100 BMVert *f;
101 BMLoop *l;
102
104
105 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
106 /* Visible faces was tagged in #createTransUVs. */
108 continue;
109 }
110
111 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
112 float dist;
113 bool uv_vert_sel = uvedit_uv_select_test_ex(ts, l, offsets);
114
115 if (uv_vert_sel) {
116 BLI_LINKSTACK_PUSH(queue, l);
118 dist = 0.0f;
119 }
120 else {
122 dist = FLT_MAX;
123 }
124
125 /* Make sure all loops are in a clean tag state. */
127
128 int loop_idx = BM_elem_index_get(l);
129
130 dists[loop_idx] = dist;
131 }
132 }
133
134 /* Need to be very careful of feedback loops here, store previous dist's to avoid feedback. */
135 float *dists_prev = static_cast<float *>(MEM_dupallocN(dists));
136
137 do {
138 while ((l = BLI_LINKSTACK_POP(queue))) {
140
141 BMLoop *l_other, *l_connected;
142 BMIter l_connected_iter;
143
144 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
145 float l_uv[2];
146
147 copy_v2_v2(l_uv, luv);
148 mul_v2_v2(l_uv, aspect);
149
150 BM_ITER_ELEM (l_other, &liter, l->f, BM_LOOPS_OF_FACE) {
151 if (l_other == l) {
152 continue;
153 }
154 float other_uv[2], edge_vec[2];
155 float *luv_other = BM_ELEM_CD_GET_FLOAT_P(l_other, offsets.uv);
156
157 copy_v2_v2(other_uv, luv_other);
158 mul_v2_v2(other_uv, aspect);
159
160 sub_v2_v2v2(edge_vec, l_uv, other_uv);
161
162 const int i = BM_elem_index_get(l);
163 const int i_other = BM_elem_index_get(l_other);
164 float dist = len_v2(edge_vec) + dists_prev[i];
165
166 if (dist < dists[i_other]) {
167 dists[i_other] = dist;
168 }
169 else {
170 /* The face loop already has a shorter path to it. */
171 continue;
172 }
173
174 bool other_vert_sel, connected_vert_sel;
175
176 other_vert_sel = BM_elem_flag_test_bool(l_other, TMP_LOOP_SELECT_TAG);
177
178 BM_ITER_ELEM (l_connected, &l_connected_iter, l_other->v, BM_LOOPS_OF_VERT) {
179 if (l_connected == l_other) {
180 continue;
181 }
182 /* Visible faces was tagged in #createTransUVs. */
183 if (!BM_elem_flag_test(l_connected->f, BM_ELEM_TAG)) {
184 continue;
185 }
186
187 float *luv_connected = BM_ELEM_CD_GET_FLOAT_P(l_connected, offsets.uv);
188 connected_vert_sel = BM_elem_flag_test_bool(l_connected, TMP_LOOP_SELECT_TAG);
189
190 /* Check if this loop is connected in UV space.
191 * If the uv loops share the same selection state (if not, they are not connected as
192 * they have been ripped or other edit commands have separated them). */
193 bool connected = other_vert_sel == connected_vert_sel &&
194 equals_v2v2(luv_other, luv_connected);
195 if (!connected) {
196 continue;
197 }
198
199 /* The loop vert is occupying the same space, so it has the same distance. */
200 const int i_connected = BM_elem_index_get(l_connected);
201 dists[i_connected] = dist;
202
203 if (BM_elem_flag_test(l_connected, BM_ELEM_TAG) == 0) {
204 BM_elem_flag_enable(l_connected, BM_ELEM_TAG);
205 BLI_LINKSTACK_PUSH(queue_next, l_connected);
206 }
207 }
208 }
209 }
210
211 /* Clear elem flags for the next loop. */
212 for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) {
213 BMLoop *l_link = static_cast<BMLoop *>(lnk->link);
214 const int i = BM_elem_index_get(l_link);
215
217
218 /* Store all new dist values. */
219 dists_prev[i] = dists[i];
220 }
221
222 BLI_LINKSTACK_SWAP(queue, queue_next);
223
224 } while (BLI_LINKSTACK_SIZE(queue));
225
226#ifndef NDEBUG
227 /* Check that we didn't leave any loops tagged. */
228 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
229 /* Visible faces was tagged in #createTransUVs. */
231 continue;
232 }
233
234 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
236 }
237 }
238#endif
239
240 BLI_LINKSTACK_FREE(queue);
241 BLI_LINKSTACK_FREE(queue_next);
242
243 MEM_freeN(dists_prev);
244#undef TMP_LOOP_SELECT_TAG
245}
246
248{
250 Scene *scene = t->scene;
251
252 const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
253 const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0;
254 const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS);
255
257
258 TransData *td = nullptr;
259 TransData2D *td2d = nullptr;
260 BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
261 BMFace *efa;
262 BMIter iter, liter;
263 UvElementMap *elementmap = nullptr;
264 struct IslandCenter {
265 float co[2];
266 int co_num;
267 } *island_center = nullptr;
268 int count = 0, countsel = 0;
269 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
270
271 if (!ED_space_image_show_uvedit(sima, tc->obedit)) {
272 continue;
273 }
274
275 /* Count. */
276 if (is_island_center) {
277 /* Create element map with island information. */
278 elementmap = BM_uv_element_map_create(em->bm, scene, true, false, true, true);
279 if (elementmap == nullptr) {
280 continue;
281 }
282
283 island_center = static_cast<IslandCenter *>(
284 MEM_callocN(sizeof(*island_center) * elementmap->total_islands, __func__));
285 }
286
287 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
288 BMLoop *l;
289
290 if (!uvedit_face_visible_test(scene, efa)) {
292 continue;
293 }
294
296 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
297 /* Make sure that the loop element flag is cleared for when we use it in
298 * uv_set_connectivity_distance later. */
300 if (uvedit_uv_select_test(scene, l, offsets)) {
301 countsel++;
302
303 if (island_center) {
304 UvElement *element = BM_uv_element_get(elementmap, l);
305
306 if (element && !element->flag) {
307 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
308 add_v2_v2(island_center[element->island].co, luv);
309 island_center[element->island].co_num++;
310 element->flag = true;
311 }
312 }
313 }
314
315 if (is_prop_edit) {
316 count++;
317 }
318 }
319 }
320
321 float *prop_dists = nullptr;
322
323 /* Support other objects using proportional editing to adjust these, unless connected is
324 * enabled. */
325 if (((is_prop_edit && !is_prop_connected) ? count : countsel) == 0) {
326 goto finally;
327 }
328
329 if (is_island_center) {
330 for (int i = 0; i < elementmap->total_islands; i++) {
331 mul_v2_fl(island_center[i].co, 1.0f / island_center[i].co_num);
332 mul_v2_v2(island_center[i].co, t->aspect);
333 }
334 }
335
336 tc->data_len = (is_prop_edit) ? count : countsel;
337 tc->data = static_cast<TransData *>(
338 MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(UV Editing)"));
339 /* For each 2d uv coord a 3d vector is allocated, so that they can be
340 * treated just as if they were 3d verts. */
341 tc->data_2d = static_cast<TransData2D *>(
342 MEM_callocN(tc->data_len * sizeof(TransData2D), "TransObData2D(UV Editing)"));
343
344 if (sima->flag & SI_CLIP_UV) {
345 t->flag |= T_CLIP_UV;
346 }
347
348 td = tc->data;
349 td2d = tc->data_2d;
350
351 if (is_prop_connected) {
352 prop_dists = static_cast<float *>(
353 MEM_callocN(em->bm->totloop * sizeof(float), "TransObPropDists(UV Editing)"));
354
355 uv_set_connectivity_distance(t->settings, em->bm, prop_dists, t->aspect);
356 }
357
358 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
359 BMLoop *l;
360
361 if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) {
362 continue;
363 }
364
365 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
366 const bool selected = uvedit_uv_select_test(scene, l, offsets);
367 float(*luv)[2];
368 const float *center = nullptr;
369 float prop_distance = FLT_MAX;
370
371 if (!is_prop_edit && !selected) {
372 continue;
373 }
374
375 if (is_prop_connected) {
376 const int idx = BM_elem_index_get(l);
377 prop_distance = prop_dists[idx];
378 }
379
380 if (is_island_center) {
381 UvElement *element = BM_uv_element_get(elementmap, l);
382 if (element) {
383 center = island_center[element->island].co;
384 }
385 }
386
387 luv = (float(*)[2])BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
388 UVsToTransData(t->aspect, *luv, center, prop_distance, selected, l, td++, td2d++);
389 }
390 }
391
392 if (sima->flag & SI_LIVE_UNWRAP) {
393 wmWindow *win_modal = CTX_wm_window(C);
394 ED_uvedit_live_unwrap_begin(t->scene, tc->obedit, win_modal);
395 }
396
397 finally:
398 if (is_prop_connected) {
399 MEM_SAFE_FREE(prop_dists);
400 }
401 if (is_island_center) {
402 BM_uv_element_map_free(elementmap);
403
404 MEM_freeN(island_center);
405 }
406 }
407}
408
411/* -------------------------------------------------------------------- */
416{
417 SpaceImage *sima = static_cast<SpaceImage *>(t->area->spacedata.first);
418 const bool use_pixel_round = ((sima->pixel_round_mode != SI_PIXEL_ROUND_DISABLED) &&
419 (t->state != TRANS_CANCEL));
420
422 TransData2D *td;
423 int a;
424 float aspect_inv[2], size[2];
425
426 aspect_inv[0] = 1.0f / t->aspect[0];
427 aspect_inv[1] = 1.0f / t->aspect[1];
428
429 if (use_pixel_round) {
430 int size_i[2];
431 ED_space_image_get_size(sima, &size_i[0], &size_i[1]);
432 size[0] = size_i[0];
433 size[1] = size_i[1];
434 }
435
436 /* Flush to 2d vector from internally used 3d vector. */
437 for (a = 0, td = tc->data_2d; a < tc->data_len; a++, td++) {
438 td->loc2d[0] = td->loc[0] * aspect_inv[0];
439 td->loc2d[1] = td->loc[1] * aspect_inv[1];
440
441 if (use_pixel_round) {
442 td->loc2d[0] *= size[0];
443 td->loc2d[1] *= size[1];
444
445 switch (sima->pixel_round_mode) {
447 td->loc2d[0] = roundf(td->loc2d[0] - 0.5f) + 0.5f;
448 td->loc2d[1] = roundf(td->loc2d[1] - 0.5f) + 0.5f;
449 break;
451 td->loc2d[0] = roundf(td->loc2d[0]);
452 td->loc2d[1] = roundf(td->loc2d[1]);
453 break;
454 }
455
456 td->loc2d[0] /= size[0];
457 td->loc2d[1] /= size[1];
458 }
459 }
460 }
461}
462
464{
465 SpaceImage *sima = static_cast<SpaceImage *>(t->area->spacedata.first);
466
467 flushTransUVs(t);
468 if (sima->flag & SI_LIVE_UNWRAP) {
470 }
471
473 if (tc->data_len) {
474 DEG_id_tag_update(static_cast<ID *>(tc->obedit->data), ID_RECALC_GEOMETRY);
475 }
476 }
477}
478
481/* -------------------------------------------------------------------- */
485struct UVGroups {
487
488 private:
489 Vector<int> groups_offs_buffer_;
490 Vector<int> groups_offs_indices_;
491
492 public:
493 void init(const TransDataContainer *tc, BMesh *bm, const BMUVOffsets &offsets)
494 {
495 /* To identify #TransData by the corner, we first need to set all values in `index` to `-1`. */
496 BMIter fiter;
497 BMIter liter;
498 BMFace *f;
499 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
500 BMLoop *l;
501 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
502 BM_elem_index_set(l, -1);
503 }
504 }
505
506 /* Now, count and set the index for the corners being transformed. */
507 this->sd_len = 0;
508 TransData *td = tc->data;
509 for (int i = 0; i < tc->data_len; i++, td++) {
510 if (!(td->flag & TD_SELECTED)) {
511 /* The selected ones are sorted at the beginning. */
512 break;
513 }
514 this->sd_len++;
515
516 BMLoop *l = static_cast<BMLoop *>(td->extra);
518 }
520
521 /* Create the groups. */
522 groups_offs_buffer_.reserve(this->sd_len);
523 groups_offs_indices_.reserve((this->sd_len / 4) + 2);
524
525 td = tc->data;
526 for (int i = 0; i < tc->data_len; i++, td++) {
527 BMLoop *l_orig = static_cast<BMLoop *>(td->extra);
528 if (BM_elem_index_get(l_orig) == -1) {
529 /* Already added to a group. */
530 continue;
531 }
532
533 const float2 &uv_orig = BM_ELEM_CD_GET_FLOAT_P(l_orig, offsets.uv);
534 groups_offs_indices_.append(groups_offs_buffer_.size());
535
536 BMIter liter;
537 BMLoop *l_iter;
538 BM_ITER_ELEM (l_iter, &liter, l_orig->v, BM_LOOPS_OF_VERT) {
539 if (BM_elem_index_get(l_iter) == -1) {
540 /* Already added to a group or not participating in the transformation. */
541 continue;
542 }
543
544 if (l_orig != l_iter &&
545 !compare_v2v2(uv_orig, BM_ELEM_CD_GET_FLOAT_P(l_iter, offsets.uv), FLT_EPSILON))
546 {
547 /* Non-connected. */
548 continue;
549 }
550
551 groups_offs_buffer_.append(BM_elem_index_get(l_iter));
552 BM_elem_index_set(l_iter, -1);
553 }
554 }
555 groups_offs_indices_.append(groups_offs_buffer_.size());
556 }
557
559 {
560 return OffsetIndices<int>(groups_offs_indices_);
561 }
562
563 Span<int> td_indices_get(const int group_index) const
564 {
565 return groups_offs_buffer_.as_span().slice(this->groups()[group_index]);
566 }
567
569 {
570 Array<TransDataVertSlideVert> r_sv(this->sd_len);
571 TransDataVertSlideVert *sv = &r_sv[0];
572 for (const int group_index : this->groups().index_range()) {
573 for (int td_index : this->td_indices_get(group_index)) {
574 TransData *td = &tc->data[td_index];
575 sv->td = td;
576 sv++;
577 }
578 }
579
580 return r_sv;
581 }
582
584 {
585 Array<TransDataEdgeSlideVert> r_sv(this->sd_len);
586 TransDataEdgeSlideVert *sv = &r_sv[0];
587 for (const int group_index : this->groups().index_range()) {
588 for (int td_index : this->td_indices_get(group_index)) {
589 TransData *td = &tc->data[td_index];
590 sv->td = td;
591 sv->dir_side[0] = float3(0);
592 sv->dir_side[1] = float3(0);
593 sv->loop_nr = -1;
594 sv++;
595 }
596 }
597
598 return r_sv;
599 }
600
602 const int group_index)
603 {
604 return sd_array.slice(this->groups()[group_index]);
605 }
606
608 const int group_index)
609 {
610 return sd_array.slice(this->groups()[group_index]);
611 }
612};
613
615{
616 UVGroups *uv_groups = static_cast<UVGroups *>(tc->custom.type.data);
617 if (uv_groups == nullptr) {
618 uv_groups = MEM_new<UVGroups>(__func__);
619 uv_groups->init(tc, bm, offsets);
620
621 /* Edge Slide and Vert Slide are often called in sequence, so, to avoid recalculating the
622 * groups, save them in the #TransDataContainer. */
623
624 tc->custom.type.data = uv_groups;
625 tc->custom.type.free_cb = [](TransInfo *, TransDataContainer *, TransCustomData *custom_data) {
626 UVGroups *data = static_cast<UVGroups *>(custom_data->data);
627 MEM_delete(data);
628 custom_data->data = nullptr;
629 };
630 }
631
632 return uv_groups;
633}
634
637/* -------------------------------------------------------------------- */
642 const TransInfo *t, TransDataContainer *tc, Vector<float3> &r_loc_dst_buffer)
643{
644
646 BMesh *bm = em->bm;
647 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
648
649 UVGroups *uv_groups = mesh_uv_groups_get(tc, bm, offsets);
650
652
653 r_loc_dst_buffer.reserve(r_sv.size() * 4);
654
655 for (const int group_index : uv_groups->groups().index_range()) {
656 const int size_prev = r_loc_dst_buffer.size();
657
658 for (int td_index : uv_groups->td_indices_get(group_index)) {
659 TransData *td = &tc->data[td_index];
660 BMLoop *l = static_cast<BMLoop *>(td->extra);
661
662 for (BMLoop *l_dst : {l->prev, l->next}) {
663 const float2 &uv_dest = BM_ELEM_CD_GET_FLOAT_P(l_dst, offsets.uv);
664 Span<float3> uvs_added = r_loc_dst_buffer.as_span().drop_front(size_prev);
665
666 bool skip = std::any_of(
667 uvs_added.begin(), uvs_added.end(), [&](const float3 &uv_dest_added) {
668 return compare_v2v2(uv_dest, uv_dest_added, FLT_EPSILON);
669 });
670
671 if (!skip) {
672 r_loc_dst_buffer.append(float3(uv_dest, 0.0f));
673 }
674 }
675 }
676
677 const int size_new = r_loc_dst_buffer.size() - size_prev;
678 for (TransDataVertSlideVert &sv : uv_groups->sd_group_get(r_sv, group_index)) {
679 /* The buffer address may change as the vector is resized. Avoid setting #Span now. */
680 // sv.targets = r_loc_dst_buffer.as_span().drop_front(size_prev);
681
682 /* Store the buffer slice temporarily in `target_curr`. */
683 sv.co_link_orig_3d = {static_cast<float3 *>(POINTER_FROM_INT(size_prev)), size_new};
684 sv.co_link_curr = 0;
685 }
686 }
687
688 if (t->aspect[0] != 1.0f || t->aspect[1] != 1.0f) {
689 for (float3 &dest : r_loc_dst_buffer) {
690 dest[0] *= t->aspect[0];
691 dest[1] *= t->aspect[1];
692 }
693 }
694
695 for (TransDataVertSlideVert &sv : r_sv) {
696 int start = POINTER_AS_INT(sv.co_link_orig_3d.data());
697 sv.co_link_orig_3d = r_loc_dst_buffer.as_span().slice(start, sv.co_link_orig_3d.size());
698 }
699
700 return r_sv;
701}
702
705/* -------------------------------------------------------------------- */
709/* Check if the UV group is a vertex between 2 faces. */
711 const BMUVOffsets &offsets,
712 Span<int> group)
713{
714 if (group.size() == 1) {
715 return false;
716 }
717 if (group.size() > 2) {
718 return false;
719 }
720
721 TransData *td_a = &tc->data[group[0]];
722 TransData *td_b = &tc->data[group[1]];
723 BMLoop *l_a = static_cast<BMLoop *>(td_a->extra);
724 BMLoop *l_b = static_cast<BMLoop *>(td_b->extra);
725 BMLoop *l_a_prev = l_a->prev;
726 BMLoop *l_a_next = l_a->next;
727 BMLoop *l_b_prev = l_b->next;
728 BMLoop *l_b_next = l_b->prev;
729 if (l_a_prev->v != l_b_prev->v) {
730 std::swap(l_b_prev, l_b_next);
731 if (l_a_prev->v != l_b_prev->v) {
732 return false;
733 }
734 }
735
736 if (l_a_next->v != l_b_next->v) {
737 return false;
738 }
739
740 const float2 &uv_a_prev = BM_ELEM_CD_GET_FLOAT_P(l_a_prev, offsets.uv);
741 const float2 &uv_b_prev = BM_ELEM_CD_GET_FLOAT_P(l_b_prev, offsets.uv);
742 if (!compare_v2v2(uv_a_prev, uv_b_prev, FLT_EPSILON)) {
743 return false;
744 }
745
746 const float2 &uv_a_next = BM_ELEM_CD_GET_FLOAT_P(l_a_next, offsets.uv);
747 const float2 &uv_b_next = BM_ELEM_CD_GET_FLOAT_P(l_b_next, offsets.uv);
748 if (!compare_v2v2(uv_a_next, uv_b_next, FLT_EPSILON)) {
749 return false;
750 }
751
752 return true;
753}
754
759static bool bm_loop_uv_calc_opposite_co(const BMLoop *l_tmp,
760 const float2 &uv_tmp,
761 const BMUVOffsets &offsets,
762 const float2 &ray_direction,
763 float2 &r_co)
764{
765 /* skip adjacent edges */
766 BMLoop *l_first = l_tmp->next;
767 BMLoop *l_last = l_tmp->prev;
768 BMLoop *l_iter;
769 float dist_sq_best = FLT_MAX;
770 bool found = false;
771
772 l_iter = l_first;
773 do {
774 const float2 &uv_iter = BM_ELEM_CD_GET_FLOAT_P(l_iter, offsets.uv);
775 const float2 &uv_iter_next = BM_ELEM_CD_GET_FLOAT_P(l_iter->next, offsets.uv);
776 float lambda;
777 if (isect_ray_seg_v2(uv_tmp, ray_direction, uv_iter, uv_iter_next, &lambda, nullptr) ||
778 isect_ray_seg_v2(uv_tmp, -ray_direction, uv_iter, uv_iter_next, &lambda, nullptr))
779 {
780 float2 isect_co = uv_tmp + ray_direction * lambda;
781 /* likelihood of multiple intersections per ngon is quite low,
782 * it would have to loop back on itself, but better support it
783 * so check for the closest opposite edge */
784 const float dist_sq_test = math::distance_squared(uv_tmp, isect_co);
785 if (dist_sq_test < dist_sq_best) {
786 r_co = isect_co;
787 dist_sq_best = dist_sq_test;
788 found = true;
789 }
790 }
791 } while ((l_iter = l_iter->next) != l_last);
792
793 return found;
794}
795
797 const float2 &uv,
798 const float2 &aspect,
799 const BMUVOffsets &offsets)
800{
801 BMFace *f = l->f;
802 BMLoop *l_next = l->next;
803 if (f->len == 4) {
804 /* we could use code below, but in this case
805 * sliding diagonally across the quad works well */
806 return BM_ELEM_CD_GET_FLOAT_P(l_next->next, offsets.uv);
807 }
808
809 BMLoop *l_prev = l->prev;
810 const float2 &uv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
811 const float2 &uv_next = BM_ELEM_CD_GET_FLOAT_P(l_next, offsets.uv);
812
813 float2 ray_dir = (uv - uv_prev) + (uv_next - uv);
814 ray_dir = math::orthogonal(ray_dir * aspect);
815 ray_dir[0] /= aspect[0];
816 ray_dir[1] /= aspect[1];
817
818 float2 isect_co;
819 if (!bm_loop_uv_calc_opposite_co(l, uv, offsets, ray_dir, isect_co)) {
820 /* Rare case. */
821 mid_v3_v3v3(isect_co, l->prev->v->co, l_next->v->co);
822 }
823 return isect_co;
824}
825
828 int *r_group_len)
829{
832 BMesh *bm = em->bm;
833 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
834
835 const bool check_edge = ED_uvedit_select_mode_get(t->scene) == UV_SELECT_EDGE;
836
837 UVGroups *uv_groups = mesh_uv_groups_get(tc, bm, offsets);
838 Array<int2> groups_linked(uv_groups->groups().size(), int2(-1, -1));
839
840 {
841 /* Identify the group to which a loop belongs through the element's index value. */
842
843 /* First we just need to "clean up" the neighboring loops.
844 * This way we can identify where a group of sliding edges starts and where it ends. */
845 TransData *td = tc->data;
846 for (int i = 0; i < tc->data_len; i++, td++) {
847 if (!(td->flag & TD_SELECTED)) {
848 /* The selected ones are sorted at the beginning. */
849 break;
850 }
851 BMLoop *l = static_cast<BMLoop *>(td->extra);
854 }
855
856 /* Now set the group indexes. */
857 for (const int group_index : uv_groups->groups().index_range()) {
858 for (int td_index : uv_groups->td_indices_get(group_index)) {
859 TransData *td = &tc->data[td_index];
860 BMLoop *l = static_cast<BMLoop *>(td->extra);
861 BM_elem_index_set(l, group_index);
862 }
863 }
865 }
866
867 for (const int group_index : uv_groups->groups().index_range()) {
868 int2 &group_linked_pair = groups_linked[group_index];
869
870 for (int td_index : uv_groups->td_indices_get(group_index)) {
871 TransData *td = &tc->data[td_index];
872 BMLoop *l = static_cast<BMLoop *>(td->extra);
873
874 for (BMLoop *l_dst : {l->prev, l->next}) {
875 const int group_index_dst = BM_elem_index_get(l_dst);
876 if (group_index_dst == -1) {
877 continue;
878 }
879
880 if (ELEM(group_index_dst, group_linked_pair[0], group_linked_pair[1])) {
881 continue;
882 }
883
884 if (check_edge) {
885 BMLoop *l_edge = l_dst == l->prev ? l_dst : l;
886 if (!uvedit_edge_select_test_ex(t->settings, l_edge, offsets)) {
887 continue;
888 }
889 }
890
891 if (group_linked_pair[1] != -1) {
892 /* For Edge Slide, the vertex can only be connected to a maximum of 2 sliding edges. */
893 return r_sv;
894 }
895 const int slot = int(group_linked_pair[0] != -1);
896 group_linked_pair[slot] = group_index_dst;
897 }
898 }
899
900 if (group_linked_pair[0] == -1) {
901 /* For Edge Slide, the vertex must be connected to at least 1 sliding edge. */
902 return r_sv;
903 }
904 }
905
906 /* Alloc and initialize the #TransDataEdgeSlideVert. */
907 r_sv = uv_groups->sd_array_create_and_init_edge(tc);
908
909 /* Compute the sliding groups. */
910 int loop_nr = 0;
911 for (int i : r_sv.index_range()) {
912 if (r_sv[i].loop_nr != -1) {
913 /* This vertex has already been computed. */
914 continue;
915 }
916
917 BMLoop *l = static_cast<BMLoop *>(r_sv[i].td->extra);
918 int group_index = BM_elem_index_get(l);
919
920 /* Start from a vertex connected to just a single edge or any if it doesn't exist. */
921 int i_curr = group_index;
922 int i_prev = groups_linked[group_index][1];
923 while (!ELEM(i_prev, -1, group_index)) {
924 int tmp = groups_linked[i_prev][0] != i_curr ? groups_linked[i_prev][0] :
925 groups_linked[i_prev][1];
926 i_curr = i_prev;
927 i_prev = tmp;
928 }
929
938 struct SlideTempDataUV {
939 int i; /* The group index. */
940 struct {
941 BMFace *f;
942 float2 dst;
943 } fdata[2];
944 bool vert_is_inner; /* In the middle of two faces. */
953 int find_best_dir(const SlideTempDataUV *curr_side_other,
954 const BMLoop *l_src,
955 const BMLoop *l_dst,
956 const float2 &src,
957 const float2 &dst,
958 bool *r_do_isect_curr_dirs) const
959 {
960 *r_do_isect_curr_dirs = false;
961 const BMFace *f_curr = l_src->f;
962 if (curr_side_other->fdata[0].f &&
963 (curr_side_other->fdata[0].f == f_curr ||
964 compare_v2v2(dst, curr_side_other->fdata[0].dst, FLT_EPSILON)))
965 {
966 return 0;
967 }
968
969 if (curr_side_other->fdata[1].f &&
970 (curr_side_other->fdata[1].f == f_curr ||
971 compare_v2v2(dst, curr_side_other->fdata[1].dst, FLT_EPSILON)))
972 {
973 return 1;
974 }
975
976 if (curr_side_other->fdata[0].f || curr_side_other->fdata[1].f) {
977 /* Find the best direction checking the edges that share faces between them. */
978 int best_dir = -1;
979 const BMLoop *l_edge_dst = l_src->prev == l_dst ? l_src->prev : l_src;
980 const BMLoop *l_other = l_edge_dst->radial_next;
981 while (l_other != l_edge_dst) {
982 const BMLoop *l_other_dst = l_other->v == l_src->v ? l_other->next : l_other;
983 if (BM_elem_index_get(l_other_dst) != -1) {
984 /* This is a sliding edge corner. */
985 break;
986 }
987
988 if (l_other->f == curr_side_other->fdata[0].f) {
989 best_dir = 0;
990 break;
991 }
992 if (l_other->f == curr_side_other->fdata[1].f) {
993 best_dir = 1;
994 break;
995 }
996 l_other = (l_other->v == l_src->v ? l_other->prev : l_other->next)->radial_next;
997 }
998
999 if (best_dir != -1) {
1000 *r_do_isect_curr_dirs = true;
1001 return best_dir;
1002 }
1003 }
1004
1005 if (ELEM(nullptr, this->fdata[0].f, this->fdata[1].f)) {
1006 return int(this->fdata[0].f != nullptr);
1007 }
1008
1009 /* Find the closest direction. */
1010 *r_do_isect_curr_dirs = true;
1011
1012 float2 dir_curr = dst - src;
1013 float2 dir0 = math::normalize(this->fdata[0].dst - src);
1014 float2 dir1 = math::normalize(this->fdata[1].dst - src);
1015 float dot0 = math::dot(dir_curr, dir0);
1016 float dot1 = math::dot(dir_curr, dir1);
1017 return int(dot0 < dot1);
1018 }
1019 } prev = {}, curr = {}, next = {}, tmp = {};
1020
1021 curr.i = i_curr;
1022 curr.vert_is_inner = mesh_uv_group_is_inner(tc, offsets, uv_groups->td_indices_get(curr.i));
1023
1024 /* Do not compute `prev` for now. Let the loop calculate `curr` twice. */
1025 prev.i = -1;
1026
1027 while (curr.i != -1) {
1028 int tmp_i = prev.i == -1 ? i_prev : prev.i;
1029 next.i = groups_linked[curr.i][0] != tmp_i ? groups_linked[curr.i][0] :
1030 groups_linked[curr.i][1];
1031 if (next.i != -1) {
1032 next.vert_is_inner = mesh_uv_group_is_inner(
1033 tc, offsets, uv_groups->td_indices_get(next.i));
1034
1035 tmp = curr;
1036 Span<int> td_indices_next = uv_groups->td_indices_get(next.i);
1037
1038 for (int td_index_curr : uv_groups->td_indices_get(curr.i)) {
1039 BMLoop *l_curr = static_cast<BMLoop *>(tc->data[td_index_curr].extra);
1040 const float2 &src = BM_ELEM_CD_GET_FLOAT_P(l_curr, offsets.uv);
1041
1042 for (int td_index_next : td_indices_next) {
1043 BMLoop *l_next = static_cast<BMLoop *>(tc->data[td_index_next].extra);
1044 if (l_curr->f != l_next->f) {
1045 continue;
1046 }
1047
1048 BLI_assert(l_curr != l_next);
1049
1050 BMLoop *l1_dst, *l2_dst;
1051 if (l_curr->next == l_next) {
1052 l1_dst = l_curr->prev;
1053 l2_dst = l_next->next;
1054 }
1055 else {
1056 l1_dst = l_curr->next;
1057 l2_dst = l_next->prev;
1058 }
1059
1060 const float2 &dst = BM_ELEM_CD_GET_FLOAT_P(l1_dst, offsets.uv);
1061
1062 /* Sometimes the sliding direction may fork (`isect_curr_dirs` is `true`).
1063 * In this case, the resulting direction is the intersection of the destinations. */
1064 bool isect_curr_dirs = false;
1065
1066 /* Identify the slot to slide according to the directions already computed in `curr`.
1067 */
1068 int best_dir = curr.find_best_dir(&tmp, l_curr, l1_dst, src, dst, &isect_curr_dirs);
1069
1070 if (curr.fdata[best_dir].f == nullptr) {
1071 curr.fdata[best_dir].f = l_curr->f;
1072 if (curr.vert_is_inner) {
1073 curr.fdata[best_dir].dst = isect_face_dst(l_curr, src, t->aspect, offsets);
1074 }
1075 else {
1076 curr.fdata[best_dir].dst = dst;
1077 }
1078 }
1079
1080 /* Compute `next`. */
1081 next.fdata[best_dir].f = l_curr->f;
1082 if (BM_elem_index_get(l2_dst) != -1 || next.vert_is_inner) {
1083 /* Case where the vertex slides over the face. */
1084 const float2 &src_next = BM_ELEM_CD_GET_FLOAT_P(l_next, offsets.uv);
1085 next.fdata[best_dir].dst = isect_face_dst(l_next, src_next, t->aspect, offsets);
1086 }
1087 else {
1088 /* Case where the vertex slides over an edge. */
1089 const float2 &dst_next = BM_ELEM_CD_GET_FLOAT_P(l2_dst, offsets.uv);
1090 next.fdata[best_dir].dst = dst_next;
1091 }
1092
1093 if (isect_curr_dirs) {
1094 /* The `best_dir` can only have one direction. */
1095 const float2 &dst0 = prev.fdata[best_dir].dst;
1096 const float2 &dst1 = curr.fdata[best_dir].dst;
1097 const float2 &dst2 = dst;
1098 const float2 &dst3 = next.fdata[best_dir].dst;
1099 if (isect_line_line_v2_point(dst0, dst1, dst2, dst3, curr.fdata[best_dir].dst) ==
1101 {
1102 curr.fdata[best_dir].dst = math::midpoint(dst1, dst2);
1103 }
1104 }
1105 /* There is only one pair of corners to slide per face, we don't need to keep checking
1106 * `if (f_curr != l_next->f)`. */
1107 break;
1108 }
1109 }
1110 }
1111
1112 TransDataEdgeSlideVert *sv_first = nullptr;
1113 for (TransDataEdgeSlideVert &sv : uv_groups->sd_group_get(r_sv, curr.i)) {
1114 if (sv_first) {
1115 TransData *td = sv.td;
1116 sv = *sv_first;
1117 sv.td = td;
1118 }
1119 else {
1120 sv_first = &sv;
1121 float2 iloc = sv.td->iloc;
1122 const float2 &aspect = t->aspect;
1123 if (curr.fdata[0].f) {
1124 float2 dst = curr.fdata[0].dst * aspect;
1125 sv.dir_side[0] = float3(dst - iloc, 0.0f);
1126 }
1127 if (curr.fdata[1].f) {
1128 float2 dst = curr.fdata[1].dst * aspect;
1129 sv.dir_side[1] = float3(dst - iloc, 0.0f);
1130 }
1131 sv.edge_len = math::distance(sv.dir_side[0], sv.dir_side[1]);
1132 sv.loop_nr = loop_nr;
1133 }
1134 }
1135
1136 if (i_prev != -1 && prev.i == i_prev) {
1137 /* Cycle returned to the beginning.
1138 * The data with index `i_curr` was computed twice to make sure the directions are
1139 * correct the second time. */
1140 break;
1141 }
1142
1143 /* Move forward. */
1144 prev = curr;
1145 curr = next;
1146 next.fdata[0].f = next.fdata[1].f = nullptr;
1147 }
1148 loop_nr++;
1149 }
1150 *r_group_len = loop_nr;
1151 return r_sv;
1152}
1153
1157 /*flags*/ (T_EDIT | T_POINTS | T_2D_EDIT),
1158 /*create_trans_data*/ createTransUVs,
1159 /*recalc_data*/ recalcData_uv,
1160 /*special_aftertrans_update*/ nullptr,
1161};
SpaceImage * CTX_wm_space_image(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
#define BLI_assert(a)
Definition BLI_assert.h:50
#define ISECT_LINE_LINE_COLINEAR
bool isect_ray_seg_v2(const float ray_origin[2], const float ray_direction[2], const float v0[2], const float v1[2], float *r_lambda, float *r_u)
int isect_line_line_v2_point(const float v0[2], const float v1[2], const float v2[2], const float v3[2], float r_vi[2])
void unit_m3(float m[3][3])
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v2_v2(float r[2], const float a[2])
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE bool equals_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
MINLINE bool compare_v2v2(const float v1[2], const float v2[2], float limit) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ UV_SELECT_EDGE
@ SI_CLIP_UV
@ SI_LIVE_UNWRAP
@ SI_PIXEL_ROUND_CENTER
@ SI_PIXEL_ROUND_CORNER
@ SI_PIXEL_ROUND_DISABLED
@ V3D_AROUND_LOCAL_ORIGINS
void ED_space_image_get_size(SpaceImage *sima, int *r_width, int *r_height)
bool ED_space_image_show_uvedit(const SpaceImage *sima, Object *obedit)
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)
void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit, struct wmWindow *win_modal)
char ED_uvedit_select_mode_get(const Scene *scene)
void ED_uvedit_live_unwrap_re_solve()
bool uvedit_edge_select_test_ex(const ToolSettings *ts, const BMLoop *l, BMUVOffsets offsets)
bool uvedit_face_visible_test(const Scene *scene, BMFace *efa)
bool uvedit_uv_select_test_ex(const ToolSettings *ts, const BMLoop *l, BMUVOffsets offsets)
bool uvedit_uv_select_test(const Scene *scene, BMLoop *l, BMUVOffsets offsets)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
@ BM_LOOP
@ BM_ELEM_TAG
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_test_bool(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_LOOPS_OF_FACE
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMLoop * l_b
BMUVOffsets BM_uv_map_get_offsets(const BMesh *bm)
int64_t size() const
Definition BLI_array.hh:245
IndexRange index_range() const
Definition BLI_array.hh:349
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr const T * end() const
Definition BLI_span.hh:225
constexpr const T * begin() const
Definition BLI_span.hh:221
int64_t size() const
void append(const T &value)
void reserve(const int64_t min_capacity)
Span< T > as_span() const
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
int count
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
static ulong * next
T distance(const T &a, const T &b)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
T midpoint(const T &a, const T &b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
VecBase< T, 3 > orthogonal(const VecBase< T, 3 > &v)
VecBase< float, 3 > float3
#define FLT_MAX
Definition stdcycles.h:14
struct BMVert * v
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
float co[3]
char elem_index_dirty
int totloop
Definition DNA_ID.h:413
struct LinkNode * next
void * first
ListBase spacedata
TransCustomData type
Definition transform.hh:425
void(* free_cb)(TransInfo *, TransDataContainer *tc, TransCustomData *custom_data)
Definition transform.hh:409
TransCustomDataContainer custom
Definition transform.hh:501
TransData * data
Definition transform.hh:445
blender::float3 dir_side[2]
float smtx[3][3]
float axismtx[3][3]
float mtx[3][3]
TransDataExtension * ext
short around
Definition transform.hh:580
eTState state
Definition transform.hh:527
ToolSettings * settings
Definition transform.hh:656
float aspect[3]
Definition transform.hh:553
Scene * scene
Definition transform.hh:654
eTFlag flag
Definition transform.hh:523
ScrArea * area
Definition transform.hh:651
Array< TransDataEdgeSlideVert > sd_array_create_and_init_edge(TransDataContainer *tc)
MutableSpan< TransDataEdgeSlideVert > sd_group_get(MutableSpan< TransDataEdgeSlideVert > sd_array, const int group_index)
OffsetIndices< int > groups() const
Array< TransDataVertSlideVert > sd_array_create_and_init(TransDataContainer *tc)
MutableSpan< TransDataVertSlideVert > sd_group_get(MutableSpan< TransDataVertSlideVert > sd_array, const int group_index)
void init(const TransDataContainer *tc, BMesh *bm, const BMUVOffsets &offsets)
Span< int > td_indices_get(const int group_index) const
@ T_PROP_CONNECTED
Definition transform.hh:99
@ T_PROP_EDIT
Definition transform.hh:98
@ T_2D_EDIT
Definition transform.hh:104
@ T_POINTS
Definition transform.hh:93
@ T_CLIP_UV
Definition transform.hh:105
@ T_EDIT
Definition transform.hh:91
@ TRANS_CANCEL
Definition transform.hh:210
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
Definition transform.hh:854
conversion and adaptation of different datablocks to a common struct.
static UVGroups * mesh_uv_groups_get(TransDataContainer *tc, BMesh *bm, const BMUVOffsets &offsets)
Array< TransDataEdgeSlideVert > transform_mesh_uv_edge_slide_data_create(const TransInfo *t, TransDataContainer *tc, int *r_group_len)
static bool mesh_uv_group_is_inner(const TransDataContainer *tc, const BMUVOffsets &offsets, Span< int > group)
Array< TransDataVertSlideVert > transform_mesh_uv_vert_slide_data_create(const TransInfo *t, TransDataContainer *tc, Vector< float3 > &r_loc_dst_buffer)
static void uv_set_connectivity_distance(const ToolSettings *ts, BMesh *bm, float *dists, const float aspect[2])
#define TMP_LOOP_SELECT_TAG
static void flushTransUVs(TransInfo *t)
TransConvertTypeInfo TransConvertType_MeshUV
static float2 isect_face_dst(const BMLoop *l, const float2 &uv, const float2 &aspect, const BMUVOffsets &offsets)
static void createTransUVs(bContext *C, TransInfo *t)
static void UVsToTransData(const float aspect[2], float *uv, const float *center, const float calc_dist, const bool selected, BMLoop *l, TransData *r_td, TransData2D *r_td2d)
static void recalcData_uv(TransInfo *t)
static bool bm_loop_uv_calc_opposite_co(const BMLoop *l_tmp, const float2 &uv_tmp, const BMUVOffsets &offsets, const float2 &ray_direction, float2 &r_co)
@ TD_SELECTED