Blender V5.0
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.hh"
47#include "UI_interface_icons.hh"
48#include "interface_intern.hh"
49
50/* own includes */
52
53/* -------------------------------------------------------------------- */
56
59 bool is_init;
60 /* Use an icon or shape */
61 int icon;
62 blender::gpu::Batch *shape_batch[2];
63};
64
66
67/* -------------------------------------------------------------------- */
70
72 const float color[4],
73 const float fill_alpha,
74 const bool select,
75 const float screen_scale)
76{
77 float viewport[4];
79
80 const float max_pixel_error = 0.25f;
81 int nsegments = int(ceilf(M_PI / acosf(1.0f - max_pixel_error / screen_scale)));
82 nsegments = max_ff(nsegments, 8);
83 nsegments = min_ff(nsegments, 1000);
84
86 /* NOTE(Metal): Prefer 3D coordinate for 2D rendering when using 3D shader. */
87 uint pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
88
89 /* TODO: other draw styles. */
90 if (color[3] == 1.0 && fill_alpha == 1.0 && select == false) {
92 immUniformColor4fv(color);
93 imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
95
97 immUniform2fv("viewportSize", &viewport[2]);
98 immUniform1f("lineWidth", (gz->line_width * U.pixelsize) + WM_gizmo_select_bias(select));
99 immUniformColor4fv(color);
100 imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
102 }
103 else {
104 /* Draw fill. */
105 if ((fill_alpha != 0.0f) || (select == true)) {
106 const float fill_color[4] = {UNPACK3(color), fill_alpha * color[3]};
108 immUniformColor4fv(fill_color);
109 imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
111 }
112
113 /* Draw outline. */
114 if ((fill_alpha != 1.0f) && (select == false)) {
116 immUniform2fv("viewportSize", &viewport[2]);
117 immUniform1f("lineWidth", (gz->line_width * U.pixelsize) + WM_gizmo_select_bias(select));
118 immUniformColor4fv(color);
119 imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, nsegments);
121 }
122 }
123
125}
126
127static void button2d_draw_intern(const bContext *C,
128 wmGizmo *gz,
129 const bool select,
130 const bool highlight)
131{
132 ButtonGizmo2D *button = (ButtonGizmo2D *)gz;
133 float viewport[4];
134 GPU_viewport_size_get_f(viewport);
135
136 const int draw_options = RNA_enum_get(gz->ptr, "draw_options");
137 if (button->is_init == false) {
138 button->is_init = true;
139 button->icon = -1;
140
141 PropertyRNA *icon_prop = RNA_struct_find_property(gz->ptr, "icon");
142 PropertyRNA *icon_value_prop = RNA_struct_find_property(gz->ptr, "icon_value");
143 PropertyRNA *shape_prop = RNA_struct_find_property(gz->ptr, "shape");
144
145 /* Same logic as in the RNA UI API, use icon_value only if icon is not defined. */
146 if (RNA_property_is_set(gz->ptr, icon_prop)) {
147 button->icon = RNA_property_enum_get(gz->ptr, icon_prop);
148 }
149 else if (RNA_property_is_set(gz->ptr, icon_value_prop)) {
150 button->icon = RNA_property_int_get(gz->ptr, icon_value_prop);
151 ui_icon_ensure_deferred(C, button->icon, false);
152 }
153 else if (RNA_property_is_set(gz->ptr, shape_prop)) {
154 const uint polys_len = RNA_property_string_length(gz->ptr, shape_prop);
155 if (LIKELY(polys_len > 0)) {
156 char *polys = MEM_malloc_arrayN<char>(polys_len, __func__);
157 RNA_property_string_get(gz->ptr, shape_prop, polys);
158 /* Subtract 1 because this holds a null byte. */
160 (const uchar *)polys, polys_len - 1, nullptr);
162 (const uchar *)polys, polys_len - 1, nullptr);
163 MEM_freeN(polys);
164 }
165 }
166 }
167
168 float color[4];
169 float matrix_final[4][4];
170
171 gizmo_color_get(gz, highlight, color);
172 WM_gizmo_calc_matrix_final(gz, matrix_final);
173
174 bool is_3d = (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) != 0;
175
176 if ((select == false) && (draw_options & ED_GIZMO_BUTTON_SHOW_HELPLINE)) {
177 float matrix_final_no_offset[4][4];
178 WM_gizmo_calc_matrix_final_no_offset(gz, matrix_final_no_offset);
180 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
182 immUniform2fv("viewportSize", &viewport[2]);
183 immUniform1f("lineWidth", (gz->line_width * U.pixelsize) + WM_gizmo_select_bias(select));
184 immUniformColor4fv(color);
186 immVertex3fv(pos, matrix_final[3]);
187 immVertex3fv(pos, matrix_final_no_offset[3]);
188 immEnd();
190 }
191
192 bool need_to_pop = true;
194 GPU_matrix_mul(matrix_final);
195
196 float screen_scale = 200.0f;
197 if (is_3d) {
199 float matrix_align[4][4];
200 float matrix_final_unit[4][4];
201 normalize_m4_m4(matrix_final_unit, matrix_final);
202 mul_m4_m4m4(matrix_align, rv3d->viewmat, matrix_final_unit);
203 zero_v3(matrix_align[3]);
204 transpose_m4(matrix_align);
205 GPU_matrix_mul(matrix_align);
206 }
207 else {
208 screen_scale = mat4_to_scale(matrix_final);
209 }
210
211 if (select) {
212 BLI_assert(is_3d);
213 button2d_geom_draw_backdrop(gz, color, 1.0, select, screen_scale);
214 }
215 else {
216
218
219 if (draw_options & ED_GIZMO_BUTTON_SHOW_BACKDROP) {
220 const float fill_alpha = RNA_float_get(gz->ptr, "backdrop_fill_alpha");
221 button2d_geom_draw_backdrop(gz, color, fill_alpha, select, screen_scale);
222 }
223
224 if (button->shape_batch[0] != nullptr) {
225 GPU_line_smooth(true);
226 GPU_polygon_smooth(false);
227 for (uint i = 0; i < ARRAY_SIZE(button->shape_batch) && button->shape_batch[i]; i++) {
228 const bool do_wires = (i == 1);
229 if (do_wires) {
232 GPU_batch_uniform_2fv(button->shape_batch[i], "viewportSize", &viewport[2]);
233 GPU_batch_uniform_1f(button->shape_batch[i], "lineWidth", gz->line_width * U.pixelsize);
234 }
235 else {
237 }
238
239 /* Invert line color for wire. */
240 if (draw_options & ED_GIZMO_BUTTON_SHOW_BACKDROP) {
241 /* If we have a backdrop already,
242 * draw a contrasting shape over it instead of drawing it the same color.
243 * Use a low value instead of 50% so some darker primary colors
244 * aren't considered being close to black. */
245 float color_contrast[4];
246 copy_v3_fl(color_contrast, srgb_to_grayscale(color) < 0.2f ? 1 : 0);
247 color_contrast[3] = color[3];
248 GPU_shader_uniform_4f(button->shape_batch[i]->shader, "color", UNPACK4(color_contrast));
249 }
250 else {
251 GPU_shader_uniform_4f(button->shape_batch[i]->shader, "color", UNPACK4(color));
252 }
253
254 GPU_batch_draw(button->shape_batch[i]);
255
256 if (draw_options & ED_GIZMO_BUTTON_SHOW_OUTLINE) {
257 color[0] = 1.0f - color[0];
258 color[1] = 1.0f - color[1];
259 color[2] = 1.0f - color[2];
260 }
261 }
262 GPU_line_smooth(false);
263 GPU_polygon_smooth(true);
264 }
265 else if (button->icon != -1) {
266 float pos[2];
267 if (is_3d) {
268 const float fac = 2.0f;
269 GPU_matrix_translate_2f(-(fac / 2), -(fac / 2));
272 pos[0] = 1.0f;
273 pos[1] = 1.0f;
274 }
275 else {
276 pos[0] = gz->matrix_basis[3][0] - (ICON_DEFAULT_WIDTH / 2.0) * UI_SCALE_FAC;
277 pos[1] = gz->matrix_basis[3][1] - (ICON_DEFAULT_HEIGHT / 2.0) * UI_SCALE_FAC;
279 need_to_pop = false;
280 }
281
282 float alpha = (highlight) ? 1.0f : 0.8f;
283 GPU_polygon_smooth(false);
284 UI_icon_draw_alpha(pos[0], pos[1], button->icon, alpha);
285 GPU_polygon_smooth(true);
286 }
288 }
289
290 if (need_to_pop) {
292 }
293}
294
295static void gizmo_button2d_draw_select(const bContext *C, wmGizmo *gz, int select_id)
296{
297 GPU_select_load_id(select_id);
298 button2d_draw_intern(C, gz, true, false);
299}
300
301static void gizmo_button2d_draw(const bContext *C, wmGizmo *gz)
302{
303 const bool is_highlight = (gz->state & WM_GIZMO_STATE_HIGHLIGHT) != 0;
304
306 button2d_draw_intern(C, gz, false, is_highlight);
308}
309
310static int gizmo_button2d_test_select(bContext *C, wmGizmo *gz, const int mval[2])
311{
312 float point_local[2];
313
314 if (false) {
315 /* correct, but unnecessarily slow. */
317 C, gz, blender::float2{blender::int2(mval)}, 2, true, point_local) == false)
318 {
319 return -1;
320 }
321 }
322 else {
323 copy_v2_v2(point_local, blender::float2{blender::int2(mval)});
324 sub_v2_v2(point_local, gz->matrix_basis[3]);
325 mul_v2_fl(point_local, 1.0f / gz->scale_final);
326 }
327 /* The 'gz->scale_final' is already applied when projecting. */
328 if (len_squared_v2(point_local) < 1.0f) {
329 return 0;
330 }
331
332 return -1;
333}
334
336{
337 if (RNA_boolean_get(gz->ptr, "show_drag")) {
339 }
340 return WM_CURSOR_DEFAULT;
341}
342
343#define CIRCLE_RESOLUTION_3D 32
344static bool gizmo_button2d_bounds(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
345{
346 ScrArea *area = CTX_wm_area(C);
347 float rad = CIRCLE_RESOLUTION_3D * UI_SCALE_FAC / 2.0f;
348 const float *co = nullptr;
349 float matrix_final[4][4];
350 float co_proj[3];
351 WM_gizmo_calc_matrix_final(gz, matrix_final);
352
354 ARegion *region = CTX_wm_region(C);
355 if (ED_view3d_project_float_global(region, matrix_final[3], co_proj, V3D_PROJ_TEST_NOP) ==
357 {
358 float matrix_final_no_offset[4][4];
359 const RegionView3D *rv3d = static_cast<const RegionView3D *>(region->regiondata);
360 WM_gizmo_calc_matrix_final_no_offset(gz, matrix_final_no_offset);
361 const float factor = ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_final_no_offset[3]) /
362 ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_final[3]);
363 /* It's possible (although unlikely) `matrix_final_no_offset` is behind the view.
364 * `matrix_final` has already been projected so both can't be negative. */
365 if (factor > 0.0f) {
366 rad *= factor;
367 }
368 co = co_proj;
369 }
370 }
371 else {
372 rad = mat4_to_scale(matrix_final);
373 co = matrix_final[3];
374 }
375
376 if (co != nullptr) {
377 r_bounding_box->xmin = co[0] + area->totrct.xmin - rad;
378 r_bounding_box->ymin = co[1] + area->totrct.ymin - rad;
379 r_bounding_box->xmax = r_bounding_box->xmin + rad;
380 r_bounding_box->ymax = r_bounding_box->ymin + rad;
381 return true;
382 }
383 return false;
384}
385
387{
388 ButtonGizmo2D *shape = (ButtonGizmo2D *)gz;
389
390 for (uint i = 0; i < ARRAY_SIZE(shape->shape_batch); i++) {
392 }
393}
394
396
397/* -------------------------------------------------------------------- */
400
402{
403 /* identifiers */
404 gzt->idname = "GIZMO_GT_button_2d";
405
406 /* API callbacks. */
413
414 gzt->struct_size = sizeof(ButtonGizmo2D);
415
416 /* rna */
417 static const EnumPropertyItem rna_enum_draw_options[] = {
418 {ED_GIZMO_BUTTON_SHOW_OUTLINE, "OUTLINE", 0, "Outline", ""},
419 {ED_GIZMO_BUTTON_SHOW_BACKDROP, "BACKDROP", 0, "Backdrop", ""},
420 {ED_GIZMO_BUTTON_SHOW_HELPLINE, "HELPLINE", 0, "Help Line", ""},
421 {0, nullptr, 0, nullptr, nullptr},
422 };
423 PropertyRNA *prop;
424
425 RNA_def_enum_flag(gzt->srna, "draw_options", rna_enum_draw_options, 0, "Draw Options", "");
426
427 prop = RNA_def_property(gzt->srna, "icon", PROP_ENUM, PROP_NONE);
429
430 RNA_def_property(gzt->srna, "icon_value", PROP_INT, PROP_UNSIGNED);
431
432 /* Passed to 'GPU_batch_tris_from_poly_2d_encoded' */
434
435 /* Currently only used for cursor display. */
436 RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", "");
437
438 RNA_def_float(gzt->srna,
439 "backdrop_fill_alpha",
440 1.0f,
441 0.0f,
442 1.0,
443 "When below 1.0, draw the interior with a reduced alpha compared to the outline",
444 "",
445 0.0f,
446 1.0f);
447}
448
453 /* 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 LIKELY(x)
#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:271
void GPU_batch_program_set_builtin(blender::gpu::Batch *batch, GPUBuiltinShader shader_id)
#define GPU_batch_uniform_2fv(batch, name, val)
Definition GPU_batch.hh:277
#define GPU_BATCH_DISCARD_SAFE(batch)
Definition GPU_batch.hh:197
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(GPUBuiltinShader 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(blender::gpu::Shader *sh, const char *name, float x, float y, float z, float w)
@ GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR
@ GPU_SHADER_3D_UNIFORM_COLOR
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(GPUBlend blend)
Definition gpu_state.cc:42
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
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
Read Guarded memory(de)allocation.
@ PROP_ENUM
Definition RNA_types.hh:166
@ PROP_INT
Definition RNA_types.hh:163
@ PROP_STRING
Definition RNA_types.hh:165
@ PROP_BYTESTRING
Definition RNA_types.hh:240
@ PROP_NONE
Definition RNA_types.hh:233
@ PROP_UNSIGNED
Definition RNA_types.hh:249
#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 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
#define ceilf
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:26
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:572
void WM_gizmo_calc_matrix_final_no_offset(const wmGizmo *gz, float r_mat[4][4])
Definition wm_gizmo.cc:559
void WM_gizmotype_append(void(*gtfunc)(wmGizmoType *))