Blender V4.5
button2d_gizmo.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
16
17#include "MEM_guardedalloc.h"
18
19#include "BLI_math_color.h"
20#include "BLI_math_matrix.h"
21#include "BLI_math_vector.h"
23
24#include "DNA_userdef_types.h"
25
26#include "BKE_context.hh"
27
28#include "GPU_batch.hh"
29#include "GPU_batch_utils.hh"
30#include "GPU_immediate.hh"
31#include "GPU_immediate_util.hh"
32#include "GPU_matrix.hh"
33#include "GPU_select.hh"
34#include "GPU_state.hh"
35
36#include "RNA_access.hh"
37#include "RNA_define.hh"
38#include "RNA_enum_types.hh"
39
40#include "WM_api.hh"
41#include "WM_types.hh"
42
43#include "ED_gizmo_library.hh"
44#include "ED_view3d.hh"
45
46#include "UI_interface_icons.hh"
47#include "interface_intern.hh"
48
49/* own includes */
51
52/* -------------------------------------------------------------------- */
55
58 bool is_init;
59 /* Use an icon or shape */
60 int icon;
61 blender::gpu::Batch *shape_batch[2];
62};
63
65
66/* -------------------------------------------------------------------- */
69
71 const float color[4],
72 const float fill_alpha,
73 const bool select,
74 const float screen_scale)
75{
76 float viewport[4];
78
79 const float max_pixel_error = 0.25f;
80 int nsegments = int(ceilf(M_PI / acosf(1.0f - max_pixel_error / screen_scale)));
81 nsegments = max_ff(nsegments, 8);
82 nsegments = min_ff(nsegments, 1000);
83
85 /* NOTE(Metal): Prefer 3D coordinate for 2D rendering when using 3D shader. */
87
88 /* TODO: other draw styles. */
89 if (color[3] == 1.0 && fill_alpha == 1.0 && select == false) {
91 immUniformColor4fv(color);
92 imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
94
96 immUniform2fv("viewportSize", &viewport[2]);
97 immUniform1f("lineWidth", (gz->line_width * U.pixelsize) + WM_gizmo_select_bias(select));
98 immUniformColor4fv(color);
99 imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
101 }
102 else {
103 /* Draw fill. */
104 if ((fill_alpha != 0.0f) || (select == true)) {
105 const float fill_color[4] = {UNPACK3(color), fill_alpha * color[3]};
107 immUniformColor4fv(fill_color);
108 imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
110 }
111
112 /* Draw outline. */
113 if ((fill_alpha != 1.0f) && (select == false)) {
115 immUniform2fv("viewportSize", &viewport[2]);
116 immUniform1f("lineWidth", (gz->line_width * U.pixelsize) + WM_gizmo_select_bias(select));
117 immUniformColor4fv(color);
118 imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
120 }
121 }
122
124}
125
126static void button2d_draw_intern(const bContext *C,
127 wmGizmo *gz,
128 const bool select,
129 const bool highlight)
130{
131 ButtonGizmo2D *button = (ButtonGizmo2D *)gz;
132 float viewport[4];
133 GPU_viewport_size_get_f(viewport);
134
135 const int draw_options = RNA_enum_get(gz->ptr, "draw_options");
136 if (button->is_init == false) {
137 button->is_init = true;
138 button->icon = -1;
139
140 PropertyRNA *icon_prop = RNA_struct_find_property(gz->ptr, "icon");
141 PropertyRNA *icon_value_prop = RNA_struct_find_property(gz->ptr, "icon_value");
142 PropertyRNA *shape_prop = RNA_struct_find_property(gz->ptr, "shape");
143
144 /* Same logic as in the RNA UI API, use icon_value only if icon is not defined. */
145 if (RNA_property_is_set(gz->ptr, icon_prop)) {
146 button->icon = RNA_property_enum_get(gz->ptr, icon_prop);
147 }
148 else if (RNA_property_is_set(gz->ptr, icon_value_prop)) {
149 button->icon = RNA_property_int_get(gz->ptr, icon_value_prop);
150 ui_icon_ensure_deferred(C, button->icon, false);
151 }
152 else if (RNA_property_is_set(gz->ptr, shape_prop)) {
153 const uint polys_len = RNA_property_string_length(gz->ptr, shape_prop);
154 /* We shouldn't need the +1, but a null char is set. */
155 char *polys = MEM_malloc_arrayN<char>(polys_len + 1, __func__);
156 RNA_property_string_get(gz->ptr, shape_prop, polys);
158 (uchar *)polys, polys_len, nullptr);
160 (uchar *)polys, polys_len, nullptr);
161 MEM_freeN(polys);
162 }
163 }
164
165 float color[4];
166 float matrix_final[4][4];
167
168 gizmo_color_get(gz, highlight, color);
169 WM_gizmo_calc_matrix_final(gz, matrix_final);
170
171 bool is_3d = (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) != 0;
172
173 if ((select == false) && (draw_options & ED_GIZMO_BUTTON_SHOW_HELPLINE)) {
174 float matrix_final_no_offset[4][4];
175 WM_gizmo_calc_matrix_final_no_offset(gz, matrix_final_no_offset);
178 immUniform2fv("viewportSize", &viewport[2]);
179 immUniform1f("lineWidth", (gz->line_width * U.pixelsize) + WM_gizmo_select_bias(select));
180 immUniformColor4fv(color);
182 immVertex3fv(pos, matrix_final[3]);
183 immVertex3fv(pos, matrix_final_no_offset[3]);
184 immEnd();
186 }
187
188 bool need_to_pop = true;
190 GPU_matrix_mul(matrix_final);
191
192 float screen_scale = 200.0f;
193 if (is_3d) {
195 float matrix_align[4][4];
196 float matrix_final_unit[4][4];
197 normalize_m4_m4(matrix_final_unit, matrix_final);
198 mul_m4_m4m4(matrix_align, rv3d->viewmat, matrix_final_unit);
199 zero_v3(matrix_align[3]);
200 transpose_m4(matrix_align);
201 GPU_matrix_mul(matrix_align);
202 }
203 else {
204 screen_scale = mat4_to_scale(matrix_final);
205 }
206
207 if (select) {
208 BLI_assert(is_3d);
209 button2d_geom_draw_backdrop(gz, color, 1.0, select, screen_scale);
210 }
211 else {
212
214
215 if (draw_options & ED_GIZMO_BUTTON_SHOW_BACKDROP) {
216 const float fill_alpha = RNA_float_get(gz->ptr, "backdrop_fill_alpha");
217 button2d_geom_draw_backdrop(gz, color, fill_alpha, select, screen_scale);
218 }
219
220 if (button->shape_batch[0] != nullptr) {
221 GPU_line_smooth(true);
222 GPU_polygon_smooth(false);
223 for (uint i = 0; i < ARRAY_SIZE(button->shape_batch) && button->shape_batch[i]; i++) {
224 const bool do_wires = (i == 1);
225 if (do_wires) {
228 GPU_batch_uniform_2fv(button->shape_batch[i], "viewportSize", &viewport[2]);
229 GPU_batch_uniform_1f(button->shape_batch[i], "lineWidth", gz->line_width * U.pixelsize);
230 }
231 else {
233 }
234
235 /* Invert line color for wire. */
236 if (draw_options & ED_GIZMO_BUTTON_SHOW_BACKDROP) {
237 /* If we have a backdrop already,
238 * draw a contrasting shape over it instead of drawing it the same color.
239 * Use a low value instead of 50% so some darker primary colors
240 * aren't considered being close to black. */
241 float color_contrast[4];
242 copy_v3_fl(color_contrast, srgb_to_grayscale(color) < 0.2f ? 1 : 0);
243 color_contrast[3] = color[3];
244 GPU_shader_uniform_4f(button->shape_batch[i]->shader, "color", UNPACK4(color_contrast));
245 }
246 else {
247 GPU_shader_uniform_4f(button->shape_batch[i]->shader, "color", UNPACK4(color));
248 }
249
250 GPU_batch_draw(button->shape_batch[i]);
251
252 if (draw_options & ED_GIZMO_BUTTON_SHOW_OUTLINE) {
253 color[0] = 1.0f - color[0];
254 color[1] = 1.0f - color[1];
255 color[2] = 1.0f - color[2];
256 }
257 }
258 GPU_line_smooth(false);
259 GPU_polygon_smooth(true);
260 }
261 else if (button->icon != -1) {
262 float pos[2];
263 if (is_3d) {
264 const float fac = 2.0f;
265 GPU_matrix_translate_2f(-(fac / 2), -(fac / 2));
268 pos[0] = 1.0f;
269 pos[1] = 1.0f;
270 }
271 else {
272 pos[0] = gz->matrix_basis[3][0] - (ICON_DEFAULT_WIDTH / 2.0) * UI_SCALE_FAC;
273 pos[1] = gz->matrix_basis[3][1] - (ICON_DEFAULT_HEIGHT / 2.0) * UI_SCALE_FAC;
275 need_to_pop = false;
276 }
277
278 float alpha = (highlight) ? 1.0f : 0.8f;
279 GPU_polygon_smooth(false);
280 UI_icon_draw_alpha(pos[0], pos[1], button->icon, alpha);
281 GPU_polygon_smooth(true);
282 }
284 }
285
286 if (need_to_pop) {
288 }
289}
290
291static void gizmo_button2d_draw_select(const bContext *C, wmGizmo *gz, int select_id)
292{
293 GPU_select_load_id(select_id);
294 button2d_draw_intern(C, gz, true, false);
295}
296
297static void gizmo_button2d_draw(const bContext *C, wmGizmo *gz)
298{
299 const bool is_highlight = (gz->state & WM_GIZMO_STATE_HIGHLIGHT) != 0;
300
302 button2d_draw_intern(C, gz, false, is_highlight);
304}
305
306static int gizmo_button2d_test_select(bContext *C, wmGizmo *gz, const int mval[2])
307{
308 float point_local[2];
309
310 if (false) {
311 /* correct, but unnecessarily slow. */
313 C, gz, blender::float2{blender::int2(mval)}, 2, true, point_local) == false)
314 {
315 return -1;
316 }
317 }
318 else {
319 copy_v2_v2(point_local, blender::float2{blender::int2(mval)});
320 sub_v2_v2(point_local, gz->matrix_basis[3]);
321 mul_v2_fl(point_local, 1.0f / gz->scale_final);
322 }
323 /* The 'gz->scale_final' is already applied when projecting. */
324 if (len_squared_v2(point_local) < 1.0f) {
325 return 0;
326 }
327
328 return -1;
329}
330
332{
333 if (RNA_boolean_get(gz->ptr, "show_drag")) {
335 }
336 return WM_CURSOR_DEFAULT;
337}
338
339#define CIRCLE_RESOLUTION_3D 32
340static bool gizmo_button2d_bounds(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
341{
342 ScrArea *area = CTX_wm_area(C);
343 float rad = CIRCLE_RESOLUTION_3D * UI_SCALE_FAC / 2.0f;
344 const float *co = nullptr;
345 float matrix_final[4][4];
346 float co_proj[3];
347 WM_gizmo_calc_matrix_final(gz, matrix_final);
348
350 ARegion *region = CTX_wm_region(C);
351 if (ED_view3d_project_float_global(region, matrix_final[3], co_proj, V3D_PROJ_TEST_NOP) ==
353 {
354 float matrix_final_no_offset[4][4];
355 const RegionView3D *rv3d = static_cast<const RegionView3D *>(region->regiondata);
356 WM_gizmo_calc_matrix_final_no_offset(gz, matrix_final_no_offset);
357 const float factor = ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_final_no_offset[3]) /
358 ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_final[3]);
359 /* It's possible (although unlikely) `matrix_final_no_offset` is behind the view.
360 * `matrix_final` has already been projected so both can't be negative. */
361 if (factor > 0.0f) {
362 rad *= factor;
363 }
364 co = co_proj;
365 }
366 }
367 else {
368 rad = mat4_to_scale(matrix_final);
369 co = matrix_final[3];
370 }
371
372 if (co != nullptr) {
373 r_bounding_box->xmin = co[0] + area->totrct.xmin - rad;
374 r_bounding_box->ymin = co[1] + area->totrct.ymin - rad;
375 r_bounding_box->xmax = r_bounding_box->xmin + rad;
376 r_bounding_box->ymax = r_bounding_box->ymin + rad;
377 return true;
378 }
379 return false;
380}
381
383{
384 ButtonGizmo2D *shape = (ButtonGizmo2D *)gz;
385
386 for (uint i = 0; i < ARRAY_SIZE(shape->shape_batch); i++) {
388 }
389}
390
392
393/* -------------------------------------------------------------------- */
396
398{
399 /* identifiers */
400 gzt->idname = "GIZMO_GT_button_2d";
401
402 /* API callbacks. */
409
410 gzt->struct_size = sizeof(ButtonGizmo2D);
411
412 /* rna */
413 static const EnumPropertyItem rna_enum_draw_options[] = {
414 {ED_GIZMO_BUTTON_SHOW_OUTLINE, "OUTLINE", 0, "Outline", ""},
415 {ED_GIZMO_BUTTON_SHOW_BACKDROP, "BACKDROP", 0, "Backdrop", ""},
416 {ED_GIZMO_BUTTON_SHOW_HELPLINE, "HELPLINE", 0, "Help Line", ""},
417 {0, nullptr, 0, nullptr, nullptr},
418 };
419 PropertyRNA *prop;
420
421 RNA_def_enum_flag(gzt->srna, "draw_options", rna_enum_draw_options, 0, "Draw Options", "");
422
423 prop = RNA_def_property(gzt->srna, "icon", PROP_ENUM, PROP_NONE);
425
426 RNA_def_property(gzt->srna, "icon_value", PROP_INT, PROP_UNSIGNED);
427
428 /* Passed to 'GPU_batch_tris_from_poly_2d_encoded' */
430
431 /* Currently only used for cursor display. */
432 RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", "");
433
434 RNA_def_float(gzt->srna,
435 "backdrop_fill_alpha",
436 1.0f,
437 0.0f,
438 1.0,
439 "When below 1.0, draw the interior with a reduced alpha compared to the outline",
440 "",
441 0.0f,
442 1.0f);
443}
444
449 /* Button Gizmo API */
ScrArea * CTX_wm_area(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
MINLINE float srgb_to_grayscale(const float rgb[3])
#define M_PI
float mat4_to_scale(const float mat[4][4])
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void normalize_m4_m4(float rmat[4][4], const float mat[4][4]) ATTR_NONNULL()
void transpose_m4(float R[4][4])
MINLINE float len_squared_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_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_fl(float r[3], float f)
MINLINE void zero_v3(float r[3])
unsigned char uchar
unsigned int uint
#define UNPACK4(a)
#define ARRAY_SIZE(arr)
#define UNUSED_VARS(...)
#define UNPACK3(a)
#define UI_SCALE_FAC
@ ED_GIZMO_BUTTON_SHOW_BACKDROP
@ ED_GIZMO_BUTTON_SHOW_OUTLINE
@ ED_GIZMO_BUTTON_SHOW_HELPLINE
@ V3D_PROJ_TEST_NOP
Definition ED_view3d.hh:279
float ED_view3d_pixel_size_no_ui_scale(const RegionView3D *rv3d, const float co[3])
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:256
eV3DProjStatus ED_view3d_project_float_global(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
#define GPU_batch_uniform_1f(batch, name, x)
Definition GPU_batch.hh:301
#define GPU_batch_uniform_2fv(batch, name, val)
Definition GPU_batch.hh:307
#define GPU_BATCH_DISCARD_SAFE(batch)
Definition GPU_batch.hh:204
void GPU_batch_program_set_builtin(blender::gpu::Batch *batch, eGPUBuiltinShader shader_id)
void GPU_batch_draw(blender::gpu::Batch *batch)
blender::gpu::Batch * GPU_batch_tris_from_poly_2d_encoded(const uchar *polys_flat, uint polys_flat_len, const rctf *rect) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
blender::gpu::Batch * GPU_batch_wire_from_poly_2d_encoded(const uchar *polys_flat, uint polys_flat_len, const rctf *rect) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void immEnd()
void immUniform2fv(const char *name, const float data[2])
void immUnbindProgram()
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immUniform1f(const char *name, float x)
GPUVertFormat * immVertexFormat()
void immUniformColor4fv(const float rgba[4])
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
void imm_draw_circle_wire_3d(uint pos, float x, float y, float radius, int nsegments)
void imm_draw_circle_fill_3d(uint pos, float x, float y, float radius, int nsegments)
void GPU_matrix_scale_2f(float x, float y)
void GPU_matrix_push()
#define GPU_matrix_mul(x)
void GPU_matrix_pop()
void GPU_matrix_translate_2f(float x, float y)
@ GPU_PRIM_LINE_STRIP
bool GPU_select_load_id(unsigned int id)
void GPU_shader_uniform_4f(GPUShader *sh, const char *name, float x, float y, float z, float w)
@ GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
void GPU_viewport_size_get_f(float coords[4])
Definition gpu_state.cc:273
void GPU_polygon_smooth(bool enable)
Definition gpu_state.cc:83
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, blender::StringRef name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
Read Guarded memory(de)allocation.
@ PROP_ENUM
Definition RNA_types.hh:154
@ PROP_INT
Definition RNA_types.hh:151
@ PROP_STRING
Definition RNA_types.hh:153
@ PROP_BYTESTRING
Definition RNA_types.hh:228
@ PROP_NONE
Definition RNA_types.hh:221
@ PROP_UNSIGNED
Definition RNA_types.hh:237
#define C
Definition RandGen.cpp:29
#define ICON_DEFAULT_HEIGHT
void UI_icon_draw_alpha(float x, float y, int icon_id, float alpha)
#define ICON_DEFAULT_WIDTH
@ WM_GIZMOGROUPTYPE_3D
@ WM_GIZMO_STATE_HIGHLIGHT
#define U
static void GIZMO_GT_button_2d(wmGizmoType *gzt)
static void button2d_draw_intern(const bContext *C, wmGizmo *gz, const bool select, const bool highlight)
static bool gizmo_button2d_bounds(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
static void gizmo_button2d_free(wmGizmo *gz)
static void button2d_geom_draw_backdrop(const wmGizmo *gz, const float color[4], const float fill_alpha, const bool select, const float screen_scale)
static int gizmo_button2d_test_select(bContext *C, wmGizmo *gz, const int mval[2])
static int gizmo_button2d_cursor_get(wmGizmo *gz)
void ED_gizmotypes_button_2d()
#define CIRCLE_RESOLUTION_3D
static void gizmo_button2d_draw_select(const bContext *C, wmGizmo *gz, int select_id)
static void gizmo_button2d_draw(const bContext *C, wmGizmo *gz)
#define ceilf(x)
#define acosf(x)
void gizmo_color_get(const wmGizmo *gz, bool highlight, float r_color[4])
bool gizmo_window_project_2d(bContext *C, const wmGizmo *gz, const float mval[2], int axis, bool use_offset, float r_co[2])
static float WM_gizmo_select_bias(bool select)
uint pos
#define select(A, B, C)
void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool big)
format
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
int RNA_property_int_get(PointerRNA *ptr, PropertyRNA *prop)
float RNA_float_get(PointerRNA *ptr, const char *name)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
std::string RNA_property_string_get(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
int RNA_property_string_length(PointerRNA *ptr, PropertyRNA *prop)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
void RNA_def_property_enum_items(PropertyRNA *prop, const EnumPropertyItem *item)
PropertyRNA * RNA_def_property(StructOrFunctionRNA *cont_, const char *identifier, int type, int subtype)
PropertyRNA * RNA_def_enum_flag(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
const EnumPropertyItem rna_enum_icon_items[]
Definition rna_ui_api.cc:25
void * regiondata
blender::gpu::Batch * shape_batch[2]
float viewmat[4][4]
int ymin
int ymax
int xmin
int xmax
eWM_GizmoFlagGroupTypeFlag flag
wmGizmoGroupType * type
StructRNA * srna
wmGizmoFnDraw draw
wmGizmoFnScreenBoundsGet screen_bounds_get
const char * idname
wmGizmoFnTestSelect test_select
wmGizmoFnCursorGet cursor_get
wmGizmoFnFree free
wmGizmoFnDrawSelect draw_select
wmGizmoGroup * parent_gzgroup
eWM_GizmoFlagState state
float matrix_basis[4][4]
float scale_final
PointerRNA * ptr
float line_width
i
Definition text_draw.cc:230
@ WM_CURSOR_NSEW_SCROLL
Definition wm_cursors.hh:52
@ WM_CURSOR_DEFAULT
Definition wm_cursors.hh:15
void WM_gizmo_calc_matrix_final(const wmGizmo *gz, float r_mat[4][4])
Definition wm_gizmo.cc:570
void WM_gizmo_calc_matrix_final_no_offset(const wmGizmo *gz, float r_mat[4][4])
Definition wm_gizmo.cc:557
void WM_gizmotype_append(void(*gtfunc)(wmGizmoType *))