Blender V4.3
view3d_gizmo_navigate_type.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 <algorithm>
18
19#include "MEM_guardedalloc.h"
20
21#include "BLI_math_matrix.h"
23#include "BLI_sort_utils.h"
24
25#include "BKE_context.hh"
26
27#include "GPU_batch.hh"
28#include "GPU_immediate.hh"
29#include "GPU_matrix.hh"
30#include "GPU_state.hh"
31
32#include "BLF_api.hh"
33
34#include "UI_interface.hh"
35#include "UI_resources.hh"
36
37#include "WM_api.hh"
38#include "WM_types.hh"
39
40#include "view3d_intern.hh"
41
42/* Radius of the entire background. */
43#define WIDGET_RADIUS ((U.gizmo_size_navigate_v3d / 2.0f) * UI_SCALE_FAC)
44
45/* Sizes of axis spheres containing XYZ characters in relation to above. */
46#define AXIS_HANDLE_SIZE 0.20f
47
48#define AXIS_LINE_WIDTH ((U.gizmo_size_navigate_v3d / 40.0f) * U.pixelsize)
49#define AXIS_RING_WIDTH ((U.gizmo_size_navigate_v3d / 60.0f) * U.pixelsize)
50#define AXIS_TEXT_SIZE (WIDGET_RADIUS * AXIS_HANDLE_SIZE * 1.25f)
51
52/* distance within this from center is considered positive. */
53#define AXIS_DEPTH_BIAS 0.01f
54
55static void gizmo_axis_draw(const bContext *C, wmGizmo *gz)
56{
57 struct {
58 float depth;
59 char index;
60 char axis;
61 char axis_opposite;
62 bool is_pos;
63 } axis_order[6] = {
64 {-gz->matrix_offset[0][2], 0, 0, 1, false},
65 {+gz->matrix_offset[0][2], 1, 0, 0, true},
66 {-gz->matrix_offset[1][2], 2, 1, 3, false},
67 {+gz->matrix_offset[1][2], 3, 1, 2, true},
68 {-gz->matrix_offset[2][2], 4, 2, 5, false},
69 {+gz->matrix_offset[2][2], 5, 2, 4, true},
70 };
71
72 int axis_align = -1;
73 for (int axis = 0; axis < 3; axis++) {
74 if (len_squared_v2(gz->matrix_offset[axis]) < 1e-6f) {
75 axis_align = axis;
76 break;
77 }
78 }
79
80 qsort(&axis_order, ARRAY_SIZE(axis_order), sizeof(axis_order[0]), BLI_sortutil_cmp_float);
81
82 /* When the cursor is over any of the gizmos (show circle backdrop). */
83 const bool is_active = ((gz->state & WM_GIZMO_STATE_HIGHLIGHT) != 0);
84
85 /* Background color of the View3D, used to mix colors. */
86 float view_color[4];
88 view_color[3] = 1.0f;
89
90 float matrix_screen[4][4];
91 float matrix_unit[4][4];
92 unit_m4(matrix_unit);
93
95 params.matrix_offset = matrix_unit;
96 WM_gizmo_calc_matrix_final_params(gz, &params, matrix_screen);
98 GPU_matrix_mul(matrix_screen);
99
102 const uint color_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
103 float viewport_size[4];
104 GPU_viewport_size_get_f(viewport_size);
105
106 static float axis_color[3][4];
107
108 struct {
109 float matrix[4][4];
110 float matrix_m3[3][3];
111 float matrix_m3_invert[3][3];
112 int id;
113 } font;
114
115 font.id = BLF_default();
117 BLF_enable(font.id, BLF_BOLD);
118 BLF_size(font.id, AXIS_TEXT_SIZE);
119 BLF_position(font.id, 0, 0, 0);
120
121 /* Calculate the inverse of the (matrix_final * matrix_offset).
122 * This allows us to use the final location, while reversing the rotation so fonts
123 * show without any rotation. */
124 float m3[3][3];
125 float m3_offset[3][3];
126 copy_m3_m4(m3, matrix_screen);
127 copy_m3_m4(m3_offset, gz->matrix_offset);
128 mul_m3_m3m3(m3, m3, m3_offset);
129 copy_m3_m3(font.matrix_m3_invert, m3);
130 invert_m3(m3);
131 copy_m3_m3(font.matrix_m3, m3);
132 copy_m4_m3(font.matrix, m3);
133
134 bool use_project_matrix = (gz->scale_final >= -GPU_MATRIX_ORTHO_CLIP_NEAR_DEFAULT);
135 if (use_project_matrix) {
138 }
139
141 GPU_polygon_smooth(false);
142
143 /* Circle defining active area. */
144 if (is_active) {
145 const float rad = WIDGET_RADIUS;
147 GPU_matrix_scale_1f(1.0f / rad);
148
149 rctf rect{};
150 rect.xmin = -rad;
151 rect.xmax = rad;
152 rect.ymin = -rad;
153 rect.ymax = rad;
154 UI_draw_roundbox_4fv(&rect, true, rad, gz->color_hi);
156 }
157
159
160 for (int axis_index = 0; axis_index < ARRAY_SIZE(axis_order); axis_index++) {
161 const int index = axis_order[axis_index].index;
162 const int axis = axis_order[axis_index].axis;
163 const bool is_pos = axis_order[axis_index].is_pos;
164 const float depth = axis_order[axis_index].depth;
165 const bool is_behind = (depth <= (AXIS_DEPTH_BIAS * (is_pos ? -1 : 1)));
166 bool is_aligned_front = (axis_align != -1 && axis_align == axis && !is_behind);
167 bool is_aligned_back = (axis_align != -1 && axis_align == axis && is_behind);
168
169 const float v[3] = {0, 0, (1.0f - AXIS_HANDLE_SIZE) * (is_pos ? 1 : -1)};
170 const float v_final[3] = {v[(axis + 2) % 3], v[(axis + 1) % 3], v[axis]};
171
172 bool is_highlight = index + 1 == gz->highlight_part;
173 /* Check if highlight part is the other side when axis aligned. */
174 if (is_aligned_front && (axis_order[axis_index].axis_opposite + 1 == gz->highlight_part)) {
175 is_highlight = true;
176 }
177
178 UI_GetThemeColor3fv(TH_AXIS_X + axis, axis_color[axis]);
179 axis_color[axis][3] = 1.0f;
180
181 /* Color that is full at front, but 50% view background when in back. */
182 float fading_color[4];
183 interp_v4_v4v4(fading_color, view_color, axis_color[axis], ((depth + 1) * 0.25) + 0.5);
184
185 /* Color that is midway between front and back. */
186 float middle_color[4];
187 interp_v4_v4v4(middle_color, view_color, axis_color[axis], 0.75f);
188
190
191 /* Axis Line. */
192 if (is_pos || axis_align != -1) {
193
194 /* Extend slightly to meet better at the center. */
195 float v_start[3] = {0.0f, 0.0f, 0.0f};
196 mul_v3_v3fl(v_start, v_final, -(AXIS_LINE_WIDTH / WIDGET_RADIUS * 0.66f));
197
198 /* Decrease length of line by ball radius. */
199 float v_end[3] = {0.0f, 0.0f, 0.0f};
200 mul_v3_v3fl(v_end, v_final, 1.0f - AXIS_HANDLE_SIZE);
201
203 immUniform2fv("viewportSize", &viewport_size[2]);
204 immUniform1f("lineWidth", AXIS_LINE_WIDTH);
206 immAttr4fv(color_id, middle_color);
207 immVertex3fv(pos_id, v_start);
208 immAttr4fv(color_id, fading_color);
209 immVertex3fv(pos_id, v_end);
210 immEnd();
212 }
213
214 /* Axis Ball. */
215 if (!is_aligned_back) {
216 float *inner_color = fading_color;
217 float *outline_color = fading_color;
218 float negative_color[4];
219 if (!is_pos) {
220 if (is_aligned_front) {
222 negative_color, blender::float4{1.0f, 1.0f, 1.0f, 1.0f}, axis_color[axis], 0.5f);
223 negative_color[3] = std::min(depth + 1, 1.0f);
224 outline_color = negative_color;
225 }
226 else {
227 interp_v4_v4v4(negative_color, view_color, axis_color[axis], 0.25f);
228 negative_color[3] = std::min(depth + 1, 1.0f);
229 inner_color = negative_color;
230 }
231 }
232
235 GPU_matrix_mul(font.matrix);
236 /* Size change from back to front: 0.92f - 1.08f. */
237 float scale = ((depth + 1) * 0.08f) + 0.92f;
238 const float rad = WIDGET_RADIUS * AXIS_HANDLE_SIZE * scale;
239 rctf rect{};
240 rect.xmin = -rad;
241 rect.xmax = rad;
242 rect.ymin = -rad;
243 rect.ymax = rad;
245 &rect, inner_color, nullptr, 0.0f, outline_color, AXIS_RING_WIDTH, rad);
247 }
248
249 /* Axis XYZ Character. */
250 if ((is_pos || is_highlight || (axis == axis_align)) && !is_aligned_back) {
251 float axis_str_width, axis_string_height;
252 char axis_str[3] = {char('X' + axis), 0, 0};
253 if (!is_pos) {
254 axis_str[0] = '-';
255 axis_str[1] = 'X' + axis;
256 }
257 BLF_width_and_height(font.id, axis_str, 3, &axis_str_width, &axis_string_height);
258
259 /* Calculate pixel-aligned location, without this text draws fuzzy. */
260 float v_final_px[3];
261 mul_v3_m3v3(v_final_px, font.matrix_m3_invert, v_final);
262 /* Center the text and pixel align, it's important to round once
263 * otherwise the characters are noticeably not-centered.
264 * If this wasn't an issue we could use #BLF_position to place the text. */
265 v_final_px[0] = roundf(v_final_px[0] - (axis_str_width * (is_pos ? 0.5f : 0.55f)));
266 v_final_px[1] = roundf(v_final_px[1] - (axis_string_height / 2.0f));
267 mul_m3_v3(font.matrix_m3, v_final_px);
269 GPU_matrix_translate_3fv(v_final_px);
270 GPU_matrix_mul(font.matrix);
271 float text_color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
272 if (!is_highlight) {
273 zero_v4(text_color);
274 text_color[3] = is_active ? 1.0f : 0.9f;
275 }
276 BLF_color4fv(font.id, text_color);
277 BLF_draw(font.id, axis_str, 2);
279 }
280 }
281
282 if (use_project_matrix) {
284 }
285
287 BLF_disable(font.id, BLF_BOLD);
289}
290
291static int gizmo_axis_test_select(bContext * /*C*/, wmGizmo *gz, const int mval[2])
292{
293 float point_local[2] = {float(mval[0]), float(mval[1])};
294 sub_v2_v2(point_local, gz->matrix_basis[3]);
295 mul_v2_fl(point_local, 1.0f / gz->scale_final);
296
297 const float len_sq = len_squared_v2(point_local);
298 if (len_sq > 1.0) {
299 return -1;
300 }
301
302 int part_best = -1;
303 int part_index = 1;
304 /* Use 'SQUARE(HANDLE_SIZE)' if we want to be able to _not_ focus on one of the axis. */
305 float i_best_len_sq = FLT_MAX;
306 for (int i = 0; i < 3; i++) {
307 for (int is_pos = 0; is_pos < 2; is_pos++) {
308 const float co[2] = {
309 gz->matrix_offset[i][0] * (is_pos ? 1 : -1),
310 gz->matrix_offset[i][1] * (is_pos ? 1 : -1),
311 };
312
313 bool ok = true;
314
315 /* Check if we're viewing on an axis,
316 * there is no point to clicking on the current axis so show the reverse. */
317 if (len_squared_v2(co) < 1e-6f && (gz->matrix_offset[i][2] > 0.0f) == is_pos) {
318 ok = false;
319 }
320
321 if (ok) {
322 const float len_axis_sq = len_squared_v2v2(co, point_local);
323 if (len_axis_sq < i_best_len_sq) {
324 part_best = part_index;
325 i_best_len_sq = len_axis_sq;
326 }
327 }
328 part_index += 1;
329 }
330 }
331
332 if (part_best != -1) {
333 return part_best;
334 }
335
336 /* The 'gz->scale_final' is already applied when projecting. */
337 if (len_sq < 1.0f) {
338 return 0;
339 }
340
341 return -1;
342}
343
344static int gizmo_axis_cursor_get(wmGizmo * /*gz*/)
345{
346 return WM_CURSOR_DEFAULT;
347}
348
349static bool gizmo_axis_screen_bounds_get(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
350{
351 ScrArea *area = CTX_wm_area(C);
352 const float rad = WIDGET_RADIUS;
353 r_bounding_box->xmin = gz->matrix_basis[3][0] + area->totrct.xmin - rad;
354 r_bounding_box->ymin = gz->matrix_basis[3][1] + area->totrct.ymin - rad;
355 r_bounding_box->xmax = r_bounding_box->xmin + rad;
356 r_bounding_box->ymax = r_bounding_box->ymin + rad;
357 return true;
358}
359
361{
362 /* identifiers */
363 gzt->idname = "VIEW3D_GT_navigate_rotate";
364
365 /* api callbacks */
366 gzt->draw = gizmo_axis_draw;
370
371 gzt->struct_size = sizeof(wmGizmo);
372}
ScrArea * CTX_wm_area(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
void BLF_size(int fontid, float size)
Definition blf.cc:426
void BLF_width_and_height(int fontid, const char *str, size_t str_len, float *r_width, float *r_height) ATTR_NONNULL()
Definition blf.cc:778
void BLF_color4fv(int fontid, const float rgba[4])
Definition blf.cc:488
void BLF_disable(int fontid, int option)
Definition blf.cc:321
int BLF_default()
void BLF_draw(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:568
void BLF_enable(int fontid, int option)
Definition blf.cc:312
@ BLF_ROTATION
Definition BLF_api.hh:361
@ BLF_WORD_WRAP
Definition BLF_api.hh:367
@ BLF_BOLD
Definition BLF_api.hh:373
@ BLF_ASPECT
Definition BLF_api.hh:366
@ BLF_SHADOW
Definition BLF_api.hh:363
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:371
void mul_m3_v3(const float M[3][3], float r[3])
void copy_m3_m3(float m1[3][3], const float m2[3][3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
void unit_m4(float m[4][4])
Definition rct.c:1127
void copy_m4_m3(float m1[4][4], const float m2[3][3])
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3])
void mul_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3])
bool invert_m3(float mat[3][3])
MINLINE float len_squared_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v2v2(const float a[2], const float b[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)
void interp_v4_v4v4(float r[4], const float a[4], const float b[4], float t)
Definition math_vector.c:45
MINLINE void zero_v4(float r[4])
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
int BLI_sortutil_cmp_float(const void *a_, const void *b_)
Definition sort_utils.c:25
unsigned int uint
#define ARRAY_SIZE(arr)
void ED_view3d_background_color_get(const Scene *scene, const View3D *v3d, float r_color[3])
void immEnd()
void immUniform2fv(const char *name, const float data[2])
void immUnbindProgram()
void immAttr4fv(uint attr_id, const float data[4])
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immUniform1f(const char *name, float x)
GPUVertFormat * immVertexFormat()
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
void GPU_matrix_ortho_set_z(float near, float far)
void GPU_matrix_push()
void GPU_matrix_push_projection()
#define GPU_matrix_mul(x)
void GPU_matrix_scale_1f(float factor)
void GPU_matrix_pop_projection()
void GPU_matrix_translate_3fv(const float vec[3])
void GPU_matrix_pop()
#define GPU_MATRIX_ORTHO_CLIP_NEAR_DEFAULT
@ GPU_PRIM_LINES
@ GPU_SHADER_3D_POLYLINE_SMOOTH_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_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.
void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float col[4])
void UI_draw_roundbox_4fv_ex(const rctf *rect, const float inner1[4], const float inner2[4], float shade_dir, const float outline[4], float outline_width, float rad)
void UI_draw_roundbox_corner_set(int type)
@ UI_CNR_ALL
void UI_GetThemeColor3fv(int colorid, float col[3])
@ TH_AXIS_X
@ WM_GIZMO_STATE_HIGHLIGHT
ATTR_WARN_UNUSED_RESULT const BMVert * v
draw_view in_light_buf[] float
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
format
#define FLT_MAX
Definition stdcycles.h:14
float xmin
int ymin
int ymax
int xmin
int xmax
wmGizmoFnDraw draw
wmGizmoFnScreenBoundsGet screen_bounds_get
const char * idname
wmGizmoFnTestSelect test_select
wmGizmoFnCursorGet cursor_get
eWM_GizmoFlagState state
float matrix_basis[4][4]
float matrix_offset[4][4]
float color_hi[4]
float scale_final
#define AXIS_LINE_WIDTH
#define AXIS_RING_WIDTH
static int gizmo_axis_test_select(bContext *, wmGizmo *gz, const int mval[2])
#define AXIS_DEPTH_BIAS
static void gizmo_axis_draw(const bContext *C, wmGizmo *gz)
#define AXIS_TEXT_SIZE
static int gizmo_axis_cursor_get(wmGizmo *)
#define WIDGET_RADIUS
static bool gizmo_axis_screen_bounds_get(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
void VIEW3D_GT_navigate_rotate(wmGizmoType *gzt)
#define AXIS_HANDLE_SIZE
@ WM_CURSOR_DEFAULT
Definition wm_cursors.hh:15
void WM_gizmo_calc_matrix_final_params(const wmGizmo *gz, const WM_GizmoMatrixParams *params, float r_mat[4][4])
Definition wm_gizmo.cc:519