Blender V4.3
editface.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "MEM_guardedalloc.h"
10
12#include "BLI_bitmap.h"
13#include "BLI_blenlib.h"
14#include "BLI_math_matrix.h"
15#include "BLI_math_vector.h"
16#include "BLI_math_vector.hh"
17#include "BLI_task.hh"
18#include "BLI_vector_set.hh"
19
20#include "IMB_imbuf.hh"
21#include "IMB_imbuf_types.hh"
22
23#include "DNA_mesh_types.h"
24#include "DNA_meshdata_types.h"
25#include "DNA_object_types.h"
26
27#include "BKE_attribute.hh"
28#include "BKE_context.hh"
29#include "BKE_customdata.hh"
30#include "BKE_global.hh"
31#include "BKE_mesh.hh"
32#include "BKE_mesh_mapping.hh"
33#include "BKE_object.hh"
34#include "BKE_object_types.hh"
35
36#include "ED_mesh.hh"
37#include "ED_screen.hh"
38#include "ED_select_utils.hh"
39#include "ED_view3d.hh"
40
41#include "WM_api.hh"
42#include "WM_types.hh"
43
44#include "DEG_depsgraph.hh"
46
47/* own include */
48
50 Object *ob,
51 const bool flush_selection,
52 const bool flush_hidden)
53{
54 using namespace blender;
55 Mesh *mesh = BKE_mesh_from_object(ob);
56 const int *index_array = nullptr;
57
58 BLI_assert(flush_selection || flush_hidden);
59
60 if (mesh == nullptr) {
61 return;
62 }
63
64 /* NOTE: call #BKE_mesh_flush_hidden_from_verts_ex first when changing hidden flags. */
65
66 /* we could call this directly in all areas that change selection,
67 * since this could become slow for realtime updates (circle-select for eg) */
68 if (flush_selection) {
69 bke::mesh_select_face_flush(*mesh);
70 }
71
74
75 if (ob_eval == nullptr) {
76 return;
77 }
78
79 bke::AttributeAccessor attributes_me = mesh->attributes();
80 Mesh *me_orig = (Mesh *)ob_eval->runtime->data_orig;
81 bke::MutableAttributeAccessor attributes_orig = me_orig->attributes_for_write();
82 Mesh *mesh_eval = (Mesh *)ob_eval->runtime->data_eval;
83 bke::MutableAttributeAccessor attributes_eval = mesh_eval->attributes_for_write();
84 bool updated = false;
85
86 if (me_orig != nullptr && mesh_eval != nullptr && me_orig->faces_num == mesh->faces_num) {
87 /* Update the evaluated copy of the mesh. */
88 if (flush_hidden) {
89 const VArray<bool> hide_poly_me = *attributes_me.lookup_or_default<bool>(
90 ".hide_poly", bke::AttrDomain::Face, false);
91 bke::SpanAttributeWriter<bool> hide_poly_orig =
92 attributes_orig.lookup_or_add_for_write_only_span<bool>(".hide_poly",
93 bke::AttrDomain::Face);
94 hide_poly_me.materialize(hide_poly_orig.span);
95 hide_poly_orig.finish();
96 }
97 if (flush_selection) {
98 const VArray<bool> select_poly_me = *attributes_me.lookup_or_default<bool>(
99 ".select_poly", bke::AttrDomain::Face, false);
100 bke::SpanAttributeWriter<bool> select_poly_orig =
101 attributes_orig.lookup_or_add_for_write_only_span<bool>(".select_poly",
102 bke::AttrDomain::Face);
103 select_poly_me.materialize(select_poly_orig.span);
104 select_poly_orig.finish();
105 }
106
107 /* Mesh faces => Final derived faces */
108 if ((index_array = (const int *)CustomData_get_layer(&mesh_eval->face_data, CD_ORIGINDEX))) {
109 if (flush_hidden) {
110 const VArray<bool> hide_poly_orig = *attributes_orig.lookup_or_default<bool>(
111 ".hide_poly", bke::AttrDomain::Face, false);
112 bke::SpanAttributeWriter<bool> hide_poly_eval =
113 attributes_eval.lookup_or_add_for_write_only_span<bool>(".hide_poly",
114 bke::AttrDomain::Face);
115 for (const int i : IndexRange(mesh_eval->faces_num)) {
116 const int orig_face_index = index_array[i];
117 if (orig_face_index != ORIGINDEX_NONE) {
118 hide_poly_eval.span[i] = hide_poly_orig[orig_face_index];
119 }
120 }
121 hide_poly_eval.finish();
122 }
123 if (flush_selection) {
124 const VArray<bool> select_poly_orig = *attributes_orig.lookup_or_default<bool>(
125 ".select_poly", bke::AttrDomain::Face, false);
126 bke::SpanAttributeWriter<bool> select_poly_eval =
127 attributes_eval.lookup_or_add_for_write_only_span<bool>(".select_poly",
128 bke::AttrDomain::Face);
129 for (const int i : IndexRange(mesh_eval->faces_num)) {
130 const int orig_face_index = index_array[i];
131 if (orig_face_index != ORIGINDEX_NONE) {
132 select_poly_eval.span[i] = select_poly_orig[orig_face_index];
133 }
134 }
135 select_poly_eval.finish();
136 }
137
138 updated = true;
139 }
140 }
141
142 if (updated) {
143 if (flush_hidden) {
145 }
146 else {
148 }
149
150 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_SELECT);
151 }
152 else {
154 }
155
157}
158
159void paintface_hide(bContext *C, Object *ob, const bool unselected)
160{
161 using namespace blender;
162 Mesh *mesh = BKE_mesh_from_object(ob);
163 if (mesh == nullptr || mesh->faces_num == 0) {
164 return;
165 }
166
167 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
168 bke::SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_span<bool>(
169 ".hide_poly", bke::AttrDomain::Face);
170 bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
171 ".select_poly", bke::AttrDomain::Face);
172
173 for (int i = 0; i < mesh->faces_num; i++) {
174 if (!hide_poly.span[i]) {
175 if (!select_poly.span[i] == unselected) {
176 hide_poly.span[i] = true;
177 }
178 }
179
180 if (hide_poly.span[i]) {
181 select_poly.span[i] = false;
182 }
183 }
184
185 hide_poly.finish();
186 select_poly.finish();
187
188 bke::mesh_hide_face_flush(*mesh);
189
190 paintface_flush_flags(C, ob, true, true);
191}
192
193void paintface_reveal(bContext *C, Object *ob, const bool select)
194{
195 using namespace blender;
196 Mesh *mesh = BKE_mesh_from_object(ob);
197 if (mesh == nullptr || mesh->faces_num == 0) {
198 return;
199 }
200
201 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
202
203 if (select) {
204 const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
205 ".hide_poly", bke::AttrDomain::Face, false);
206 bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
207 ".select_poly", bke::AttrDomain::Face);
208 for (const int i : hide_poly.index_range()) {
209 if (hide_poly[i]) {
210 select_poly.span[i] = true;
211 }
212 }
213 select_poly.finish();
214 }
215
216 attributes.remove(".hide_poly");
217
218 bke::mesh_hide_face_flush(*mesh);
219
220 paintface_flush_flags(C, ob, true, true);
221}
222
230 Mesh &mesh,
231 const bool skip_seams = true)
232{
233 using namespace blender;
234 const OffsetIndices faces = mesh.faces();
235 const Span<int> corner_edges = mesh.corner_edges();
236
237 const bke::AttributeAccessor attributes = mesh.attributes();
238 const VArray<bool> uv_seams = *attributes.lookup_or_default<bool>(
239 ".uv_seam", bke::AttrDomain::Edge, false);
240 const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
241 ".hide_poly", bke::AttrDomain::Face, false);
242
243 /* Faces are connected if they share edges. By connecting all edges of a loop (as long as they
244 * are not a seam) we can find connected faces. */
245 threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
246 for (const int face_index : range) {
247 if (hide_poly[face_index]) {
248 continue;
249 }
250 const Span<int> face_edges = corner_edges.slice(faces[face_index]);
251
252 for (const int poly_loop_index : face_edges.index_range()) {
253 const int outer_edge = face_edges[poly_loop_index];
254 if (skip_seams && uv_seams[outer_edge]) {
255 continue;
256 }
257
258 for (const int inner_edge :
259 face_edges.slice(poly_loop_index, face_edges.size() - poly_loop_index))
260 {
261 if (outer_edge == inner_edge) {
262 continue;
263 }
264 if (skip_seams && uv_seams[inner_edge]) {
265 continue;
266 }
267 islands.join(inner_edge, outer_edge);
268 }
269 }
270 }
271 });
272}
273
274/* Select faces connected to the given face_indices. Seams are treated as separation. */
276 const blender::Span<int> face_indices,
277 const bool select)
278{
279 using namespace blender;
280
281 AtomicDisjointSet islands(mesh.edges_num);
282 build_poly_connections(islands, mesh);
283
284 const OffsetIndices faces = mesh.faces();
285 const Span<int> corner_edges = mesh.corner_edges();
286
287 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
288 const VArray<bool> uv_seams = *attributes.lookup_or_default<bool>(
289 ".uv_seam", bke::AttrDomain::Edge, false);
290 bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
291 ".select_poly", bke::AttrDomain::Face);
292
293 Set<int> selected_roots;
294 for (const int i : face_indices) {
295 for (const int edge : corner_edges.slice(faces[i])) {
296 if (uv_seams[edge]) {
297 continue;
298 }
299 const int root = islands.find_root(edge);
300 selected_roots.add(root);
301 }
302 }
303
304 threading::parallel_for(select_poly.span.index_range(), 1024, [&](const IndexRange range) {
305 for (const int face_index : range) {
306 for (const int edge : corner_edges.slice(faces[face_index])) {
307 const int root = islands.find_root(edge);
308 if (selected_roots.contains(root)) {
309 select_poly.span[face_index] = select;
310 break;
311 }
312 }
313 }
314 });
315
316 select_poly.finish();
317}
318
319void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const bool select)
320{
321 using namespace blender;
322 Mesh *mesh = BKE_mesh_from_object(ob);
323 if (mesh == nullptr || mesh->faces_num == 0) {
324 return;
325 }
326
327 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
328 bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
329 ".select_poly", bke::AttrDomain::Face);
330
331 Vector<int> indices;
332 if (mval) {
333 uint index = uint(-1);
334 if (!ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) {
335 select_poly.finish();
336 return;
337 }
338 /* Since paintface_select_linked_faces might not select the face under the cursor, select it
339 * here. */
340 select_poly.span[index] = true;
341 indices.append(index);
342 }
343
344 else {
345 for (const int i : select_poly.span.index_range()) {
346 if (!select_poly.span[i]) {
347 continue;
348 }
349 indices.append(i);
350 }
351 }
352
353 select_poly.finish();
354
355 paintface_select_linked_faces(*mesh, indices, select);
356 paintface_flush_flags(C, ob, true, false);
357}
358
361 blender::Span<int> face_edges,
362 blender::Span<blender::float3> vert_positions,
363 const int mval[2])
364{
365 using namespace blender;
366 int closest_edge_index;
367
368 const float2 mval_f = {float(mval[0]), float(mval[1])};
369 float min_distance = FLT_MAX;
370 for (const int i : face_edges) {
371 float2 screen_coordinate;
372 const int2 edge = edges[i];
373 const float3 edge_vert_average = math::midpoint(vert_positions[edge[0]],
374 vert_positions[edge[1]]);
376 region, edge_vert_average, screen_coordinate, V3D_PROJ_TEST_CLIP_DEFAULT);
377 if (status != V3D_PROJ_RET_OK) {
378 continue;
379 }
380 const float distance = math::distance_squared(mval_f, screen_coordinate);
381 if (distance < min_distance) {
382 min_distance = distance;
383 closest_edge_index = i;
384 }
385 }
386 return closest_edge_index;
387}
388
390 const blender::Span<int> corner_edges,
391 const int current_edge_index)
392{
393 const int index_in_poly = corner_edges.slice(face).first_index(current_edge_index);
394 /* Assumes that edge index of opposing face edge is always off by 2 on quads. */
395 if (index_in_poly >= 2) {
396 return corner_edges[face[index_in_poly - 2]];
397 }
398 /* Cannot be out of bounds because of the preceding if statement: if i < 2 then i+2 < 4. */
399 return corner_edges[face[index_in_poly + 2]];
400}
401
406static bool follow_face_loop(const int face_start_index,
407 const int edge_start_index,
408 const blender::OffsetIndices<int> faces,
409 const blender::VArray<bool> &hide_poly,
410 const blender::Span<int> corner_edges,
411 const blender::GroupedSpan<int> edge_to_face_map,
412 blender::VectorSet<int> &r_loop_faces)
413{
414 using namespace blender;
415 int current_face_index = face_start_index;
416 int current_edge_index = edge_start_index;
417
418 while (current_edge_index > 0) {
419 int next_face_index = -1;
420
421 for (const int face_index : edge_to_face_map[current_edge_index]) {
422 if (face_index != current_face_index) {
423 next_face_index = face_index;
424 break;
425 }
426 }
427
428 /* Edge might only have 1 face connected. */
429 if (next_face_index == -1) {
430 return false;
431 }
432
433 /* Only works on quads. */
434 if (faces[next_face_index].size() != 4) {
435 return false;
436 }
437
438 /* Happens if we looped around the mesh. */
439 if (r_loop_faces.contains(next_face_index)) {
440 return true;
441 }
442
443 /* Hidden faces stop selection. */
444 if (hide_poly[next_face_index]) {
445 return false;
446 }
447
448 r_loop_faces.add(next_face_index);
449
450 const IndexRange next_face = faces[next_face_index];
451 current_edge_index = get_opposing_edge_index(next_face, corner_edges, current_edge_index);
452 current_face_index = next_face_index;
453 }
454 return false;
455}
456
457void paintface_select_loop(bContext *C, Object *ob, const int mval[2], const bool select)
458{
459 using namespace blender;
460
464
466 if (!ob_eval) {
467 return;
468 }
469
470 uint poly_pick_index = uint(-1);
471 if (!ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &poly_pick_index)) {
472 return;
473 }
474
475 ARegion *region = CTX_wm_region(C);
476 RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
477 ED_view3d_init_mats_rv3d(ob_eval, rv3d);
478
479 Mesh *mesh = BKE_mesh_from_object(ob);
480 const Span<int> corner_edges = mesh->corner_edges();
481 const Span<float3> verts = mesh->vert_positions();
482 const OffsetIndices faces = mesh->faces();
483 const Span<int2> edges = mesh->edges();
484
485 const IndexRange face = faces[poly_pick_index];
486 const int closest_edge_index = find_closest_edge_in_poly(
487 region, edges, corner_edges.slice(face), verts, mval);
488
489 Array<int> edge_to_face_offsets;
490 Array<int> edge_to_face_indices;
491 const GroupedSpan<int> edge_to_face_map = bke::mesh::build_edge_to_face_map(
492 faces, corner_edges, mesh->edges_num, edge_to_face_offsets, edge_to_face_indices);
493
494 VectorSet<int> faces_to_select;
495
496 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
497 const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
498 ".hide_poly", bke::AttrDomain::Face, false);
499
500 const Span<int> faces_to_closest_edge = edge_to_face_map[closest_edge_index];
501 const bool traced_full_loop = follow_face_loop(faces_to_closest_edge[0],
502 closest_edge_index,
503 faces,
504 hide_poly,
505 corner_edges,
506 edge_to_face_map,
507 faces_to_select);
508
509 if (!traced_full_loop && faces_to_closest_edge.size() > 1) {
510 /* Trace the other way. */
511 follow_face_loop(faces_to_closest_edge[1],
512 closest_edge_index,
513 faces,
514 hide_poly,
515 corner_edges,
516 edge_to_face_map,
517 faces_to_select);
518 }
519
520 bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
521 ".select_poly", bke::AttrDomain::Face);
522
523 /* Toggling behavior. When one of the faces of the picked edge is already selected,
524 * it deselects the loop instead. */
525 bool any_adjacent_poly_selected = false;
526 for (const int i : faces_to_closest_edge) {
527 any_adjacent_poly_selected |= select_poly.span[i];
528 }
529 const bool select_toggle = select && !any_adjacent_poly_selected;
530 select_poly.span.fill_indices(faces_to_select.as_span(), select_toggle);
531
532 select_poly.finish();
533 paintface_flush_flags(C, ob, true, false);
534}
535
538 blender::Span<bool> select_vert,
539 const bool face_step)
540{
541 for (const int edge_index : face_edges) {
542 const blender::int2 &edge = edges[edge_index];
543 /* If a face is selected, all of its verts are selected too, meaning that neighboring faces
544 * will have some vertices selected. */
545 if (face_step) {
546 if (select_vert[edge[0]] || select_vert[edge[1]]) {
547 return true;
548 }
549 }
550 else {
551 if (select_vert[edge[0]] && select_vert[edge[1]]) {
552 return true;
553 }
554 }
555 }
556 return false;
557}
558
559void paintface_select_more(Mesh *mesh, const bool face_step)
560{
561 using namespace blender;
562
563 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
564 bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
565 ".select_poly", bke::AttrDomain::Face);
566 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
567 ".select_vert", bke::AttrDomain::Point);
568 const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
569 ".hide_poly", bke::AttrDomain::Face, false);
570
571 const OffsetIndices faces = mesh->faces();
572 const Span<int> corner_edges = mesh->corner_edges();
573 const Span<int2> edges = mesh->edges();
574
575 threading::parallel_for(select_poly.span.index_range(), 1024, [&](const IndexRange range) {
576 for (const int i : range) {
577 if (select_poly.span[i] || hide_poly[i]) {
578 continue;
579 }
580 const IndexRange face = faces[i];
581 if (poly_has_selected_neighbor(corner_edges.slice(face), edges, select_vert.span, face_step))
582 {
583 select_poly.span[i] = true;
584 }
585 }
586 });
587
588 select_poly.finish();
589 select_vert.finish();
590}
591
594 blender::BitSpan verts_of_unselected_faces,
595 const bool face_step)
596{
597 for (const int edge_index : face_edges) {
598 const blender::int2 &edge = edges[edge_index];
599 if (face_step) {
600 if (verts_of_unselected_faces[edge[0]] || verts_of_unselected_faces[edge[1]]) {
601 return true;
602 }
603 }
604 else {
605 if (verts_of_unselected_faces[edge[0]] && verts_of_unselected_faces[edge[1]]) {
606 return true;
607 }
608 }
609 }
610 return false;
611}
612
613void paintface_select_less(Mesh *mesh, const bool face_step)
614{
615 using namespace blender;
616
617 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
618 bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
619 ".select_poly", bke::AttrDomain::Face);
620 const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
621 ".hide_poly", bke::AttrDomain::Face, false);
622
623 const OffsetIndices faces = mesh->faces();
624 const Span<int> corner_verts = mesh->corner_verts();
625 const Span<int> corner_edges = mesh->corner_edges();
626 const Span<int2> edges = mesh->edges();
627
628 BitVector<> verts_of_unselected_faces(mesh->verts_num);
629
630 /* Find all vertices of unselected faces to help find neighboring faces after. */
631 for (const int i : faces.index_range()) {
632 if (select_poly.span[i]) {
633 continue;
634 }
635 const IndexRange face = faces[i];
636 for (const int vert : corner_verts.slice(face)) {
637 verts_of_unselected_faces[vert].set(true);
638 }
639 }
640
641 threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
642 for (const int i : range) {
643 if (!select_poly.span[i] || hide_poly[i]) {
644 continue;
645 }
646 const IndexRange face = faces[i];
647 if (poly_has_unselected_neighbor(
648 corner_edges.slice(face), edges, verts_of_unselected_faces, face_step))
649 {
650 select_poly.span[i] = false;
651 }
652 }
653 });
654
655 select_poly.finish();
656}
657
658bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool flush_flags)
659{
660 using namespace blender;
661 Mesh *mesh = BKE_mesh_from_object(ob);
662 if (mesh == nullptr) {
663 return false;
664 }
665
666 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
667 const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
668 ".hide_poly", bke::AttrDomain::Face, false);
669 bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
670 ".select_poly", bke::AttrDomain::Face);
671
672 if (action == SEL_TOGGLE) {
673 action = SEL_SELECT;
674
675 for (int i = 0; i < mesh->faces_num; i++) {
676 if (!hide_poly[i] && select_poly.span[i]) {
677 action = SEL_DESELECT;
678 break;
679 }
680 }
681 }
682
683 bool changed = false;
684
685 for (int i = 0; i < mesh->faces_num; i++) {
686 if (hide_poly[i]) {
687 continue;
688 }
689 const bool old_selection = select_poly.span[i];
690 switch (action) {
691 case SEL_SELECT:
692 select_poly.span[i] = true;
693 break;
694 case SEL_DESELECT:
695 select_poly.span[i] = false;
696 break;
697 case SEL_INVERT:
698 select_poly.span[i] = !select_poly.span[i];
699 changed = true;
700 break;
701 }
702 if (old_selection != select_poly.span[i]) {
703 changed = true;
704 }
705 }
706
707 select_poly.finish();
708
709 if (changed) {
710 if (flush_flags) {
711 paintface_flush_flags(C, ob, true, false);
712 }
713 }
714 return changed;
715}
716
717bool paintface_minmax(Object *ob, float r_min[3], float r_max[3])
718{
719 using namespace blender;
720 bool ok = false;
721 float vec[3], bmat[3][3];
722
723 const Mesh *mesh = BKE_mesh_from_object(ob);
724 if (!mesh || !CustomData_has_layer(&mesh->corner_data, CD_PROP_FLOAT2)) {
725 return ok;
726 }
727
728 copy_m3_m4(bmat, ob->object_to_world().ptr());
729
730 const Span<float3> positions = mesh->vert_positions();
731 const OffsetIndices faces = mesh->faces();
732 const Span<int> corner_verts = mesh->corner_verts();
733 bke::AttributeAccessor attributes = mesh->attributes();
734 const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
735 ".hide_poly", bke::AttrDomain::Face, false);
736 const VArray<bool> select_poly = *attributes.lookup_or_default<bool>(
737 ".select_poly", bke::AttrDomain::Face, false);
738
739 for (int i = 0; i < mesh->faces_num; i++) {
740 if (hide_poly[i] || !select_poly[i]) {
741 continue;
742 }
743
744 for (const int vert : corner_verts.slice(faces[i])) {
745 mul_v3_m3v3(vec, bmat, positions[vert]);
746 add_v3_v3v3(vec, vec, ob->object_to_world().location());
747 minmax_v3v3_v3(r_min, r_max, vec);
748 }
749
750 ok = true;
751 }
752
753 return ok;
754}
755
757 const int mval[2],
759 Object *ob)
760{
761 using namespace blender;
762 uint index;
763 bool changed = false;
764 bool found = false;
765
766 /* Get the face under the cursor */
767 Mesh *mesh = BKE_mesh_from_object(ob);
768
769 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
770 const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
771 ".hide_poly", bke::AttrDomain::Face, false);
772 bke::AttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write<bool>(
773 ".select_poly", bke::AttrDomain::Face);
774
775 if (ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) {
776 if (index < mesh->faces_num) {
777 if (!hide_poly[index]) {
778 found = true;
779 }
780 }
781 }
782
783 if (params->sel_op == SEL_OP_SET) {
784 if ((found && params->select_passthrough) && select_poly.varray[index]) {
785 found = false;
786 }
787 else if (found || params->deselect_all) {
788 /* Deselect everything. */
789 changed |= paintface_deselect_all_visible(C, ob, SEL_DESELECT, false);
790 }
791 }
792
793 if (found) {
794 mesh->act_face = int(index);
795
796 switch (params->sel_op) {
797 case SEL_OP_SET:
798 case SEL_OP_ADD:
799 select_poly.varray.set(index, true);
800 break;
801 case SEL_OP_SUB:
802 select_poly.varray.set(index, false);
803 break;
804 case SEL_OP_XOR:
805 select_poly.varray.set(index, !select_poly.varray[index]);
806 break;
807 case SEL_OP_AND:
808 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
809 break;
810 }
811
812 /* image window redraw */
813
814 paintface_flush_flags(C, ob, true, false);
815 ED_region_tag_redraw(CTX_wm_region(C)); /* XXX: should redraw all 3D views. */
816 changed = true;
817 }
818 select_poly.finish();
819 return changed || found;
820}
821
823{
824 using namespace blender;
825 Mesh *mesh = BKE_mesh_from_object(ob);
826 Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
827 if (mesh == nullptr) {
828 return;
829 }
830
831 /* we could call this directly in all areas that change selection,
832 * since this could become slow for realtime updates (circle-select for eg) */
833 bke::mesh_select_vert_flush(*mesh);
834
835 if (mesh_eval == nullptr) {
836 return;
837 }
838
839 const bke::AttributeAccessor attributes_orig = mesh->attributes();
840 bke::MutableAttributeAccessor attributes_eval = mesh_eval->attributes_for_write();
841
842 const int *orig_indices = (const int *)CustomData_get_layer(&mesh_eval->vert_data, CD_ORIGINDEX);
843
844 const VArray<bool> hide_vert_orig = *attributes_orig.lookup_or_default<bool>(
845 ".hide_vert", bke::AttrDomain::Point, false);
846 bke::SpanAttributeWriter<bool> hide_vert_eval =
847 attributes_eval.lookup_or_add_for_write_only_span<bool>(".hide_vert",
848 bke::AttrDomain::Point);
849 if (orig_indices) {
850 for (const int i : hide_vert_eval.span.index_range()) {
851 if (orig_indices[i] != ORIGINDEX_NONE) {
852 hide_vert_eval.span[i] = hide_vert_orig[orig_indices[i]];
853 }
854 }
855 }
856 else {
857 hide_vert_orig.materialize(hide_vert_eval.span);
858 }
859 hide_vert_eval.finish();
860
861 const VArray<bool> select_vert_orig = *attributes_orig.lookup_or_default<bool>(
862 ".select_vert", bke::AttrDomain::Point, false);
863 bke::SpanAttributeWriter<bool> select_vert_eval =
864 attributes_eval.lookup_or_add_for_write_only_span<bool>(".select_vert",
865 bke::AttrDomain::Point);
866 if (orig_indices) {
867 for (const int i : select_vert_eval.span.index_range()) {
868 if (orig_indices[i] != ORIGINDEX_NONE) {
869 select_vert_eval.span[i] = select_vert_orig[orig_indices[i]];
870 }
871 }
872 }
873 else {
874 select_vert_orig.materialize(select_vert_eval.span);
875 }
876 select_vert_eval.finish();
877
879}
880
882 Object *ob,
883 const blender::Span<int> vertex_indices,
884 const bool select)
885{
886 using namespace blender;
887
888 Mesh *mesh = BKE_mesh_from_object(ob);
889 if (mesh == nullptr || mesh->faces_num == 0) {
890 return;
891 }
892
893 /* AtomicDisjointSet is used to store connection information in vertex indices. */
894 AtomicDisjointSet islands(mesh->verts_num);
895 const Span<int2> edges = mesh->edges();
896
897 /* By calling join() on the vertices of all edges, the AtomicDisjointSet contains information on
898 * which parts of the mesh are connected. */
899 threading::parallel_for(edges.index_range(), 1024, [&](const IndexRange range) {
900 for (const int2 &edge : edges.slice(range)) {
901 islands.join(edge[0], edge[1]);
902 }
903 });
904
905 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
906 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
907 ".select_vert", bke::AttrDomain::Point);
908
909 Set<int> selected_roots;
910
911 for (const int i : vertex_indices) {
912 const int root = islands.find_root(i);
913 selected_roots.add(root);
914 }
915
916 threading::parallel_for(select_vert.span.index_range(), 1024, [&](const IndexRange range) {
917 for (const int i : range) {
918 const int root = islands.find_root(i);
919 if (selected_roots.contains(root)) {
920 select_vert.span[i] = select;
921 }
922 }
923 });
924
925 select_vert.finish();
926
929}
930
932 Object *ob,
933 const int region_coordinates[2],
934 const bool select)
935{
936 uint index = uint(-1);
937 if (!ED_mesh_pick_vert(C, ob, region_coordinates, ED_MESH_PICK_DEFAULT_VERT_DIST, true, &index))
938 {
939 return;
940 }
941
943}
944
946{
947 using namespace blender;
948 Mesh *mesh = BKE_mesh_from_object(ob);
949 if (mesh == nullptr || mesh->faces_num == 0) {
950 return;
951 }
952
953 blender::bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
955 attributes.lookup_or_add_for_write_span<bool>(".select_vert", bke::AttrDomain::Point);
956
958 for (const int i : select_vert.span.index_range()) {
959 if (!select_vert.span[i]) {
960 continue;
961 }
962 indices.append(i);
963 }
964 select_vert.finish();
965 paintvert_select_linked_vertices(C, ob, indices, true);
966}
967
968void paintvert_select_more(Mesh *mesh, const bool face_step)
969{
970 using namespace blender;
971
972 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
973 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
974 ".select_vert", bke::AttrDomain::Point);
975 const VArray<bool> hide_edge = *attributes.lookup_or_default<bool>(
976 ".hide_edge", bke::AttrDomain::Edge, false);
977 const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
978 ".hide_poly", bke::AttrDomain::Face, false);
979
980 const OffsetIndices faces = mesh->faces();
981 const Span<int> corner_edges = mesh->corner_edges();
982 const Span<int> corner_verts = mesh->corner_verts();
983 const Span<int2> edges = mesh->edges();
984
985 Array<int> edge_to_face_offsets;
986 Array<int> edge_to_face_indices;
987 GroupedSpan<int> edge_to_face_map;
988 if (face_step) {
989 edge_to_face_map = bke::mesh::build_edge_to_face_map(
990 faces, corner_edges, mesh->edges_num, edge_to_face_offsets, edge_to_face_indices);
991 }
992
993 /* Need a copy of the selected verts that we can read from and is not modified. */
994 BitVector<> select_vert_original(mesh->verts_num, false);
995 for (int i = 0; i < mesh->verts_num; i++) {
996 select_vert_original[i].set(select_vert.span[i]);
997 }
998
999 /* If we iterated over faces we wouldn't extend the selection through edges that have no face
1000 * attached to them. */
1001 for (const int i : edges.index_range()) {
1002 const int2 &edge = edges[i];
1003 if ((!select_vert_original[edge[0]] && !select_vert_original[edge[1]]) || hide_edge[i]) {
1004 continue;
1005 }
1006 select_vert.span[edge[0]] = true;
1007 select_vert.span[edge[1]] = true;
1008 if (!face_step) {
1009 continue;
1010 }
1011 const Span<int> neighbor_polys = edge_to_face_map[i];
1012 for (const int face_i : neighbor_polys) {
1013 if (hide_poly[face_i]) {
1014 continue;
1015 }
1016 const IndexRange face = faces[face_i];
1017 for (const int vert : corner_verts.slice(face)) {
1018 select_vert.span[vert] = true;
1019 }
1020 }
1021 }
1022
1023 select_vert.finish();
1024}
1025
1026void paintvert_select_less(Mesh *mesh, const bool face_step)
1027{
1028 using namespace blender;
1029
1030 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
1031 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
1032 ".select_vert", bke::AttrDomain::Point);
1033 const VArray<bool> hide_edge = *attributes.lookup_or_default<bool>(
1034 ".hide_edge", bke::AttrDomain::Edge, false);
1035 const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
1036 ".hide_poly", bke::AttrDomain::Face, false);
1037
1038 const OffsetIndices faces = mesh->faces();
1039 const Span<int> corner_edges = mesh->corner_edges();
1040 const Span<int> corner_verts = mesh->corner_verts();
1041 const Span<int2> edges = mesh->edges();
1042
1043 GroupedSpan<int> edge_to_face_map;
1044 Array<int> edge_to_face_offsets;
1045 Array<int> edge_to_face_indices;
1046 if (face_step) {
1047 edge_to_face_map = bke::mesh::build_edge_to_face_map(
1048 faces, corner_edges, edges.size(), edge_to_face_offsets, edge_to_face_indices);
1049 }
1050
1051 /* Need a copy of the selected verts that we can read from and is not modified. */
1052 BitVector<> select_vert_original(mesh->verts_num);
1053 for (int i = 0; i < mesh->verts_num; i++) {
1054 select_vert_original[i].set(select_vert.span[i]);
1055 }
1056
1057 for (const int i : edges.index_range()) {
1058 const int2 &edge = edges[i];
1059 if ((select_vert_original[edge[0]] && select_vert_original[edge[1]]) && !hide_edge[i]) {
1060 continue;
1061 }
1062 select_vert.span[edge[0]] = false;
1063 select_vert.span[edge[1]] = false;
1064
1065 if (!face_step) {
1066 continue;
1067 }
1068 for (const int face_i : edge_to_face_map[i]) {
1069 if (hide_poly[face_i]) {
1070 continue;
1071 }
1072 const IndexRange face = faces[face_i];
1073 for (const int vert : corner_verts.slice(face)) {
1074 select_vert.span[vert] = false;
1075 }
1076 }
1077 }
1078 select_vert.finish();
1079}
1080
1086
1087bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags)
1088{
1089 using namespace blender;
1090 Mesh *mesh = BKE_mesh_from_object(ob);
1091 if (mesh == nullptr) {
1092 return false;
1093 }
1094
1095 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
1096 const VArray<bool> hide_vert = *attributes.lookup_or_default<bool>(
1097 ".hide_vert", bke::AttrDomain::Point, false);
1098 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
1099 ".select_vert", bke::AttrDomain::Point);
1100
1101 if (action == SEL_TOGGLE) {
1102 action = SEL_SELECT;
1103
1104 for (int i = 0; i < mesh->verts_num; i++) {
1105 if (!hide_vert[i] && select_vert.span[i]) {
1106 action = SEL_DESELECT;
1107 break;
1108 }
1109 }
1110 }
1111
1112 bool changed = false;
1113 for (int i = 0; i < mesh->verts_num; i++) {
1114 if (hide_vert[i]) {
1115 continue;
1116 }
1117 const bool old_selection = select_vert.span[i];
1118 switch (action) {
1119 case SEL_SELECT:
1120 select_vert.span[i] = true;
1121 break;
1122 case SEL_DESELECT:
1123 select_vert.span[i] = false;
1124 break;
1125 case SEL_INVERT:
1126 select_vert.span[i] = !select_vert.span[i];
1127 break;
1128 }
1129 if (old_selection != select_vert.span[i]) {
1130 changed = true;
1131 }
1132 }
1133
1134 select_vert.finish();
1135
1136 if (changed) {
1137 /* handle mselect */
1138 if (action == SEL_SELECT) {
1139 /* pass */
1140 }
1141 else if (ELEM(action, SEL_DESELECT, SEL_INVERT)) {
1143 }
1144 else {
1146 }
1147
1148 if (flush_flags) {
1150 }
1151 }
1152 return changed;
1153}
1154
1155void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags)
1156{
1157 using namespace blender;
1158 Mesh *mesh = BKE_mesh_from_object(ob);
1159 if (mesh == nullptr) {
1160 return;
1161 }
1162 const Span<MDeformVert> dverts = mesh->deform_verts();
1163 if (dverts.is_empty()) {
1164 return;
1165 }
1166
1167 if (!extend) {
1169 }
1170
1171 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
1172 const VArray<bool> hide_vert = *attributes.lookup_or_default<bool>(
1173 ".hide_vert", bke::AttrDomain::Point, false);
1174 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
1175 ".select_vert", bke::AttrDomain::Point);
1176
1177 for (const int i : select_vert.span.index_range()) {
1178 if (!hide_vert[i]) {
1179 if (dverts[i].dw == nullptr) {
1180 /* if null weight then not grouped */
1181 select_vert.span[i] = true;
1182 }
1183 }
1184 }
1185
1186 select_vert.finish();
1187
1188 if (flush_flags) {
1190 }
1191}
1192
1193void paintvert_hide(bContext *C, Object *ob, const bool unselected)
1194{
1195 using namespace blender;
1196 Mesh *mesh = BKE_mesh_from_object(ob);
1197 if (mesh == nullptr || mesh->verts_num == 0) {
1198 return;
1199 }
1200
1201 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
1202 bke::SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_span<bool>(
1203 ".hide_vert", bke::AttrDomain::Point);
1204 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
1205 ".select_vert", bke::AttrDomain::Point);
1206
1207 for (const int i : hide_vert.span.index_range()) {
1208 if (!hide_vert.span[i]) {
1209 if (!select_vert.span[i] == unselected) {
1210 hide_vert.span[i] = true;
1211 }
1212 }
1213
1214 if (hide_vert.span[i]) {
1215 select_vert.span[i] = false;
1216 }
1217 }
1218 hide_vert.finish();
1219 select_vert.finish();
1220
1221 bke::mesh_hide_vert_flush(*mesh);
1222
1225}
1226
1227void paintvert_reveal(bContext *C, Object *ob, const bool select)
1228{
1229 using namespace blender;
1230 Mesh *mesh = BKE_mesh_from_object(ob);
1231 if (mesh == nullptr || mesh->verts_num == 0) {
1232 return;
1233 }
1234
1235 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
1236 const VArray<bool> hide_vert = *attributes.lookup_or_default<bool>(
1237 ".hide_vert", bke::AttrDomain::Point, false);
1238 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
1239 ".select_vert", bke::AttrDomain::Point);
1240
1241 for (const int i : select_vert.span.index_range()) {
1242 if (hide_vert[i]) {
1243 select_vert.span[i] = select;
1244 }
1245 }
1246
1247 select_vert.finish();
1248
1249 /* Remove the hide attribute to reveal all vertices. */
1250 attributes.remove(".hide_vert");
1251
1252 bke::mesh_hide_vert_flush(*mesh);
1253
1256}
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
const void * CustomData_get_layer(const CustomData *data, eCustomDataType type)
#define ORIGINDEX_NONE
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
void BKE_mesh_batch_cache_dirty_tag(Mesh *mesh, eMeshBatchDirtyMode mode)
void BKE_mesh_mselect_clear(Mesh *mesh)
void BKE_mesh_mselect_validate(Mesh *mesh)
Mesh * BKE_mesh_from_object(Object *ob)
@ BKE_MESH_BATCH_DIRTY_SELECT_PAINT
Definition BKE_mesh.h:40
@ BKE_MESH_BATCH_DIRTY_ALL
Definition BKE_mesh.h:38
General operations, lookup, etc. for blender objects.
Mesh * BKE_object_get_evaluated_mesh(const Object *object_eval)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
void copy_m3_m4(float m1[3][3], const float m2[4][4])
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3])
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
unsigned int uint
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
@ CD_PROP_FLOAT2
Object is a sort of wrapper for general info.
#define ED_MESH_PICK_DEFAULT_VERT_DIST
Definition ED_mesh.hh:582
bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px, uint *r_index)
#define ED_MESH_PICK_DEFAULT_FACE_DIST
Definition ED_mesh.hh:583
bool ED_mesh_pick_vert(bContext *C, Object *ob, const int mval[2], uint dist_px, bool use_zbuf, uint *r_index)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_AND
@ SEL_OP_XOR
eV3DProjStatus ED_view3d_project_float_object(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
void ED_view3d_init_mats_rv3d(const Object *ob, RegionView3D *rv3d)
eV3DProjStatus
Definition ED_view3d.hh:242
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:243
void ED_view3d_select_id_validate(const ViewContext *vc)
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
#define V3D_PROJ_TEST_CLIP_DEFAULT
Definition ED_view3d.hh:296
Contains defines and structs used throughout the imbuf module.
Read Guarded memory(de)allocation.
#define NC_GEOM
Definition WM_types.hh:360
#define ND_SELECT
Definition WM_types.hh:474
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr int64_t first_index(const T &search_value) const
Definition BLI_span.hh:378
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr bool is_empty() const
Definition BLI_span.hh:261
IndexRange index_range() const
void materialize(MutableSpan< T > r_span) const
bool add(const Key &key)
Span< Key > as_span() const
bool contains(const Key &key) const
void append(const T &value)
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
const Depsgraph * depsgraph
static void build_poly_connections(blender::AtomicDisjointSet &islands, Mesh &mesh, const bool skip_seams=true)
Definition editface.cc:229
static bool follow_face_loop(const int face_start_index, const int edge_start_index, const blender::OffsetIndices< int > faces, const blender::VArray< bool > &hide_poly, const blender::Span< int > corner_edges, const blender::GroupedSpan< int > edge_to_face_map, blender::VectorSet< int > &r_loop_faces)
Definition editface.cc:406
static bool poly_has_unselected_neighbor(blender::Span< int > face_edges, blender::Span< blender::int2 > edges, blender::BitSpan verts_of_unselected_faces, const bool face_step)
Definition editface.cc:592
bool paintface_minmax(Object *ob, float r_min[3], float r_max[3])
Definition editface.cc:717
void paintvert_flush_flags(Object *ob)
Definition editface.cc:822
void paintface_flush_flags(bContext *C, Object *ob, const bool flush_selection, const bool flush_hidden)
Definition editface.cc:49
void paintface_reveal(bContext *C, Object *ob, const bool select)
Definition editface.cc:193
void paintvert_tag_select_update(bContext *C, Object *ob)
Definition editface.cc:1081
void paintface_hide(bContext *C, Object *ob, const bool unselected)
Definition editface.cc:159
void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags)
Definition editface.cc:1155
static int get_opposing_edge_index(const blender::IndexRange face, const blender::Span< int > corner_edges, const int current_edge_index)
Definition editface.cc:389
void paintface_select_less(Mesh *mesh, const bool face_step)
Definition editface.cc:613
void paintface_select_more(Mesh *mesh, const bool face_step)
Definition editface.cc:559
bool paintface_mouse_select(bContext *C, const int mval[2], const SelectPick_Params *params, Object *ob)
Definition editface.cc:756
bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags)
Definition editface.cc:1087
void paintvert_select_linked(bContext *C, Object *ob)
Definition editface.cc:945
void paintvert_select_linked_pick(bContext *C, Object *ob, const int region_coordinates[2], const bool select)
Definition editface.cc:931
static int find_closest_edge_in_poly(ARegion *region, blender::Span< blender::int2 > edges, blender::Span< int > face_edges, blender::Span< blender::float3 > vert_positions, const int mval[2])
Definition editface.cc:359
static void paintface_select_linked_faces(Mesh &mesh, const blender::Span< int > face_indices, const bool select)
Definition editface.cc:275
static bool poly_has_selected_neighbor(blender::Span< int > face_edges, blender::Span< blender::int2 > edges, blender::Span< bool > select_vert, const bool face_step)
Definition editface.cc:536
void paintvert_hide(bContext *C, Object *ob, const bool unselected)
Definition editface.cc:1193
void paintvert_reveal(bContext *C, Object *ob, const bool select)
Definition editface.cc:1227
void paintvert_select_less(Mesh *mesh, const bool face_step)
Definition editface.cc:1026
bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool flush_flags)
Definition editface.cc:658
void paintface_select_loop(bContext *C, Object *ob, const int mval[2], const bool select)
Definition editface.cc:457
void paintvert_select_more(Mesh *mesh, const bool face_step)
Definition editface.cc:968
void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const bool select)
Definition editface.cc:319
static void paintvert_select_linked_vertices(bContext *C, Object *ob, const blender::Span< int > vertex_indices, const bool select)
Definition editface.cc:881
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
static ushort indices[]
static float verts[][3]
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
static BMFace * face_step(BMEdge *edge, BMFace *f)
T midpoint(const T &a, const T &b)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
float distance(float a, float b)
#define FLT_MAX
Definition stdcycles.h:14
Definition DNA_ID.h:413
CustomData face_data
CustomData vert_data
int faces_num
ObjectRuntimeHandle * runtime
void WM_event_add_notifier(const bContext *C, uint type, void *reference)