Blender  V2.93
view3d_gizmo_navigate_type.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
29 #include "MEM_guardedalloc.h"
30 
31 #include "BLI_math.h"
32 #include "BLI_sort_utils.h"
33 
34 #include "BKE_context.h"
35 
36 #include "GPU_batch.h"
37 #include "GPU_batch_presets.h"
38 #include "GPU_immediate.h"
39 #include "GPU_immediate_util.h"
40 #include "GPU_matrix.h"
41 #include "GPU_state.h"
42 
43 #include "BLF_api.h"
44 
45 #include "RNA_access.h"
46 #include "RNA_define.h"
47 
48 #include "UI_interface.h"
49 #include "UI_resources.h"
50 
51 #include "WM_api.h"
52 #include "WM_types.h"
53 
54 #include "ED_screen.h"
55 
56 #include "view3d_intern.h"
57 
58 /* Radius of the entire background. */
59 #define WIDGET_RADIUS ((U.gizmo_size_navigate_v3d / 2.0f) * UI_DPI_FAC)
60 
61 /* Sizes of axis spheres containing XYZ characters in relation to above. */
62 #define AXIS_HANDLE_SIZE 0.20f
63 
64 #define AXIS_LINE_WIDTH ((U.gizmo_size_navigate_v3d / 40.0f) * U.pixelsize)
65 #define AXIS_RING_WIDTH ((U.gizmo_size_navigate_v3d / 60.0f) * U.pixelsize)
66 #define AXIS_TEXT_SIZE (WIDGET_RADIUS * AXIS_HANDLE_SIZE * 1.25f)
67 
68 /* distance within this from center is considered positive. */
69 #define AXIS_DEPTH_BIAS 0.01f
70 
71 static void gizmo_axis_draw(const bContext *C, wmGizmo *gz)
72 {
73  struct {
74  float depth;
75  char index;
76  char axis;
77  char axis_opposite;
78  bool is_pos;
79  } axis_order[6] = {
80  {-gz->matrix_offset[0][2], 0, 0, 1, false},
81  {+gz->matrix_offset[0][2], 1, 0, 0, true},
82  {-gz->matrix_offset[1][2], 2, 1, 3, false},
83  {+gz->matrix_offset[1][2], 3, 1, 2, true},
84  {-gz->matrix_offset[2][2], 4, 2, 5, false},
85  {+gz->matrix_offset[2][2], 5, 2, 4, true},
86  };
87 
88  int axis_align = -1;
89  for (int axis = 0; axis < 3; axis++) {
90  if (len_squared_v2(gz->matrix_offset[axis]) < 1e-6f) {
91  axis_align = axis;
92  break;
93  }
94  }
95 
96  qsort(&axis_order, ARRAY_SIZE(axis_order), sizeof(axis_order[0]), BLI_sortutil_cmp_float);
97 
98  /* When the cursor is over any of the gizmos (show circle backdrop). */
99  const bool is_active = ((gz->state & WM_GIZMO_STATE_HIGHLIGHT) != 0);
100 
101  /* Background color of the View3D, used to mix colors. */
102  float view_color[4];
104  view_color[3] = 1.0f;
105 
106  float matrix_screen[4][4];
107  float matrix_unit[4][4];
108  unit_m4(matrix_unit);
110  &((struct WM_GizmoMatrixParams){
111  .matrix_offset = matrix_unit,
112  }),
113  matrix_screen);
114  GPU_matrix_push();
115  GPU_matrix_mul(matrix_screen);
116 
118  const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
119  const uint color_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
120  float viewport_size[4];
121  GPU_viewport_size_get_f(viewport_size);
122 
123  static float axis_color[3][4];
124 
125  struct {
126  float matrix[4][4];
127  float matrix_m3[3][3];
128  float matrix_m3_invert[3][3];
129  int id;
130  } font;
131 
132  font.id = BLF_default();
134  BLF_enable(font.id, BLF_BOLD);
135  BLF_size(font.id, AXIS_TEXT_SIZE, 72);
136  BLF_position(font.id, 0, 0, 0);
137 
138  /* Calculate the inverse of the (matrix_final * matrix_offset).
139  * This allows us to use the final location, while reversing the rotation so fonts
140  * show without any rotation. */
141  float m3[3][3];
142  float m3_offset[3][3];
143  copy_m3_m4(m3, matrix_screen);
144  copy_m3_m4(m3_offset, gz->matrix_offset);
145  mul_m3_m3m3(m3, m3, m3_offset);
146  copy_m3_m3(font.matrix_m3_invert, m3);
147  invert_m3(m3);
148  copy_m3_m3(font.matrix_m3, m3);
149  copy_m4_m3(font.matrix, m3);
150 
151  bool use_project_matrix = (gz->scale_final >= -GPU_MATRIX_ORTHO_CLIP_NEAR_DEFAULT);
152  if (use_project_matrix) {
155  }
156 
158  GPU_polygon_smooth(false);
159 
160  /* Circle defining active area. */
161  if (is_active) {
162  const float rad = WIDGET_RADIUS;
163  GPU_matrix_push();
164  GPU_matrix_scale_1f(1.0f / rad);
166  &(const rctf){
167  .xmin = -rad,
168  .xmax = rad,
169  .ymin = -rad,
170  .ymax = rad,
171  },
172  true,
173  rad,
174  gz->color_hi);
175  GPU_matrix_pop();
176  }
177 
179 
180  for (int axis_index = 0; axis_index < ARRAY_SIZE(axis_order); axis_index++) {
181  const int index = axis_order[axis_index].index;
182  const int axis = axis_order[axis_index].axis;
183  const bool is_pos = axis_order[axis_index].is_pos;
184  const float depth = axis_order[axis_index].depth;
185  const bool is_behind = (depth <= (AXIS_DEPTH_BIAS * (is_pos ? -1 : 1)));
186  bool is_aligned_front = (axis_align != -1 && axis_align == axis && !is_behind);
187  bool is_aligned_back = (axis_align != -1 && axis_align == axis && is_behind);
188 
189  const float v[3] = {0, 0, (1.0f - AXIS_HANDLE_SIZE) * (is_pos ? 1 : -1)};
190  const float v_final[3] = {v[(axis + 2) % 3], v[(axis + 1) % 3], v[axis]};
191 
192  bool is_highlight = index + 1 == gz->highlight_part;
193  /* Check if highlight part is the other side when axis aligned. */
194  if (is_aligned_front && (axis_order[axis_index].axis_opposite + 1 == gz->highlight_part)) {
195  is_highlight = true;
196  }
197 
198  UI_GetThemeColor3fv(TH_AXIS_X + axis, axis_color[axis]);
199  axis_color[axis][3] = 1.0f;
200 
201  /* Color that is full at front, but 50% view background when in back. */
202  float fading_color[4];
203  interp_v4_v4v4(fading_color, view_color, axis_color[axis], ((depth + 1) * 0.25) + 0.5);
204 
205  /* Color that is midway between front and back. */
206  float middle_color[4];
207  interp_v4_v4v4(middle_color, view_color, axis_color[axis], 0.75f);
208 
210 
211  /* Axis Line. */
212  if (is_pos || axis_align != -1) {
213 
214  /* Extend slightly to meet better at the center. */
215  float v_start[3] = {0.0f, 0.0f, 0.0f};
216  mul_v3_v3fl(v_start, v_final, -(AXIS_LINE_WIDTH / WIDGET_RADIUS * 0.66f));
217 
218  /* Decrease length of line by ball radius. */
219  float v_end[3] = {0.0f, 0.0f, 0.0f};
220  mul_v3_v3fl(v_end, v_final, 1.0f - AXIS_HANDLE_SIZE);
221 
223  immUniform2fv("viewportSize", &viewport_size[2]);
224  immUniform1f("lineWidth", AXIS_LINE_WIDTH);
226  immAttr4fv(color_id, middle_color);
227  immVertex3fv(pos_id, v_start);
228  immAttr4fv(color_id, fading_color);
229  immVertex3fv(pos_id, v_end);
230  immEnd();
232  }
233 
234  /* Axis Ball. */
235  if (!is_aligned_back) {
236  float *inner_color = fading_color;
237  float *outline_color = fading_color;
238  float negative_color[4];
239  if (!is_pos) {
240  if (is_aligned_front) {
242  negative_color, (float[4]){1.0f, 1.0f, 1.0f, 1.0f}, axis_color[axis], 0.5f);
243  negative_color[3] = MIN2(depth + 1, 1.0f);
244  outline_color = negative_color;
245  }
246  else {
247  interp_v4_v4v4(negative_color, view_color, axis_color[axis], 0.25f);
248  negative_color[3] = MIN2(depth + 1, 1.0f);
249  inner_color = negative_color;
250  }
251  }
252 
253  GPU_matrix_push();
254  GPU_matrix_translate_3fv(v_final);
255  GPU_matrix_mul(font.matrix);
256  /* Size change from back to front: 0.92f - 1.08f. */
257  float scale = ((depth + 1) * 0.08f) + 0.92f;
258  const float rad = WIDGET_RADIUS * AXIS_HANDLE_SIZE * scale;
260  &(const rctf){
261  .xmin = -rad,
262  .xmax = rad,
263  .ymin = -rad,
264  .ymax = rad,
265  },
266  inner_color,
267  NULL,
268  0.0f,
269  outline_color,
271  rad);
272  GPU_matrix_pop();
273  }
274 
275  /* Axis XYZ Character. */
276  if ((is_pos || is_highlight || (axis == axis_align)) && !is_aligned_back) {
277  float axis_str_width, axis_string_height;
278  char axis_str[3] = {'X' + axis, 0, 0};
279  if (!is_pos) {
280  axis_str[0] = '-';
281  axis_str[1] = 'X' + axis;
282  }
283  BLF_width_and_height(font.id, axis_str, 3, &axis_str_width, &axis_string_height);
284 
285  /* Calculate pixel-aligned location, without this text draws fuzzy. */
286  float v_final_px[3];
287  mul_v3_m3v3(v_final_px, font.matrix_m3_invert, v_final);
288  /* Center the text and pixel align, it's important to round once
289  * otherwise the characters are noticeably not-centered.
290  * If this wasn't an issue we could use #BLF_position to place the text. */
291  v_final_px[0] = roundf(v_final_px[0] - (axis_str_width * (is_pos ? 0.5f : 0.55f)));
292  v_final_px[1] = roundf(v_final_px[1] - (axis_string_height / 2.0f));
293  mul_m3_v3(font.matrix_m3, v_final_px);
294  GPU_matrix_push();
295  GPU_matrix_translate_3fv(v_final_px);
296  GPU_matrix_mul(font.matrix);
297  float text_color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
298  if (!is_highlight) {
299  zero_v4(text_color);
300  text_color[3] = is_active ? 1.0f : 0.9f;
301  }
302  BLF_color4fv(font.id, text_color);
303  BLF_draw_ascii(font.id, axis_str, 2);
304  GPU_matrix_pop();
305  }
306  }
307 
308  if (use_project_matrix) {
310  }
311 
313  BLF_disable(font.id, BLF_BOLD);
314  GPU_matrix_pop();
315 }
316 
317 static int gizmo_axis_test_select(bContext *UNUSED(C), wmGizmo *gz, const int mval[2])
318 {
319  float point_local[2] = {UNPACK2(mval)};
320  sub_v2_v2(point_local, gz->matrix_basis[3]);
321  mul_v2_fl(point_local, 1.0f / gz->scale_final);
322 
323  const float len_sq = len_squared_v2(point_local);
324  if (len_sq > 1.0) {
325  return -1;
326  }
327 
328  int part_best = -1;
329  int part_index = 1;
330  /* Use 'SQUARE(HANDLE_SIZE)' if we want to be able to _not_ focus on one of the axis. */
331  float i_best_len_sq = FLT_MAX;
332  for (int i = 0; i < 3; i++) {
333  for (int is_pos = 0; is_pos < 2; is_pos++) {
334  const float co[2] = {
335  gz->matrix_offset[i][0] * (is_pos ? 1 : -1),
336  gz->matrix_offset[i][1] * (is_pos ? 1 : -1),
337  };
338 
339  bool ok = true;
340 
341  /* Check if we're viewing on an axis,
342  * there is no point to clicking on the current axis so show the reverse. */
343  if (len_squared_v2(co) < 1e-6f && (gz->matrix_offset[i][2] > 0.0f) == is_pos) {
344  ok = false;
345  }
346 
347  if (ok) {
348  const float len_axis_sq = len_squared_v2v2(co, point_local);
349  if (len_axis_sq < i_best_len_sq) {
350  part_best = part_index;
351  i_best_len_sq = len_axis_sq;
352  }
353  }
354  part_index += 1;
355  }
356  }
357 
358  if (part_best != -1) {
359  return part_best;
360  }
361 
362  /* The 'gz->scale_final' is already applied when projecting. */
363  if (len_sq < 1.0f) {
364  return 0;
365  }
366 
367  return -1;
368 }
369 
371 {
372  return WM_CURSOR_DEFAULT;
373 }
374 
375 static bool gizmo_axis_screen_bounds_get(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
376 {
378  const float rad = WIDGET_RADIUS;
379  r_bounding_box->xmin = gz->matrix_basis[3][0] + area->totrct.xmin - rad;
380  r_bounding_box->ymin = gz->matrix_basis[3][1] + area->totrct.ymin - rad;
381  r_bounding_box->xmax = r_bounding_box->xmin + rad;
382  r_bounding_box->ymax = r_bounding_box->ymin + rad;
383  return true;
384 }
385 
387 {
388  /* identifiers */
389  gzt->idname = "VIEW3D_GT_navigate_rotate";
390 
391  /* api callbacks */
392  gzt->draw = gizmo_axis_draw;
396 
397  gzt->struct_size = sizeof(wmGizmo);
398 }
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:714
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1034
struct View3D * CTX_wm_view3d(const bContext *C)
Definition: context.c:760
int BLF_default(void)
Definition: blf_default.c:55
void BLF_color4fv(int fontid, const float rgba[4])
Definition: blf.c:441
#define BLF_ASPECT
Definition: BLF_api.h:274
void BLF_draw_ascii(int fontid, const char *str, size_t len) ATTR_NONNULL(2)
Definition: blf.c:573
void BLF_width_and_height(int fontid, const char *str, size_t len, float *r_width, float *r_height) ATTR_NONNULL()
Definition: blf.c:698
#define BLF_BOLD
Definition: BLF_api.h:280
void BLF_disable(int fontid, int option)
Definition: blf.c:283
#define BLF_ROTATION
Definition: BLF_api.h:269
#define BLF_MATRIX
Definition: BLF_api.h:273
void BLF_size(int fontid, int size, int dpi)
Definition: blf.c:367
void BLF_enable(int fontid, int option)
Definition: blf.c:274
#define BLF_SHADOW
Definition: BLF_api.h:271
#define BLF_WORD_WRAP
Definition: BLF_api.h:275
void BLF_position(int fontid, float x, float y, float z)
Definition: blf.c:312
void mul_m3_v3(const float M[3][3], float r[3])
Definition: math_matrix.c:930
void copy_m3_m3(float m1[3][3], const float m2[3][3])
Definition: math_matrix.c:89
void copy_m3_m4(float m1[3][3], const float m2[4][4])
Definition: math_matrix.c:105
void unit_m4(float m[4][4])
Definition: rct.c:1140
void copy_m4_m3(float m1[4][4], const float m2[3][3])
Definition: math_matrix.c:120
bool invert_m3(float R[3][3])
Definition: math_matrix.c:1152
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3])
Definition: math_matrix.c:901
void mul_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3])
Definition: math_matrix.c:391
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], const float t)
Definition: math_vector.c:58
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:40
unsigned int uint
Definition: BLI_sys_types.h:83
#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:142
void GPU_matrix_pop_projection(void)
Definition: gpu_matrix.cc:156
void GPU_matrix_ortho_set_z(float near, float far)
Definition: gpu_matrix.cc:422
#define GPU_matrix_mul(x)
Definition: GPU_matrix.h:223
void GPU_matrix_push(void)
Definition: gpu_matrix.cc:135
void GPU_matrix_scale_1f(float factor)
Definition: gpu_matrix.cc:225
void GPU_matrix_translate_3fv(const float vec[3])
Definition: gpu_matrix.cc:220
#define GPU_MATRIX_ORTHO_CLIP_NEAR_DEFAULT
Definition: GPU_matrix.h:236
void GPU_matrix_push_projection(void)
Definition: gpu_matrix.cc:149
@ GPU_PRIM_LINES
Definition: GPU_primitive.h:36
@ GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR
Definition: GPU_shader.h:240
@ GPU_BLEND_NONE
Definition: GPU_state.h:55
@ GPU_BLEND_ALPHA
Definition: GPU_state.h:57
void GPU_blend(eGPUBlend blend)
Definition: gpu_state.cc:55
void GPU_viewport_size_get_f(float coords[4])
Definition: gpu_state.cc:279
void GPU_polygon_smooth(bool enable)
Definition: gpu_state.cc:90
@ 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:39
@ UI_CNR_ALL
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)
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:1191
@ TH_AXIS_X
Definition: UI_resources.h:316
struct wmGizmo wmGizmo
Definition: WM_api.h:74
@ WM_GIZMO_STATE_HIGHLIGHT
ATTR_WARN_UNUSED_RESULT const BMVert * v
format
Definition: logImageCore.h:47
static void area(int d1, int d2, int e1, int e2, float weights[2])
int ymin
Definition: DNA_vec_types.h:80
int ymax
Definition: DNA_vec_types.h:80
int xmin
Definition: DNA_vec_types.h:79
int xmax
Definition: DNA_vec_types.h:79
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:34
void WM_gizmo_calc_matrix_final_params(const wmGizmo *gz, const struct WM_GizmoMatrixParams *params, float r_mat[4][4])
Definition: wm_gizmo.c:549