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
16
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/* -------------------------------------------------------------------- */
51
54 bool is_init;
55 /* Use an icon or shape */
56 int icon;
57 blender::gpu::Batch *shape_batch[2];
58};
59
61
62/* -------------------------------------------------------------------- */
65
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) {
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);
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);
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);
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++) {
376 }
377}
378
380
381/* -------------------------------------------------------------------- */
384
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 /* 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:275
float ED_view3d_pixel_size_no_ui_scale(const RegionView3D *rv3d, const float co[3])
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:252
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.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a color
@ 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 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)
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
VecBase< float, 2 > float2
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
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
@ 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 *))