Blender V4.3
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
17#include "MEM_guardedalloc.h"
18
19#include "BLI_math_color.h"
20#include "BLI_math_matrix.h"
22
23#include "BKE_context.hh"
24
25#include "GPU_batch.hh"
26#include "GPU_batch_utils.hh"
27#include "GPU_immediate.hh"
28#include "GPU_immediate_util.hh"
29#include "GPU_matrix.hh"
30#include "GPU_select.hh"
31#include "GPU_state.hh"
32
33#include "RNA_access.hh"
34#include "RNA_define.hh"
35#include "RNA_enum_types.hh"
36
37#include "WM_api.hh"
38#include "WM_types.hh"
39
40#include "ED_gizmo_library.hh"
41#include "ED_view3d.hh"
42
43#include "UI_interface_icons.hh"
44
45/* own includes */
47
48/* -------------------------------------------------------------------- */
54 bool is_init;
55 /* Use an icon or shape */
56 int icon;
57 blender::gpu::Batch *shape_batch[2];
58};
59
62/* -------------------------------------------------------------------- */
67 const float color[4],
68 const float fill_alpha,
69 const bool select,
70 const float screen_scale)
71{
72 float viewport[4];
74
75 const float max_pixel_error = 0.25f;
76 int nsegments = int(ceilf(M_PI / acosf(1.0f - max_pixel_error / screen_scale)));
77 nsegments = max_ff(nsegments, 8);
78 nsegments = min_ff(nsegments, 1000);
79
81 /* NOTE(Metal): Prefer 3D coordinate for 2D rendering when using 3D shader. */
83
84 /* TODO: other draw styles. */
85 if (color[3] == 1.0 && fill_alpha == 1.0 && select == false) {
87 immUniformColor4fv(color);
88 imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
90
92 immUniform2fv("viewportSize", &viewport[2]);
93 immUniform1f("lineWidth", gz->line_width * U.pixelsize);
94 immUniformColor4fv(color);
95 imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
97 }
98 else {
99 /* Draw fill. */
100 if ((fill_alpha != 0.0f) || (select == true)) {
101 const float fill_color[4] = {UNPACK3(color), fill_alpha * color[3]};
103 immUniformColor4fv(fill_color);
104 imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
106 }
107
108 /* Draw outline. */
109 if ((fill_alpha != 1.0f) && (select == false)) {
111 immUniform2fv("viewportSize", &viewport[2]);
112 immUniform1f("lineWidth", gz->line_width * U.pixelsize);
113 immUniformColor4fv(color);
114 imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
116 }
117 }
118
120}
121
122static void button2d_draw_intern(const bContext *C,
123 wmGizmo *gz,
124 const bool select,
125 const bool highlight)
126{
127 ButtonGizmo2D *button = (ButtonGizmo2D *)gz;
128 float viewport[4];
129 GPU_viewport_size_get_f(viewport);
130
131 const int draw_options = RNA_enum_get(gz->ptr, "draw_options");
132 if (button->is_init == false) {
133 button->is_init = true;
134 PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon");
135 button->icon = -1;
136 if (RNA_property_is_set(gz->ptr, prop)) {
137 button->icon = RNA_property_enum_get(gz->ptr, prop);
138 }
139 else {
140 prop = RNA_struct_find_property(gz->ptr, "shape");
141 const uint polys_len = RNA_property_string_length(gz->ptr, prop);
142 /* We shouldn't need the +1, but a nullptr char is set. */
143 char *polys = static_cast<char *>(MEM_mallocN(polys_len + 1, __func__));
144 RNA_property_string_get(gz->ptr, prop, polys);
146 (uchar *)polys, polys_len, nullptr);
148 (uchar *)polys, polys_len, nullptr);
149 MEM_freeN(polys);
150 }
151 }
152
153 float color[4];
154 float matrix_final[4][4];
155
156 gizmo_color_get(gz, highlight, color);
157 WM_gizmo_calc_matrix_final(gz, matrix_final);
158
159 bool is_3d = (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) != 0;
160
161 if ((select == false) && (draw_options & ED_GIZMO_BUTTON_SHOW_HELPLINE)) {
162 float matrix_final_no_offset[4][4];
163 WM_gizmo_calc_matrix_final_no_offset(gz, matrix_final_no_offset);
166 immUniform2fv("viewportSize", &viewport[2]);
167 immUniform1f("lineWidth", gz->line_width * U.pixelsize);
168 immUniformColor4fv(color);
170 immVertex3fv(pos, matrix_final[3]);
171 immVertex3fv(pos, matrix_final_no_offset[3]);
172 immEnd();
174 }
175
176 bool need_to_pop = true;
178 GPU_matrix_mul(matrix_final);
179
180 float screen_scale = 200.0f;
181 if (is_3d) {
183 float matrix_align[4][4];
184 float matrix_final_unit[4][4];
185 normalize_m4_m4(matrix_final_unit, matrix_final);
186 mul_m4_m4m4(matrix_align, rv3d->viewmat, matrix_final_unit);
187 zero_v3(matrix_align[3]);
188 transpose_m4(matrix_align);
189 GPU_matrix_mul(matrix_align);
190 }
191 else {
192 screen_scale = mat4_to_scale(matrix_final);
193 }
194
195 if (select) {
196 BLI_assert(is_3d);
197 button2d_geom_draw_backdrop(gz, color, 1.0, select, screen_scale);
198 }
199 else {
200
202
203 if (draw_options & ED_GIZMO_BUTTON_SHOW_BACKDROP) {
204 const float fill_alpha = RNA_float_get(gz->ptr, "backdrop_fill_alpha");
205 button2d_geom_draw_backdrop(gz, color, fill_alpha, select, screen_scale);
206 }
207
208 if (button->shape_batch[0] != nullptr) {
209 GPU_line_smooth(true);
210 GPU_polygon_smooth(false);
211 for (uint i = 0; i < ARRAY_SIZE(button->shape_batch) && button->shape_batch[i]; i++) {
212 const bool do_wires = (i == 1);
213 if (do_wires) {
216 GPU_batch_uniform_2fv(button->shape_batch[i], "viewportSize", &viewport[2]);
217 GPU_batch_uniform_1f(button->shape_batch[i], "lineWidth", gz->line_width * U.pixelsize);
218 }
219 else {
221 }
222
223 /* Invert line color for wire. */
224 if (draw_options & ED_GIZMO_BUTTON_SHOW_BACKDROP) {
225 /* If we have a backdrop already,
226 * draw a contrasting shape over it instead of drawing it the same color.
227 * Use a low value instead of 50% so some darker primary colors
228 * aren't considered being close to black. */
229 float color_contrast[4];
230 copy_v3_fl(color_contrast, rgb_to_grayscale(color) < 0.2f ? 1 : 0);
231 color_contrast[3] = color[3];
232 GPU_shader_uniform_4f(button->shape_batch[i]->shader, "color", UNPACK4(color_contrast));
233 }
234 else {
235 GPU_shader_uniform_4f(button->shape_batch[i]->shader, "color", UNPACK4(color));
236 }
237
238 GPU_batch_draw(button->shape_batch[i]);
239
240 if (draw_options & ED_GIZMO_BUTTON_SHOW_OUTLINE) {
241 color[0] = 1.0f - color[0];
242 color[1] = 1.0f - color[1];
243 color[2] = 1.0f - color[2];
244 }
245 }
246 GPU_line_smooth(false);
247 GPU_polygon_smooth(true);
248 }
249 else if (button->icon != -1) {
250 float pos[2];
251 if (is_3d) {
252 const float fac = 2.0f;
253 GPU_matrix_translate_2f(-(fac / 2), -(fac / 2));
256 pos[0] = 1.0f;
257 pos[1] = 1.0f;
258 }
259 else {
260 pos[0] = gz->matrix_basis[3][0] - (ICON_DEFAULT_WIDTH / 2.0) * UI_SCALE_FAC;
261 pos[1] = gz->matrix_basis[3][1] - (ICON_DEFAULT_HEIGHT / 2.0) * UI_SCALE_FAC;
263 need_to_pop = false;
264 }
265
266 float alpha = (highlight) ? 1.0f : 0.8f;
267 GPU_polygon_smooth(false);
268 UI_icon_draw_alpha(pos[0], pos[1], button->icon, alpha);
269 GPU_polygon_smooth(true);
270 }
272 }
273
274 if (need_to_pop) {
276 }
277}
278
279static void gizmo_button2d_draw_select(const bContext *C, wmGizmo *gz, int select_id)
280{
281 GPU_select_load_id(select_id);
282 button2d_draw_intern(C, gz, true, false);
283}
284
285static void gizmo_button2d_draw(const bContext *C, wmGizmo *gz)
286{
287 const bool is_highlight = (gz->state & WM_GIZMO_STATE_HIGHLIGHT) != 0;
288
290 button2d_draw_intern(C, gz, false, is_highlight);
292}
293
294static int gizmo_button2d_test_select(bContext *C, wmGizmo *gz, const int mval[2])
295{
296 float point_local[2];
297
298 if (false) {
299 /* correct, but unnecessarily slow. */
301 C, gz, blender::float2{blender::int2(mval)}, 2, true, point_local) == false)
302 {
303 return -1;
304 }
305 }
306 else {
307 copy_v2_v2(point_local, blender::float2{blender::int2(mval)});
308 sub_v2_v2(point_local, gz->matrix_basis[3]);
309 mul_v2_fl(point_local, 1.0f / gz->scale_final);
310 }
311 /* The 'gz->scale_final' is already applied when projecting. */
312 if (len_squared_v2(point_local) < 1.0f) {
313 return 0;
314 }
315
316 return -1;
317}
318
320{
321 if (RNA_boolean_get(gz->ptr, "show_drag")) {
323 }
324 return WM_CURSOR_DEFAULT;
325}
326
327#define CIRCLE_RESOLUTION_3D 32
328static bool gizmo_button2d_bounds(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
329{
330 ScrArea *area = CTX_wm_area(C);
331 float rad = CIRCLE_RESOLUTION_3D * UI_SCALE_FAC / 2.0f;
332 const float *co = nullptr;
333 float matrix_final[4][4];
334 float co_proj[3];
335 WM_gizmo_calc_matrix_final(gz, matrix_final);
336
338 ARegion *region = CTX_wm_region(C);
339 if (ED_view3d_project_float_global(region, matrix_final[3], co_proj, V3D_PROJ_TEST_NOP) ==
341 {
342 float matrix_final_no_offset[4][4];
343 const RegionView3D *rv3d = static_cast<const RegionView3D *>(region->regiondata);
344 WM_gizmo_calc_matrix_final_no_offset(gz, matrix_final_no_offset);
345 const float factor = ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_final_no_offset[3]) /
346 ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_final[3]);
347 /* It's possible (although unlikely) `matrix_final_no_offset` is behind the view.
348 * `matrix_final` has already been projected so both can't be negative. */
349 if (factor > 0.0f) {
350 rad *= factor;
351 }
352 co = co_proj;
353 }
354 }
355 else {
356 rad = mat4_to_scale(matrix_final);
357 co = matrix_final[3];
358 }
359
360 if (co != nullptr) {
361 r_bounding_box->xmin = co[0] + area->totrct.xmin - rad;
362 r_bounding_box->ymin = co[1] + area->totrct.ymin - rad;
363 r_bounding_box->xmax = r_bounding_box->xmin + rad;
364 r_bounding_box->ymax = r_bounding_box->ymin + rad;
365 return true;
366 }
367 return false;
368}
369
371{
372 ButtonGizmo2D *shape = (ButtonGizmo2D *)gz;
373
374 for (uint i = 0; i < ARRAY_SIZE(shape->shape_batch); i++) {
375 GPU_BATCH_DISCARD_SAFE(shape->shape_batch[i]);
376 }
377}
378
381/* -------------------------------------------------------------------- */
386{
387 /* identifiers */
388 gzt->idname = "GIZMO_GT_button_2d";
389
390 /* api callbacks */
397
398 gzt->struct_size = sizeof(ButtonGizmo2D);
399
400 /* rna */
401 static const EnumPropertyItem rna_enum_draw_options[] = {
402 {ED_GIZMO_BUTTON_SHOW_OUTLINE, "OUTLINE", 0, "Outline", ""},
403 {ED_GIZMO_BUTTON_SHOW_BACKDROP, "BACKDROP", 0, "Backdrop", ""},
404 {ED_GIZMO_BUTTON_SHOW_HELPLINE, "HELPLINE", 0, "Help Line", ""},
405 {0, nullptr, 0, nullptr, nullptr},
406 };
407 PropertyRNA *prop;
408
409 RNA_def_enum_flag(gzt->srna, "draw_options", rna_enum_draw_options, 0, "Draw Options", "");
410
411 prop = RNA_def_property(gzt->srna, "icon", PROP_ENUM, PROP_NONE);
413
414 /* Passed to 'GPU_batch_tris_from_poly_2d_encoded' */
416
417 /* Currently only used for cursor display. */
418 RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", "");
419
420 RNA_def_float(gzt->srna,
421 "backdrop_fill_alpha",
422 1.0f,
423 0.0f,
424 1.0,
425 "When below 1.0, draw the interior with a reduced alpha compared to the outline",
426 "",
427 0.0f,
428 1.0f);
429}
430
435
436 /* 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:50
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
#define M_PI
MINLINE float rgb_to_grayscale(const float rgb[3])
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:266
float ED_view3d_pixel_size_no_ui_scale(const RegionView3D *rv3d, const float co[3])
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:243
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:299
#define GPU_batch_uniform_2fv(batch, name, val)
Definition GPU_batch.hh:305
#define GPU_BATCH_DISCARD_SAFE(batch)
Definition GPU_batch.hh:205
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:262
void GPU_polygon_smooth(bool enable)
Definition gpu_state.cc:83
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
Read Guarded memory(de)allocation.
@ PROP_ENUM
Definition RNA_types.hh:69
@ PROP_STRING
Definition RNA_types.hh:68
@ PROP_BYTESTRING
Definition RNA_types.hh:143
@ PROP_NONE
Definition RNA_types.hh:136
#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
unsigned int U
Definition btGjkEpa3.h:78
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)
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
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])
format
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
VecBase< int32_t, 2 > int2
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_is_set(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:31
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
@ WM_CURSOR_NSEW_SCROLL
Definition wm_cursors.hh:51
@ 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:569
void WM_gizmo_calc_matrix_final_no_offset(const wmGizmo *gz, float r_mat[4][4])
Definition wm_gizmo.cc:556
void WM_gizmotype_append(void(*gtfunc)(wmGizmoType *))