Blender V4.3
draw_select_buffer.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2019 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include "MEM_guardedalloc.h"
12
13#include "BLI_array_utils.h"
14#include "BLI_bitmap.h"
15#include "BLI_bitmap_draw_2d.h"
16#include "BLI_rect.h"
17
18#include "DNA_screen_types.h"
19
20#include "GPU_select.hh"
21
22#include "DEG_depsgraph.hh"
24
25#include "DRW_engine.hh"
26#include "DRW_select_buffer.hh"
27
28#include "draw_manager_c.hh"
29
31
32using blender::int2;
33using blender::Span;
34
36{
37 /* Check if the viewport has changed. */
38 float(*persmat)[4] = rv3d->persmat;
39 bool is_dirty = !compare_m4m4(this->persmat, persmat, FLT_EPSILON);
40
41 if (!is_dirty) {
42 /* Check if any of the drawn objects have been transformed. */
43 for (Object *obj_eval : this->objects) {
44 DrawData *data = DRW_drawdata_get(&obj_eval->id, &draw_engine_select_type);
45 if (!data || (data->recalc & ID_RECALC_TRANSFORM)) {
46 is_dirty = true;
47 break;
48 }
49 }
50 }
51
52 return is_dirty;
53}
54
55/* -------------------------------------------------------------------- */
60 Depsgraph *depsgraph, ARegion *region, View3D *v3d, const rcti *rect, uint *r_buf_len)
61{
62 uint *r_buf = nullptr;
63 uint buf_len = 0;
64
65 /* Clamp rect. */
66 rcti r{};
67 r.xmin = 0;
68 r.xmax = region->winx;
69 r.ymin = 0;
70 r.ymax = region->winy;
71
72 /* Make sure that the rect is within the bounds of the viewport.
73 * Some GPUs have problems reading pixels off limits. */
74 rcti rect_clamp = *rect;
75 if (BLI_rcti_isect(&r, &rect_clamp, &rect_clamp)) {
77 RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
78
80
81 if (select_ctx->is_dirty(rv3d)) {
82 /* Update drawing. */
83 DRW_draw_select_id(depsgraph, region, v3d);
84 }
85
86 if (select_ctx->index_drawn_len > 1) {
89
90 /* Read the UI32 pixels. */
91 buf_len = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect);
92 r_buf = static_cast<uint *>(MEM_mallocN(buf_len * sizeof(*r_buf), __func__));
93
94 GPUFrameBuffer *select_id_fb = DRW_engine_select_framebuffer_get();
95 GPU_framebuffer_bind(select_id_fb);
96 GPU_framebuffer_read_color(select_id_fb,
97 rect_clamp.xmin,
98 rect_clamp.ymin,
99 BLI_rcti_size_x(&rect_clamp),
100 BLI_rcti_size_y(&rect_clamp),
101 1,
102 0,
104 r_buf);
105
106 if (!BLI_rcti_compare(rect, &rect_clamp)) {
107 /* The rect has been clamped so we need to realign the buffer and fill in the blanks */
108 GPU_select_buffer_stride_realign(rect, &rect_clamp, r_buf);
109 }
110 }
111
114 }
115
116 if (r_buf_len) {
117 *r_buf_len = buf_len;
118 }
119
120 return r_buf;
121}
122
125/* -------------------------------------------------------------------- */
134 Depsgraph *depsgraph, ARegion *region, View3D *v3d, const rcti *rect, uint *r_bitmap_len)
135{
137
138 rcti rect_px = *rect;
139 rect_px.xmax += 1;
140 rect_px.ymax += 1;
141
142 uint buf_len;
143 uint *buf = DRW_select_buffer_read(depsgraph, region, v3d, &rect_px, &buf_len);
144 if (buf == nullptr) {
145 return nullptr;
146 }
147
148 BLI_assert(select_ctx->index_drawn_len > 0);
149 const uint bitmap_len = select_ctx->index_drawn_len - 1;
150
151 BLI_bitmap *bitmap_buf = BLI_BITMAP_NEW(bitmap_len, __func__);
152 const uint *buf_iter = buf;
153 while (buf_len--) {
154 const uint index = *buf_iter - 1;
155 if (index < bitmap_len) {
156 BLI_BITMAP_ENABLE(bitmap_buf, index);
157 }
158 buf_iter++;
159 }
160 MEM_freeN((void *)buf);
161
162 if (r_bitmap_len) {
163 *r_bitmap_len = bitmap_len;
164 }
165
166 return bitmap_buf;
167}
168
170 ARegion *region,
171 View3D *v3d,
172 const int center[2],
173 const int radius,
174 uint *r_bitmap_len)
175{
177
178 rcti rect{};
179 rect.xmin = center[0] - radius;
180 rect.xmax = center[0] + radius + 1;
181 rect.ymin = center[1] - radius;
182 rect.ymax = center[1] + radius + 1;
183
184 const uint *buf = DRW_select_buffer_read(depsgraph, region, v3d, &rect, nullptr);
185
186 if (buf == nullptr) {
187 return nullptr;
188 }
189
190 BLI_assert(select_ctx->index_drawn_len > 0);
191 const uint bitmap_len = select_ctx->index_drawn_len - 1;
192
193 BLI_bitmap *bitmap_buf = BLI_BITMAP_NEW(bitmap_len, __func__);
194 const uint *buf_iter = buf;
195 const int radius_sq = radius * radius;
196 for (int yc = -radius; yc <= radius; yc++) {
197 for (int xc = -radius; xc <= radius; xc++, buf_iter++) {
198 if (xc * xc + yc * yc < radius_sq) {
199 /* Intentionally wrap to max value if this is zero. */
200 const uint index = *buf_iter - 1;
201 if (index < bitmap_len) {
202 BLI_BITMAP_ENABLE(bitmap_buf, index);
203 }
204 }
205 }
206 }
207 MEM_freeN((void *)buf);
208
209 if (r_bitmap_len) {
210 *r_bitmap_len = bitmap_len;
211 }
212
213 return bitmap_buf;
214}
215
220
221static void drw_select_mask_px_cb(int x, int x_end, int y, void *user_data)
222{
223 PolyMaskData *data = static_cast<PolyMaskData *>(user_data);
224 BLI_bitmap *px = data->px;
225 int i = (y * data->width) + x;
226 do {
227 BLI_BITMAP_ENABLE(px, i);
228 i++;
229 } while (++x != x_end);
230}
231
233 ARegion *region,
234 View3D *v3d,
235 const Span<int2> poly,
236 const rcti *rect,
237 uint *r_bitmap_len)
238{
240
241 rcti rect_px = *rect;
242 rect_px.xmax += 1;
243 rect_px.ymax += 1;
244
245 uint buf_len;
246 uint *buf = DRW_select_buffer_read(depsgraph, region, v3d, &rect_px, &buf_len);
247 if (buf == nullptr) {
248 return nullptr;
249 }
250
251 BLI_bitmap *buf_mask = BLI_BITMAP_NEW(buf_len, __func__);
252
253 PolyMaskData poly_mask_data;
254 poly_mask_data.px = buf_mask;
255 poly_mask_data.width = (rect->xmax - rect->xmin) + 1;
256
258 rect_px.ymin,
259 rect_px.xmax,
260 rect_px.ymax,
261 poly,
263 &poly_mask_data);
264
265 BLI_assert(select_ctx->index_drawn_len > 0);
266 const uint bitmap_len = select_ctx->index_drawn_len - 1;
267
268 BLI_bitmap *bitmap_buf = BLI_BITMAP_NEW(bitmap_len, __func__);
269 const uint *buf_iter = buf;
270 int i = 0;
271 while (buf_len--) {
272 const uint index = *buf_iter - 1;
273 if (index < bitmap_len && BLI_BITMAP_TEST(buf_mask, i)) {
274 BLI_BITMAP_ENABLE(bitmap_buf, index);
275 }
276 buf_iter++;
277 i++;
278 }
279 MEM_freeN((void *)buf);
280 MEM_freeN(buf_mask);
281
282 if (r_bitmap_len) {
283 *r_bitmap_len = bitmap_len;
284 }
285
286 return bitmap_buf;
287}
288
291/* -------------------------------------------------------------------- */
299 ARegion *region,
300 View3D *v3d,
301 const int center[2])
302{
303 uint ret = 0;
304
305 rcti rect{};
306 rect.xmin = center[0];
307 rect.xmax = center[0] + 1;
308 rect.ymin = center[1];
309 rect.ymax = center[1] + 1;
310
311 uint buf_len;
312 uint *buf = DRW_select_buffer_read(depsgraph, region, v3d, &rect, &buf_len);
313 if (buf) {
314 BLI_assert(0 != buf_len);
315 ret = buf[0];
316 MEM_freeN(buf);
317 }
318
319 return ret;
320}
321
328
329static bool select_buffer_test_fn(const void *__restrict value, void *__restrict userdata)
330{
331 SelectReadData *data = static_cast<SelectReadData *>(userdata);
332 uint hit_id = *(uint *)value;
333 if (hit_id && hit_id >= data->id_min && hit_id < data->id_max) {
334 /* Start at 1 to confirm. */
335 data->val_ptr = value;
336 data->r_index = (hit_id - data->id_min) + 1;
337 return true;
338 }
339 return false;
340}
341
343 ARegion *region,
344 View3D *v3d,
345 const int center[2],
346 const uint id_min,
347 const uint id_max,
348 uint *dist)
349{
350 /* Create region around center (typically the mouse cursor).
351 * This must be square and have an odd width. */
352
353 rcti rect;
354 BLI_rcti_init_pt_radius(&rect, center, *dist);
355 rect.xmax += 1;
356 rect.ymax += 1;
357
358 int width = BLI_rcti_size_x(&rect);
359 int height = width;
360
361 /* Read from selection framebuffer. */
362
363 uint buf_len;
364 const uint *buf = DRW_select_buffer_read(depsgraph, region, v3d, &rect, &buf_len);
365
366 if (buf == nullptr) {
367 return 0;
368 }
369
370 const int shape[2] = {height, width};
371 const int center_yx[2] = {(height - 1) / 2, (width - 1) / 2};
372 SelectReadData data = {nullptr, id_min, id_max, 0};
373 BLI_array_iter_spiral_square(buf, shape, center_yx, select_buffer_test_fn, &data);
374
375 if (data.val_ptr) {
376 size_t offset = (size_t(data.val_ptr) - size_t(buf)) / sizeof(*buf);
377 int hit_x = offset % width;
378 int hit_y = offset / width;
379 *dist = uint(abs(hit_y - center_yx[0]) + abs(hit_x - center_yx[1]));
380 }
381
382 MEM_freeN((void *)buf);
383 return data.r_index;
384}
385
388/* -------------------------------------------------------------------- */
393 uint *r_elem,
394 uint *r_base_index,
395 char *r_elem_type)
396{
398
399 char elem_type = 0;
400 uint elem_id = 0;
401 uint base_index = 0;
402
403 for (; base_index < select_ctx->objects.size(); base_index++) {
404 ObjectOffsets *base_ofs = &select_ctx->index_offsets[base_index];
405
406 if (base_ofs->face > sel_id) {
407 elem_id = sel_id - base_ofs->face_start;
408 elem_type = SCE_SELECT_FACE;
409 break;
410 }
411 if (base_ofs->edge > sel_id) {
412 elem_id = sel_id - base_ofs->edge_start;
413 elem_type = SCE_SELECT_EDGE;
414 break;
415 }
416 if (base_ofs->vert > sel_id) {
417 elem_id = sel_id - base_ofs->vert_start;
418 elem_type = SCE_SELECT_VERTEX;
419 break;
420 }
421 }
422
423 if (base_index == select_ctx->objects.size()) {
424 return false;
425 }
426
427 *r_elem = elem_id;
428
429 if (r_base_index) {
430 *r_base_index = base_index;
431 }
432
433 if (r_elem_type) {
434 *r_elem_type = elem_type;
435 }
436
437 return true;
438}
439
441 Object *object,
442 char elem_type)
443{
445
446 Object *ob_eval = DEG_get_evaluated_object(depsgraph, object);
447
449 &ob_eval->id, &draw_engine_select_type);
450
451 if (!sel_data) {
452 return 0;
453 }
454
455 ObjectOffsets *base_ofs = &select_ctx->index_offsets[sel_data->drawn_index];
456
457 if (elem_type == SCE_SELECT_VERTEX) {
458 return base_ofs->vert_start;
459 }
460 if (elem_type == SCE_SELECT_EDGE) {
461 return base_ofs->edge_start;
462 }
463 if (elem_type == SCE_SELECT_FACE) {
464 return base_ofs->face_start;
465 }
466 BLI_assert(0);
467 return 0;
468}
469
472/* -------------------------------------------------------------------- */
477 const blender::Span<Base *> bases,
478 short select_mode)
479{
481
482 select_ctx->objects.reinitialize(bases.size());
483 select_ctx->index_offsets.reinitialize(bases.size());
484
485 for (const int i : bases.index_range()) {
486 Object *obj = bases[i]->object;
487 select_ctx->objects[i] = DEG_get_evaluated_object(depsgraph, obj);
488 }
489
490 select_ctx->select_mode = select_mode;
491 memset(select_ctx->persmat, 0, sizeof(select_ctx->persmat));
492}
493
Generic array manipulation API.
#define BLI_array_iter_spiral_square(arr, arr_shape, center, test_fn, user_data)
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_BITMAP_NEW(_num, _alloc_string)
Definition BLI_bitmap.h:41
#define BLI_BITMAP_TEST(_bitmap, _index)
Definition BLI_bitmap.h:65
#define BLI_BITMAP_ENABLE(_bitmap, _index)
Definition BLI_bitmap.h:82
unsigned int BLI_bitmap
Definition BLI_bitmap.h:17
void BLI_bitmap_draw_2d_poly_v2i_n(int xmin, int ymin, int xmax, int ymax, blender::Span< blender::int2 > verts, void(*callback)(int x, int x_end, int y, void *), void *user_data)
bool compare_m4m4(const float mat1[4][4], const float mat2[4][4], float limit)
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:193
bool BLI_rcti_compare(const struct rcti *rect_a, const struct rcti *rect_b)
void BLI_rcti_init_pt_radius(struct rcti *rect, const int xy[2], int size)
Definition rct.c:470
bool BLI_rcti_isect(const struct rcti *src1, const struct rcti *src2, struct rcti *dest)
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:189
unsigned int uint
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1021
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
void DRW_gpu_context_disable()
void DRW_gpu_context_enable()
void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d)
void GPU_framebuffer_restore()
void GPU_framebuffer_bind(GPUFrameBuffer *framebuffer)
void GPU_framebuffer_read_color(GPUFrameBuffer *framebuffer, int x, int y, int width, int height, int channels, int slot, eGPUDataFormat data_format, void *r_data)
void GPU_select_buffer_stride_realign(const rcti *src, const rcti *dst, uint *r_buf)
int GPU_texture_height(const GPUTexture *texture)
int GPU_texture_width(const GPUTexture *texture)
@ GPU_DATA_UINT
Read Guarded memory(de)allocation.
int64_t size() const
Definition BLI_array.hh:245
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
const Depsgraph * depsgraph
DrawData * DRW_drawdata_get(ID *id, DrawEngineType *engine_type)
uint * DRW_select_buffer_bitmap_from_rect(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const rcti *rect, uint *r_bitmap_len)
bool DRW_select_buffer_elem_get(const uint sel_id, uint *r_elem, uint *r_base_index, char *r_elem_type)
static bool select_buffer_test_fn(const void *__restrict value, void *__restrict userdata)
uint * DRW_select_buffer_bitmap_from_circle(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const int center[2], const int radius, uint *r_bitmap_len)
void DRW_select_buffer_context_create(Depsgraph *depsgraph, const blender::Span< Base * > bases, short select_mode)
uint * DRW_select_buffer_bitmap_from_poly(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const Span< int2 > poly, const rcti *rect, uint *r_bitmap_len)
uint DRW_select_buffer_sample_point(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const int center[2])
uint DRW_select_buffer_context_offset_for_object_elem(Depsgraph *depsgraph, Object *object, char elem_type)
uint DRW_select_buffer_find_nearest_to_point(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const int center[2], const uint id_min, const uint id_max, uint *dist)
uint * DRW_select_buffer_read(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const rcti *rect, uint *r_buf_len)
static void drw_select_mask_px_cb(int x, int x_end, int y, void *user_data)
draw_view in_light_buf[] float
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
return ret
DrawEngineType draw_engine_select_type
SELECTID_Context * DRW_select_engine_context_get()
GPUFrameBuffer * DRW_engine_select_framebuffer_get()
GPUTexture * DRW_engine_select_texture_get()
float persmat[4][4]
blender::Array< Object * > objects
blender::Array< ObjectOffsets > index_offsets
bool is_dirty(RegionView3D *rv3d)
int ymin
int ymax
int xmin
int xmax
ccl_device_inline int abs(int x)
Definition util/math.h:120