Blender V5.0
select_engine.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
10
11#include "BKE_editmesh.hh"
12#include "BKE_mesh_types.hh"
13#include "BLI_math_matrix.h"
14
15#include "BLT_translation.hh"
16
18#include "DRW_render.hh"
19#include "ED_view3d.hh"
20
21#include "RE_engine.h"
22
23#include "DRW_engine.hh"
24#include "DRW_select_buffer.hh"
25
26#include "draw_cache_impl.hh"
27#include "draw_common_c.hh"
29#include "draw_manager.hh"
30#include "draw_pass.hh"
31#include "draw_view_data.hh"
32
34
35#include "select_engine.hh"
36
38
39#define USE_CAGE_OCCLUSION
40
41struct Instance : public DrawEngine {
42 private:
43 PassSimple depth_only_ps = {"depth_only_ps"};
44 PassSimple::Sub *depth_only = nullptr;
45 PassSimple::Sub *depth_occlude = nullptr;
46
47 PassSimple select_edge_ps = {"select_id_edge_ps"};
48 PassSimple::Sub *select_edge = nullptr;
49
50 PassSimple select_id_vert_ps = {"select_id_vert_ps"};
51 PassSimple::Sub *select_vert = nullptr;
52
53 PassSimple select_face_ps = {"select_id_face_ps"};
54 PassSimple::Sub *select_face_uniform = nullptr;
55 PassSimple::Sub *select_face_flat = nullptr;
56
57 View view_faces = {"view_faces"};
58 View view_edges = {"view_edges"};
59 View view_verts = {"view_verts"};
60
61 UniformArrayBuffer<float4, 6> clip_planes_buf;
62
63 const DRWContext *draw_ctx = nullptr;
64
65 public:
84
86 {
87 return "SelectID";
88 }
89
90 void init() final
91 {
92 this->draw_ctx = DRW_context_get();
93 StaticData &e_data = StaticData::get();
94 GPUShaderConfig sh_cfg = RV3D_CLIPPING_ENABLED(draw_ctx->v3d, draw_ctx->rv3d) ?
97
98 StaticData::Shaders *sh_data = &e_data.sh_data[sh_cfg];
99
100 /* Prepass */
101 if (!sh_data->select_id_flat) {
103 sh_cfg == GPU_SHADER_CFG_CLIPPED ? "select_id_flat_clipped" : "select_id_flat");
104 }
105 if (!sh_data->select_id_uniform) {
107 sh_cfg == GPU_SHADER_CFG_CLIPPED ? "select_id_uniform_clipped" : "select_id_uniform");
108 }
109 }
110
112 {
113 StaticData &e_data = StaticData::get();
114 GPUShaderConfig sh_cfg = RV3D_CLIPPING_ENABLED(draw_ctx->v3d, draw_ctx->rv3d) ?
117
118 StaticData::Shaders *sh = &e_data.sh_data[sh_cfg];
119
120 if (e_data.context.select_mode == -1) {
121 e_data.context.select_mode = get_object_select_mode(draw_ctx->scene, draw_ctx->obact);
122 BLI_assert(e_data.context.select_mode != 0);
123 }
124
126 if (RV3D_CLIPPING_ENABLED(draw_ctx->v3d, draw_ctx->rv3d)) {
128 }
129
130 bool retopology_occlusion = RETOPOLOGY_ENABLED(draw_ctx->v3d) && !XRAY_ENABLED(draw_ctx->v3d);
131 float retopology_offset = RETOPOLOGY_OFFSET(draw_ctx->v3d);
132
133 for (int i : IndexRange(6)) {
134 clip_planes_buf[i] = float4(0);
135 }
136
137 /* Note there might be less than 6 planes, but we always compute the 6 of them for simplicity.
138 */
139 int clipping_plane_count = RV3D_CLIPPING_ENABLED(draw_ctx->v3d, draw_ctx->rv3d) ? 6 : 0;
140 int plane_len = min((RV3D_LOCK_FLAGS(draw_ctx->rv3d) & RV3D_BOXCLIP) ? 4 : 6,
141 clipping_plane_count);
142
143 for (auto i : IndexRange(plane_len)) {
144 clip_planes_buf[i] = draw_ctx->rv3d->clip[i];
145 }
146
147 clip_planes_buf.push_update();
148
149 {
150 depth_only_ps.init();
151 depth_only_ps.state_set(state, clipping_plane_count);
152 depth_only_ps.bind_ubo(DRW_CLIPPING_UBO_SLOT, clip_planes_buf);
153 depth_only = nullptr;
154 depth_occlude = nullptr;
155 {
156 auto &sub = depth_only_ps.sub("DepthOnly");
157 sub.shader_set(sh->select_id_uniform);
158 sub.push_constant("retopology_offset", retopology_offset);
159 sub.push_constant("select_id", 0);
160 depth_only = &sub;
161 }
162 if (retopology_occlusion) {
163 auto &sub = depth_only_ps.sub("Occlusion");
164 sub.shader_set(sh->select_id_uniform);
165 sub.push_constant("retopology_offset", 0.0f);
166 sub.push_constant("select_id", 0);
167 depth_occlude = &sub;
168 }
169
170 select_face_ps.init();
171 select_face_ps.state_set(state, clipping_plane_count);
172 select_face_ps.bind_ubo(DRW_CLIPPING_UBO_SLOT, clip_planes_buf);
173 select_face_uniform = nullptr;
174 select_face_flat = nullptr;
175 if (e_data.context.select_mode & SCE_SELECT_FACE) {
176 auto &sub = select_face_ps.sub("Face");
177 sub.shader_set(sh->select_id_flat);
178 sub.push_constant("retopology_offset", retopology_offset);
179 select_face_flat = &sub;
180 }
181 else {
182 auto &sub = select_face_ps.sub("FaceNoSelect");
183 sub.shader_set(sh->select_id_uniform);
184 sub.push_constant("select_id", 0);
185 sub.push_constant("retopology_offset", retopology_offset);
186 select_face_uniform = &sub;
187 }
188
189 select_edge_ps.init();
190 select_edge_ps.bind_ubo(DRW_CLIPPING_UBO_SLOT, clip_planes_buf);
191 select_edge = nullptr;
192 if (e_data.context.select_mode & SCE_SELECT_EDGE) {
193 auto &sub = select_edge_ps.sub("Sub");
194 sub.state_set(state | DRW_STATE_FIRST_VERTEX_CONVENTION, clipping_plane_count);
195 sub.shader_set(sh->select_id_flat);
196 sub.push_constant("retopology_offset", retopology_offset);
197 select_edge = &sub;
198 }
199
200 select_id_vert_ps.init();
201 select_id_vert_ps.bind_ubo(DRW_CLIPPING_UBO_SLOT, clip_planes_buf);
202 select_vert = nullptr;
204 const float vertex_size = U.pixelsize *
206 auto &sub = select_id_vert_ps.sub("Sub");
207 sub.state_set(state, clipping_plane_count);
208 sub.shader_set(sh->select_id_flat);
209 sub.push_constant("vertex_size", float(2 * vertex_size));
210 sub.push_constant("retopology_offset", retopology_offset);
211 select_vert = &sub;
212 }
213 }
214
215 e_data.context.elem_ranges.clear();
216
217 e_data.context.persmat = float4x4(draw_ctx->rv3d->persmat);
218 e_data.context.max_index_drawn_len = 1;
219 framebuffer_setup();
222 }
223
225 BMEditMesh *em,
226 ResourceHandleRange res_handle,
227 short select_mode,
228 bool draw_facedot,
229 const uint initial_index)
230 {
231 using namespace blender::draw;
232 using namespace blender;
234
235 ElemIndexRanges ranges{};
236 ranges.total = IndexRange::from_begin_size(initial_index, 0);
237
239
240 if (select_mode & SCE_SELECT_FACE) {
241 ranges.face = alloc_range(ranges.total, em->bm->totface);
242
243 gpu::Batch *geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(mesh);
244 PassSimple::Sub *face_sub = select_face_flat;
245 face_sub->push_constant("offset", int(ranges.face.start()));
246 face_sub->draw(geom_faces, res_handle);
247
248 if (draw_facedot) {
249 gpu::Batch *geom_facedots = DRW_mesh_batch_cache_get_facedots_with_select_id(mesh);
250 face_sub->draw(geom_facedots, res_handle);
251 }
252 }
253 else {
254 if (ob->dt >= OB_SOLID) {
255#ifdef USE_CAGE_OCCLUSION
256 gpu::Batch *geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(mesh);
257#else
258 gpu::Batch *geom_faces = DRW_mesh_batch_cache_get_surface(mesh);
259#endif
260 select_face_uniform->draw(geom_faces, res_handle);
261 }
262 }
263
264 /* Unlike faces, only draw edges if edge select mode. */
265 if (select_mode & SCE_SELECT_EDGE) {
266 ranges.edge = alloc_range(ranges.total, em->bm->totedge);
267
268 gpu::Batch *geom_edges = DRW_mesh_batch_cache_get_edges_with_select_id(mesh);
269 select_edge->push_constant("offset", int(ranges.edge.start()));
270 select_edge->draw(geom_edges, res_handle);
271 }
272
273 /* Unlike faces, only verts if vert select mode. */
274 if (select_mode & SCE_SELECT_VERTEX) {
275 ranges.vert = alloc_range(ranges.total, em->bm->totvert);
276
277 gpu::Batch *geom_verts = DRW_mesh_batch_cache_get_verts_with_select_id(mesh);
278 select_vert->push_constant("offset", int(ranges.vert.start()));
279 select_vert->draw(geom_verts, res_handle);
280 }
281 return ranges;
282 }
283
285 ResourceHandleRange res_handle,
286 short select_mode,
287 const uint initial_index)
288 {
289 using namespace blender::draw;
290 using namespace blender;
292
293 ElemIndexRanges ranges{};
294 ranges.total = IndexRange::from_begin_size(initial_index, 0);
295
296 gpu::Batch *geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(mesh);
297 if (select_mode & SCE_SELECT_FACE) {
298 ranges.face = alloc_range(ranges.total, mesh.faces_num);
299
300 select_face_flat->push_constant("offset", int(ranges.face.start()));
301 select_face_flat->draw(geom_faces, res_handle);
302 }
303 else {
304 /* Only draw faces to mask out verts, we don't want their selection ID's. */
305 select_face_uniform->draw(geom_faces, res_handle);
306 }
307
308 if (select_mode & SCE_SELECT_EDGE) {
309 ranges.edge = alloc_range(ranges.total, mesh.edges_num);
310
311 gpu::Batch *geom_edges = DRW_mesh_batch_cache_get_edges_with_select_id(mesh);
312 select_edge->push_constant("offset", int(ranges.edge.start()));
313 select_edge->draw(geom_edges, res_handle);
314 }
315
316 if (select_mode & SCE_SELECT_VERTEX) {
317 ranges.vert = alloc_range(ranges.total, mesh.verts_num);
318
319 gpu::Batch *geom_verts = DRW_mesh_batch_cache_get_verts_with_select_id(mesh);
320 select_vert->push_constant("offset", int(ranges.vert.start()));
321 select_vert->draw(geom_verts, res_handle);
322 }
323
324 return ranges;
325 }
326
328 View3D *v3d, Object *ob, ResourceHandleRange res_handle, short select_mode, uint index_start)
329 {
330 BLI_assert_msg(index_start > 0, "Index 0 is reserved for no selection");
331
332 switch (ob->type) {
333 case OB_MESH: {
334 const bool is_editmode = ob->mode == OB_MODE_EDIT;
335 /* NOTE: it's important to get the edit-mesh before modifiers have been applied
336 * because the evaluated mesh may not have an edit-mesh, see #138715.
337 * Match edit-mesh access from #mesh_render_data_create. */
338 const Mesh *orig_edit_mesh = is_editmode ? BKE_object_get_pre_modified_mesh(ob) : nullptr;
339 BMEditMesh *em = (orig_edit_mesh) ? orig_edit_mesh->runtime->edit_mesh.get() : nullptr;
340
341 if (em) {
342 bool draw_facedot = check_ob_drawface_dot(select_mode, v3d, eDrawType(ob->dt));
343 return edit_mesh_sync(ob, em, res_handle, select_mode, draw_facedot, index_start);
344 }
345 return mesh_sync(ob, res_handle, select_mode, index_start);
346 }
347 case OB_CURVES_LEGACY:
348 case OB_SURF:
349 break;
350 }
352 return ElemIndexRanges{};
353 }
354
355 void object_sync(ObjectRef &ob_ref, Manager &manager) final
356 {
357 Object *ob = ob_ref.object;
358 StaticData &e_data = StaticData::get();
359 SELECTID_Context &sel_ctx = e_data.context;
360
361 if (!sel_ctx.objects.contains(ob)) {
362 if (ob->dt >= OB_SOLID) {
363 /* This object is not selectable. It is here to participate in occlusion.
364 * This is the case in retopology mode. */
365 blender::gpu::Batch *geom_faces = DRW_mesh_batch_cache_get_surface(
367
368 depth_occlude->draw(geom_faces, manager.unique_handle(ob_ref));
369 }
370 return;
371 }
372
373 /* Only sync selectable object once.
374 * This can happen in retopology mode where there is two sync loop. */
375 sel_ctx.elem_ranges.lookup_or_add_cb(ob, [&]() {
376 ResourceHandleRange res_handle = manager.unique_handle(ob_ref);
377 ElemIndexRanges elem_ranges = object_sync(
378 draw_ctx->v3d, ob, res_handle, sel_ctx.select_mode, sel_ctx.max_index_drawn_len);
379 sel_ctx.max_index_drawn_len = elem_ranges.total.one_after_last();
380 return elem_ranges;
381 });
382 }
383
384 void end_sync() final {}
385
386 void draw(Manager &manager) final
387 {
388 StaticData &e_data = StaticData::get();
389
391 {
392 View::OffsetData offset_data(*draw_ctx->rv3d);
393 /* Create view with depth offset */
394 const View &view = View::default_get();
395 view_faces.sync(view.viewmat(), view.winmat());
396 view_edges.sync(view.viewmat(), offset_data.winmat_polygon_offset(view.winmat(), 1.0f));
397 view_verts.sync(view.viewmat(), offset_data.winmat_polygon_offset(view.winmat(), 1.1f));
398 }
399
400 {
401 DefaultFramebufferList *dfbl = draw_ctx->viewport_framebuffer_list_get();
404 manager.submit(depth_only_ps, view_faces);
405 }
406
407 /* Setup framebuffer */
409
410 manager.submit(select_face_ps, view_faces);
411
412 if (e_data.context.select_mode & SCE_SELECT_EDGE) {
413 manager.submit(select_edge_ps, view_edges);
414 }
415
417 manager.submit(select_id_vert_ps, view_verts);
418 }
420 }
421
422 private:
423 void framebuffer_setup()
424 {
425 StaticData &e_data = StaticData::get();
427 int size[2];
428 size[0] = GPU_texture_width(dtxl->depth);
429 size[1] = GPU_texture_height(dtxl->depth);
430
431 if (e_data.framebuffer_select_id == nullptr) {
432 e_data.framebuffer_select_id = GPU_framebuffer_create("framebuffer_select_id");
433 }
434
435 if ((e_data.texture_u32 != nullptr) && ((GPU_texture_width(e_data.texture_u32) != size[0]) ||
436 (GPU_texture_height(e_data.texture_u32) != size[1])))
437 {
438 GPU_texture_free(e_data.texture_u32);
439 e_data.texture_u32 = nullptr;
440 }
441
442 /* Make sure the depth texture is attached.
443 * It may disappear when loading another Blender session. */
444 GPU_framebuffer_texture_attach(e_data.framebuffer_select_id, dtxl->depth, 0, 0);
445
446 if (e_data.texture_u32 == nullptr) {
448 e_data.texture_u32 = GPU_texture_create_2d(
449 "select_buf_ids", size[0], size[1], 1, gpu::TextureFormat::UINT_32, usage, nullptr);
450 GPU_framebuffer_texture_attach(e_data.framebuffer_select_id, e_data.texture_u32, 0, 0);
451
452 GPU_framebuffer_check_valid(e_data.framebuffer_select_id, nullptr);
453 }
454 }
455
456 short get_object_select_mode(Scene *scene, Object *ob)
457 {
458 short select_mode = 0;
460 /* In order to sample flat colors for vertex weights / texture-paint / vertex-paint
461 * we need to be in SCE_SELECT_FACE mode so select_cache_init() correctly sets up
462 * a shgroup with select_id_flat.
463 * Note this is not working correctly for vertex-paint (yet), but has been discussed
464 * in #66645 and there is a solution by @mano-wii in P1032.
465 * So OB_MODE_VERTEX_PAINT is already included here [required for P1032 I guess]. */
466 Mesh *me_orig = static_cast<Mesh *>(DEG_get_original(ob)->data);
467 if (me_orig->editflag & ME_EDIT_PAINT_VERT_SEL) {
468 select_mode = SCE_SELECT_VERTEX;
469 }
470 else {
471 select_mode = SCE_SELECT_FACE;
472 }
473 }
474 else {
475 select_mode = scene->toolsettings->selectmode;
476 }
477
478 return select_mode;
479 }
480
481 bool check_ob_drawface_dot(short select_mode, const View3D *v3d, eDrawType dt)
482 {
483 if (select_mode & SCE_SELECT_FACE) {
484 if ((dt < OB_SOLID) || XRAY_FLAG_ENABLED(v3d)) {
485 return true;
486 }
488 return true;
489 }
490 }
491 return false;
492 }
493
494 /* Return a new range if size `n` after `total_range` and grow `total_range` by the same amount.
495 */
496 IndexRange alloc_range(IndexRange &total_range, uint size)
497 {
498 const IndexRange indices = total_range.after(size);
499 total_range = IndexRange::from_begin_size(total_range.start(), total_range.size() + size);
500 return indices;
501 }
502};
503
505{
506 return new Instance();
507}
508
510{
512 for (int sh_data_index = 0; sh_data_index < ARRAY_SIZE(e_data.sh_data); sh_data_index++) {
513 Instance::StaticData::Shaders *sh_data = &e_data.sh_data[sh_data_index];
516 }
517
520}
521
522} // namespace blender::draw::edit_select
523
525
526/* -------------------------------------------------------------------- */
529
530using namespace blender::draw::edit_select;
531
537
543
549
const Mesh * BKE_object_get_pre_modified_mesh(const Object *object)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define final(a, b, c)
Definition BLI_hash.h:19
unsigned int uint
#define ARRAY_SIZE(arr)
T * DEG_get_original(T *id)
@ ME_EDIT_PAINT_VERT_SEL
struct Mesh Mesh
eDrawType
@ OB_SOLID
@ OB_MODE_EDIT
@ OB_MODE_WEIGHT_PAINT
@ OB_MODE_TEXTURE_PAINT
@ OB_MODE_VERTEX_PAINT
@ OB_SURF
@ OB_MESH
@ OB_CURVES_LEGACY
struct Object Object
struct Scene Scene
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
#define RV3D_LOCK_FLAGS(rv3d)
#define RV3D_CLIPPING_ENABLED(v3d, rv3d)
@ V3D_OVERLAY_EDIT_FACE_DOT
struct View3D View3D
@ RV3D_BOXCLIP
void DRW_submission_end()
void DRW_submission_start()
T & DRW_object_get_data_for_drawing(const Object &object)
#define XRAY_ENABLED(v3d)
#define RETOPOLOGY_ENABLED(v3d)
#define RETOPOLOGY_OFFSET(v3d)
#define XRAY_FLAG_ENABLED(v3d)
static AppView * view
blender::gpu::FrameBuffer * GPU_framebuffer_create(const char *name)
#define GPU_FRAMEBUFFER_FREE_SAFE(fb)
void GPU_framebuffer_texture_attach(blender::gpu::FrameBuffer *fb, blender::gpu::Texture *texture, int slot, int mip)
bool GPU_framebuffer_check_valid(blender::gpu::FrameBuffer *fb, char err_out[256])
void GPU_framebuffer_clear_depth(blender::gpu::FrameBuffer *fb, float clear_depth)
void GPU_framebuffer_clear_color_depth(blender::gpu::FrameBuffer *fb, const float clear_col[4], float clear_depth)
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
#define GPU_SHADER_FREE_SAFE(shader)
blender::gpu::Shader * GPU_shader_create_from_info_name(const char *info_name)
#define GPU_SHADER_CFG_LEN
@ GPU_SHADER_CFG_DEFAULT
@ GPU_SHADER_CFG_CLIPPED
int GPU_texture_height(const blender::gpu::Texture *texture)
int GPU_texture_width(const blender::gpu::Texture *texture)
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_ATTACHMENT
#define GPU_TEXTURE_FREE_SAFE(texture)
blender::gpu::Texture * GPU_texture_create_2d(const char *name, int width, int height, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
void GPU_texture_free(blender::gpu::Texture *texture)
#define U
BMesh const char void * data
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr int64_t size() const
constexpr IndexRange after(int64_t n) const
constexpr int64_t start() const
void clear()
Definition BLI_map.hh:1038
Value & lookup_or_add_cb(const Key &key, const CreateValueF &create_value)
Definition BLI_map.hh:620
bool contains(const T &value) const
constexpr int64_t one_after_last() const
static constexpr IndexRange from_begin_size(const int64_t begin, const int64_t size)
constexpr int64_t start() const
static View & default_get()
Definition draw_view.cc:317
void draw(gpu::Batch *batch, uint instance_len=-1, uint vertex_len=-1, uint vertex_first=-1, ResourceIndexRange res_index={}, uint custom_id=0)
Definition draw_pass.hh:893
void push_constant(const char *name, const float &data)
detail::PassBase< command::DrawCommandBuf > Sub
Definition draw_pass.hh:499
const DRWContext * DRW_context_get()
#define DRW_CLIPPING_UBO_SLOT
DRWState
Definition draw_state.hh:25
@ DRW_STATE_CLIP_PLANES
Definition draw_state.hh:71
@ DRW_STATE_FIRST_VERTEX_CONVENTION
Definition draw_state.hh:72
#define DRW_STATE_DEFAULT
Definition draw_state.hh:79
static ushort indices[]
static ulong state[N]
blender::gpu::Batch * DRW_mesh_batch_cache_get_facedots_with_select_id(Mesh &mesh)
detail::Pass< command::DrawCommandBuf > PassSimple
blender::gpu::Batch * DRW_mesh_batch_cache_get_surface(Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_triangles_with_select_id(Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_verts_with_select_id(Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edges_with_select_id(Mesh &mesh)
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
blender::gpu::Texture * DRW_engine_select_texture_get()
blender::gpu::FrameBuffer * DRW_engine_select_framebuffer_get()
SELECTID_Context * DRW_select_engine_context_get()
#define min(a, b)
Definition sort.cc:36
int totvert
int totedge
int totface
DefaultTextureList * viewport_texture_list_get() const
blender::gpu::FrameBuffer * depth_only_fb
blender::gpu::Texture * depth
blender::IndexRange face
blender::IndexRange total
blender::IndexRange vert
blender::IndexRange edge
int edges_num
MeshRuntimeHandle * runtime
char editflag
int faces_num
int verts_num
blender::Map< Object *, ElemIndexRanges > elem_ranges
blender::Vector< Object * > objects
blender::float4x4 persmat
struct ToolSettings * toolsettings
View3DOverlay overlay
float4x4 winmat_polygon_offset(const float4x4 &winmat, float offset)
Definition draw_view.hh:197
struct blender::draw::edit_select::Instance::StaticData::Shaders sh_data[GPU_SHADER_CFG_LEN]
blender::StringRefNull name_get() final
void object_sync(ObjectRef &ob_ref, Manager &manager) final
ElemIndexRanges object_sync(View3D *v3d, Object *ob, ResourceHandleRange res_handle, short select_mode, uint index_start)
ElemIndexRanges edit_mesh_sync(Object *ob, BMEditMesh *em, ResourceHandleRange res_handle, short select_mode, bool draw_facedot, const uint initial_index)
void draw(Manager &manager) final
ElemIndexRanges mesh_sync(Object *ob, ResourceHandleRange res_handle, short select_mode, const uint initial_index)
i
Definition text_draw.cc:230