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