Blender V4.5
transform_constraints.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <cmath>
11#include <cstdlib>
12#include <cstring>
13
14#include "DNA_scene_types.h"
15#include "DNA_screen_types.h"
16#include "DNA_space_types.h"
17#include "DNA_userdef_types.h"
18#include "DNA_view3d_types.h"
19
20#include "GPU_immediate.hh"
21#include "GPU_matrix.hh"
22#include "GPU_state.hh"
23
24#include "BLI_math_geom.h"
25#include "BLI_math_matrix.h"
26#include "BLI_math_rotation.h"
27#include "BLI_rect.h"
28#include "BLI_string.h"
29#include "BLI_utildefines.h"
30
31#include "BLT_translation.hh"
32
33#include "UI_resources.hh"
34
35#include "transform.hh"
36#include "transform_gizmo.hh"
38#include "transform_snap.hh"
39
40/* Own include. */
42
43namespace blender::ed::transform {
44
45static void drawObjectConstraint(TransInfo *t);
46
47/* -------------------------------------------------------------------- */
50
51static void projection_matrix_calc(const TransInfo *t, float r_pmtx[3][3])
52{
53 unit_m3(r_pmtx);
54
55 if (!(t->con.mode & CON_AXIS0)) {
56 zero_v3(r_pmtx[0]);
57 }
58
59 if (!(t->con.mode & CON_AXIS1)) {
60 zero_v3(r_pmtx[1]);
61 }
62
63 if (!(t->con.mode & CON_AXIS2)) {
64 zero_v3(r_pmtx[2]);
65 }
66
67 float mat[3][3];
68 mul_m3_m3m3(mat, r_pmtx, t->spacemtx_inv);
69 mul_m3_m3m3(r_pmtx, t->spacemtx, mat);
70}
71
72static void view_vector_calc(const TransInfo *t, const float focus[3], float r_vec[3])
73{
74 if (t->persp != RV3D_ORTHO) {
75 sub_v3_v3v3(r_vec, t->viewinv[3], focus);
76 }
77 else {
78 copy_v3_v3(r_vec, t->viewinv[2]);
79 }
80 normalize_v3(r_vec);
81}
82
83/* ************************** CONSTRAINTS ************************* */
84#define CONSTRAIN_EPSILON 0.0001f
85
86static void constraint_plane_normal_calc(const TransInfo *t, float r_plane_no[3])
87{
88 const float *constraint_vector[2];
89 int n = 0;
90 for (int i = 0; i < 3; i++) {
91 if (t->con.mode & (CON_AXIS0 << i)) {
92 constraint_vector[n++] = t->spacemtx[i];
93 if (n == 2) {
94 break;
95 }
96 }
97 }
98 BLI_assert(n == 2);
99
100 cross_v3_v3v3(r_plane_no, constraint_vector[0], constraint_vector[1]);
101 normalize_v3(r_plane_no);
102}
103
104void constraintNumInput(TransInfo *t, float vec[3])
105{
106 int mode = t->con.mode;
107 if (mode & CON_APPLY) {
108 float nval = (t->flag & T_NULL_ONE) ? 1.0f : 0.0f;
109
110 const int dims = getConstraintSpaceDimension(t);
111 if (dims == 2) {
112 int axis = mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
113 if (axis == (CON_AXIS0 | CON_AXIS1)) {
114 // vec[0] = vec[0]; /* Same. */
115 // vec[1] = vec[1]; /* Same. */
116 vec[2] = nval;
117 }
118 else if (axis == (CON_AXIS1 | CON_AXIS2)) {
119 vec[2] = vec[1];
120 vec[1] = vec[0];
121 vec[0] = nval;
122 }
123 else if (axis == (CON_AXIS0 | CON_AXIS2)) {
124 // vec[0] = vec[0]; /* Same. */
125 vec[2] = vec[1];
126 vec[1] = nval;
127 }
128 }
129 else if (dims == 1) {
130 if (mode & CON_AXIS0) {
131 // vec[0] = vec[0]; /* Same. */
132 vec[1] = nval;
133 vec[2] = nval;
134 }
135 else if (mode & CON_AXIS1) {
136 vec[1] = vec[0];
137 vec[0] = nval;
138 vec[2] = nval;
139 }
140 else if (mode & CON_AXIS2) {
141 vec[2] = vec[0];
142 vec[0] = nval;
143 vec[1] = nval;
144 }
145 }
146 }
147}
148
149static void viewAxisCorrectCenter(const TransInfo *t, float t_con_center[3])
150{
151 if (t->spacetype == SPACE_VIEW3D) {
152 // View3D *v3d = t->area->spacedata.first;
153 const float min_dist = 1.0f; /* `v3d->clip_start`. */
154 float dir[3];
155 float l;
156
157 sub_v3_v3v3(dir, t_con_center, t->viewinv[3]);
158 if (dot_v3v3(dir, t->viewinv[2]) < 0.0f) {
159 negate_v3(dir);
160 }
161 project_v3_v3v3(dir, dir, t->viewinv[2]);
162
163 l = len_v3(dir);
164
165 if (l < min_dist) {
166 float diff[3];
167 normalize_v3_v3_length(diff, t->viewinv[2], min_dist - l);
168 sub_v3_v3(t_con_center, diff);
169 }
170 }
171}
172
176static void axisProjection(const TransInfo *t,
177 const float axis[3],
178 const float in[3],
179 float out[3])
180{
181 float vec[3], factor, angle;
182 float t_con_center[3];
183
184 if (is_zero_v3(in)) {
185 return;
186 }
187
188 copy_v3_v3(t_con_center, t->center_global);
189
190 /* Checks for center being too close to the view center. */
191 viewAxisCorrectCenter(t, t_con_center);
192
193 angle = fabsf(angle_v3v3(axis, t->viewinv[2]));
194 if (angle > float(M_PI_2)) {
195 angle = float(M_PI) - angle;
196 }
197
198 /* For when view is parallel to constraint... will cause NaNs otherwise
199 * So we take vertical motion in 3D space and apply it to the
200 * constraint axis. Nice for camera grab + MMB. */
201 if (angle < DEG2RADF(5.0f)) {
202 project_v3_v3v3(vec, in, t->viewinv[1]);
203 factor = dot_v3v3(t->viewinv[1], vec) * 2.0f;
204 /* Since camera distance is quite relative, use quadratic relationship.
205 * holding shift can compensate. */
206 if (factor < 0.0f) {
207 factor *= -factor;
208 }
209 else {
210 factor *= factor;
211 }
212
213 /* -factor makes move down going backwards. */
214 normalize_v3_v3_length(out, axis, -factor);
215 }
216 else {
217 float v[3];
218 float norm[3], norm_center[3];
219 float plane[3];
220
221 view_vector_calc(t, t_con_center, norm_center);
222 cross_v3_v3v3(plane, norm_center, axis);
223
224 project_v3_v3v3(vec, in, plane);
225 sub_v3_v3v3(vec, in, vec);
226
227 add_v3_v3v3(v, vec, t_con_center);
229
230 /* Give arbitrary large value if projection is impossible. */
231 factor = dot_v3v3(axis, norm);
232 if (1.0f - fabsf(factor) < 0.0002f) {
233 copy_v3_v3(out, axis);
234 if (factor > 0) {
235 mul_v3_fl(out, 1000000000.0f);
236 }
237 else {
238 mul_v3_fl(out, -1000000000.0f);
239 }
240 }
241 else {
242 /* Use ray-ray intersection instead of line-line because this gave
243 * precision issues adding small values to large numbers. */
244 float mul;
245 if (isect_ray_ray_v3(t_con_center, axis, v, norm, &mul, nullptr)) {
246 mul_v3_v3fl(out, axis, mul);
247 }
248 else {
249 /* In practice this should never fail. */
250 BLI_assert(0);
251 }
252
253 /* Possible some values become nan when viewpoint and object are both zero. */
254 if (!isfinite(out[0])) {
255 out[0] = 0.0f;
256 }
257 if (!isfinite(out[1])) {
258 out[1] = 0.0f;
259 }
260 if (!isfinite(out[2])) {
261 out[2] = 0.0f;
262 }
263 }
264 }
265}
266
271 const float plane_no[3],
272 float r_out[3])
273{
274 float lambda;
275 const float *edge_snap_point = t->tsnap.snap_target;
276 const float *edge_dir = t->tsnap.snapNormal;
277 bool is_aligned = fabsf(dot_v3v3(edge_dir, plane_no)) < CONSTRAIN_EPSILON;
278 if (!is_aligned && isect_ray_plane_v3_factor(
279 edge_snap_point, edge_dir, t->tsnap.snap_source, plane_no, &lambda))
280 {
281 madd_v3_v3v3fl(r_out, edge_snap_point, edge_dir, lambda);
282 sub_v3_v3(r_out, t->tsnap.snap_source);
283 }
284}
285
286static void UNUSED_FUNCTION(constraint_snap_plane_to_face(const TransInfo *t,
287 const float plane[4],
288 float r_out[3]))
289{
290 float face_plane[4], isect_orig[3], isect_dir[3];
291 const float *face_snap_point = t->tsnap.snap_target;
292 const float *face_normal = t->tsnap.snapNormal;
293 plane_from_point_normal_v3(face_plane, face_snap_point, face_normal);
294 bool is_aligned = fabsf(dot_v3v3(plane, face_plane)) > (1.0f - CONSTRAIN_EPSILON);
295 if (!is_aligned && isect_plane_plane_v3(plane, face_plane, isect_orig, isect_dir)) {
296 closest_to_ray_v3(r_out, face_snap_point, isect_orig, isect_dir);
297 sub_v3_v3(r_out, t->tsnap.snap_source);
298 }
299}
300
302 const float axis[3],
303 float r_out[3])
304{
305 float lambda;
306 const float *edge_snap_point = t->tsnap.snap_target;
307 const float *edge_dir = t->tsnap.snapNormal;
308 bool is_aligned = fabsf(dot_v3v3(axis, edge_dir)) > (1.0f - CONSTRAIN_EPSILON);
309 if (!is_aligned &&
310 isect_ray_ray_v3(t->tsnap.snap_source, axis, edge_snap_point, edge_dir, &lambda, nullptr))
311 {
312 mul_v3_v3fl(r_out, axis, lambda);
313 }
314}
315
317 const float axis[3],
318 float r_out[3])
319{
320 float lambda;
321 const float *face_snap_point = t->tsnap.snap_target;
322 const float *face_normal = t->tsnap.snapNormal;
323 bool is_aligned = fabsf(dot_v3v3(axis, face_normal)) < CONSTRAIN_EPSILON;
324 if (!is_aligned &&
325 isect_ray_plane_v3_factor(t->tsnap.snap_source, axis, face_snap_point, face_normal, &lambda))
326 {
327 mul_v3_v3fl(r_out, axis, lambda);
328 }
329}
330
335static bool isPlaneProjectionViewAligned(const TransInfo *t, const float plane_no[3])
336{
337 const float eps = 0.001f;
338 float view_to_plane[3];
339 view_vector_calc(t, t->center_global, view_to_plane);
340
341 float factor = dot_v3v3(plane_no, view_to_plane);
342 return fabsf(factor) < eps;
343}
344
345static void planeProjection(const TransInfo *t,
346 const float plane_no[3],
347 const float in[3],
348 float out[3])
349{
350
351 float pos[3], view_vec[3], factor;
352
354 view_vector_calc(t, pos, view_vec);
355
356 if (isect_ray_plane_v3_factor(pos, view_vec, t->center_global, plane_no, &factor)) {
357 madd_v3_v3v3fl(out, in, view_vec, factor);
358 }
359}
360
362{
363 short orientation = t->orient[t->orient_curr].type;
364 if (orientation == V3D_ORIENT_CUSTOM_MATRIX) {
365 /* Use the real value of the "orient_type". */
366 orientation = t->orient[O_DEFAULT].type;
367 }
368 return orientation;
369}
370
371static const float (*transform_object_axismtx_get(const TransInfo *t,
372 const TransDataContainer * /*tc*/,
373 const TransData *td))[3]
374{
376 BLI_assert(t->orient_type_mask & (1 << V3D_ORIENT_GIMBAL));
377 if (t->options & (CTX_POSE_BONE | CTX_OBJECT)) {
378 return td->ext->axismtx_gimbal;
379 }
380 }
381 return td->axismtx;
382}
383
384void transform_constraint_get_nearest(const TransInfo *t, const float3 &vec, float r_vec[3])
385{
386 bool is_snap_to_point = false, is_snap_to_edge = false, is_snap_to_face = false;
387
389 if (validSnap(t)) {
390 is_snap_to_edge = (t->tsnap.target_type & SCE_SNAP_TO_EDGE) != 0;
391 is_snap_to_face = (t->tsnap.target_type & SCE_SNAP_TO_FACE) != 0;
392 is_snap_to_point = !is_snap_to_edge && !is_snap_to_face;
393 }
394 }
395
396 /* Fallback for when axes are aligned. */
397 mul_v3_m3v3(r_vec, t->con.pmtx, vec);
398
399 if (is_snap_to_point) {
400 /* Pass. With snap points, a projection is alright, no adjustments needed. */
401 }
402 else {
403 const int dims = getConstraintSpaceDimension(t);
404 if (dims == 2) {
405 if (!is_zero_v3(r_vec)) {
406 float plane_no[3];
407 constraint_plane_normal_calc(t, plane_no);
408
409 if (is_snap_to_edge) {
410 constraint_snap_plane_to_edge(t, plane_no, r_vec);
411 }
412 else if (is_snap_to_face) {
413 /* Disabled, as it has not proven to be really useful. (See #82386). */
414 // constraint_snap_plane_to_face(t, plane, out);
415 }
416 else if (!isPlaneProjectionViewAligned(t, plane_no)) {
417 /* View alignment correction. */
418 planeProjection(t, plane_no, vec, r_vec);
419 }
420 }
421 }
422 else if (dims == 1) {
423 float c[3];
424
425 if (t->con.mode & CON_AXIS0) {
426 copy_v3_v3(c, t->spacemtx[0]);
427 }
428 else if (t->con.mode & CON_AXIS1) {
429 copy_v3_v3(c, t->spacemtx[1]);
430 }
431 else {
433 copy_v3_v3(c, t->spacemtx[2]);
434 }
435
436 if (is_snap_to_edge) {
438 }
439 else if (is_snap_to_face) {
441 }
442 else {
443 /* View alignment correction. */
444 axisProjection(t, c, vec, r_vec);
445 }
446 }
447 }
448}
449
458 const TransDataContainer * /*tc*/,
459 const TransData *td,
460 const float in[3],
461 float out[3])
462{
463 if (td || !(t->con.mode & CON_APPLY)) {
464 copy_v3_v3(out, in);
465 return;
466 }
467
469}
470
482 const TransDataContainer *tc,
483 const TransData *td,
484 const float in[3],
485 float out[3])
486{
487 if (!td) {
488 applyAxisConstraintVec(t, tc, td, in, out);
489 }
490 else {
491 /* Specific TransData's space. */
492 copy_v3_v3(out, in);
493 if (t->con.mode & CON_APPLY) {
495 const float(*axismtx)[3] = transform_object_axismtx_get(t, tc, td);
496 mul_m3_v3(axismtx, out);
497 if (t->flag & T_EDIT) {
498 mul_m3_v3(tc->mat3_unit, out);
499 }
500 }
501 }
502}
503
508 const TransDataContainer * /*tc*/,
509 const TransData *td,
510 float r_smat[3][3])
511{
512 if (!td && t->con.mode & CON_APPLY) {
513 float tmat[3][3];
514
515 if (!(t->con.mode & CON_AXIS0)) {
516 r_smat[0][0] = 1.0f;
517 }
518 if (!(t->con.mode & CON_AXIS1)) {
519 r_smat[1][1] = 1.0f;
520 }
521 if (!(t->con.mode & CON_AXIS2)) {
522 r_smat[2][2] = 1.0f;
523 }
524
525 mul_m3_m3m3(tmat, r_smat, t->spacemtx_inv);
526 mul_m3_m3m3(r_smat, t->spacemtx, tmat);
527 }
528}
529
534 const TransDataContainer *tc,
535 const TransData *td,
536 float r_smat[3][3])
537{
538 if (td && t->con.mode & CON_APPLY) {
539 float tmat[3][3];
540 float imat[3][3];
541
542 const float(*axismtx)[3] = transform_object_axismtx_get(t, tc, td);
543 invert_m3_m3(imat, axismtx);
544
545 if (!(t->con.mode & CON_AXIS0)) {
546 r_smat[0][0] = 1.0f;
547 }
548 if (!(t->con.mode & CON_AXIS1)) {
549 r_smat[1][1] = 1.0f;
550 }
551 if (!(t->con.mode & CON_AXIS2)) {
552 r_smat[2][2] = 1.0f;
553 }
554
555 mul_m3_m3m3(tmat, r_smat, imat);
556 if (t->flag & T_EDIT) {
557 mul_m3_m3m3(r_smat, tc->mat3_unit, r_smat);
558 }
559 mul_m3_m3m3(r_smat, axismtx, tmat);
560 }
561}
562
564 const float axismtx[3][3],
565 float r_axis[3],
566 float *r_angle)
567{
569 int mode = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
570
571 switch (mode) {
572 case CON_AXIS0:
573 case (CON_AXIS1 | CON_AXIS2):
574 copy_v3_v3(r_axis, axismtx[0]);
575 break;
576 case CON_AXIS1:
577 case (CON_AXIS0 | CON_AXIS2):
578 copy_v3_v3(r_axis, axismtx[1]);
579 break;
580 case CON_AXIS2:
581 case (CON_AXIS0 | CON_AXIS1):
582 copy_v3_v3(r_axis, axismtx[2]);
583 break;
584 }
585 /* Don't flip axis if asked to or if num input. */
586 if (r_angle &&
587 !((mode & CON_NOFLIP) || hasNumInput(&t->num) || (t->flag & T_INPUT_IS_VALUES_FINAL)))
588 {
589 float view_vector[3];
590 view_vector_calc(t, t->center_global, view_vector);
591 if (dot_v3v3(r_axis, view_vector) > 0.0f) {
592 *r_angle = -(*r_angle);
593 }
594 }
595}
596
611 const TransDataContainer * /*tc*/,
612 const TransData *td,
613 float r_axis[3],
614 float *r_angle)
615{
616 if (!td && t->con.mode & CON_APPLY) {
617 constraints_rotation_impl(t, t->spacemtx, r_axis, r_angle);
618 }
619}
620
635 const TransDataContainer *tc,
636 const TransData *td,
637 float r_axis[3],
638 float *r_angle)
639{
640 if (t->con.mode & CON_APPLY) {
641 float tmp_axismtx[3][3];
642 const float(*axismtx)[3];
643
644 /* On setup call, use first object. */
645 if (td == nullptr) {
646 BLI_assert(tc == nullptr);
648 td = tc->data;
649 }
650
651 if (t->flag & T_EDIT) {
652 mul_m3_m3m3(tmp_axismtx, tc->mat3_unit, td->axismtx);
653 axismtx = tmp_axismtx;
654 }
655 else {
656 axismtx = transform_object_axismtx_get(t, tc, td);
657 }
658
659 constraints_rotation_impl(t, axismtx, r_axis, r_angle);
660 }
661}
662
664
665/* -------------------------------------------------------------------- */
668
669void setConstraint(TransInfo *t, int mode, const char text[])
670{
671 BLI_strncpy(t->con.text + 1, text, sizeof(t->con.text) - 1);
672 t->con.mode = eTConstraint(mode);
674
676
677 t->con.drawExtra = nullptr;
681 t->redraw = TREDRAW_HARD;
682}
683
684void setAxisMatrixConstraint(TransInfo *t, int mode, const char text[])
685{
686 BLI_strncpy(t->con.text + 1, text, sizeof(t->con.text) - 1);
687 t->con.mode = eTConstraint(mode);
689
691
696 t->redraw = TREDRAW_HARD;
697}
698
699void setLocalConstraint(TransInfo *t, int mode, const char text[])
700{
701 if ((t->flag & T_EDIT) || t->data_len_all == 1) {
702 /* Although in edit-mode each object has its local space, use the
703 * orientation of the active object. */
704 setConstraint(t, mode, text);
705 }
706 else {
708 }
709}
710
711void setUserConstraint(TransInfo *t, int mode, const char text_[])
712{
713 char text[256];
714 const short orientation = transform_orientation_or_default(t);
715 const char *spacename = transform_orientations_spacename_get(t, orientation);
716 SNPRINTF(text, text_, spacename);
717
718 switch (orientation) {
719 case V3D_ORIENT_LOCAL:
721 setLocalConstraint(t, mode, text);
722 break;
724 if (checkUseAxisMatrix(t)) {
726 break;
727 }
730 case V3D_ORIENT_VIEW:
734 default: {
735 setConstraint(t, mode, text);
736 break;
737 }
738 }
739 t->con.mode |= CON_USER;
740}
741
743
744/* -------------------------------------------------------------------- */
747
748static void drawLine(
749 TransInfo *t, const float center[3], const float dir[3], char axis, short options)
750{
752 return;
753 }
754
755 float v1[3], v2[3], v3[3];
756 uchar col[3], col2[3];
757
758 if (t->spacetype == SPACE_VIEW3D) {
759 View3D *v3d = static_cast<View3D *>(t->view);
760
761 copy_v3_v3(v3, dir);
762 mul_v3_fl(v3, v3d->clip_end);
763
764 sub_v3_v3v3(v2, center, v3);
765 add_v3_v3v3(v1, center, v3);
766 }
767 else if (t->spacetype == SPACE_SEQ) {
768 View2D *v2d = static_cast<View2D *>(t->view);
769
770 copy_v3_v3(v3, dir);
771 float max_dist = max_ff(BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur));
772 mul_v3_fl(v3, max_dist);
773
774 sub_v3_v3v3(v2, center, v3);
775 add_v3_v3v3(v1, center, v3);
776 }
777
779
780 if (options & DRAWLIGHT) {
781 col[0] = col[1] = col[2] = 220;
782 }
783 else {
785 }
786 UI_make_axis_color(col, axis, col2);
787
789
790 float viewport[4];
791 GPU_viewport_size_get_f(viewport);
793
795 immUniform2fv("viewportSize", &viewport[2]);
796 immUniform1f("lineWidth", U.pixelsize * 2.0f);
797
799
801 immVertex3fv(pos, v1);
803 immEnd();
804
806
808}
809
811{
812 TransCon *tc = &(t->con);
813
815 return;
816 }
817 if (!(tc->mode & CON_APPLY)) {
818 return;
819 }
820 if (t->flag & T_NO_CONSTRAINT) {
821 return;
822 }
823
824 if (tc->drawExtra) {
825 tc->drawExtra(t);
826 }
827 else {
828 if (tc->mode & CON_SELECT) {
829 float vec[3];
830
831 convertViewVec(t, vec, (t->mval[0] - t->mouse.imval[0]), (t->mval[1] - t->mouse.imval[1]));
832 add_v3_v3(vec, t->center_global);
833
834 drawLine(t, t->center_global, t->spacemtx[0], 'X', 0);
835 drawLine(t, t->center_global, t->spacemtx[1], 'Y', 0);
836 drawLine(t, t->center_global, t->spacemtx[2], 'Z', 0);
837
838 eGPUDepthTest depth_test_enabled = GPU_depth_test_get();
839 if (depth_test_enabled) {
841 }
842
843 const uint shdr_pos = GPU_vertformat_attr_add(
845
847
848 float viewport_size[4];
849 GPU_viewport_size_get_f(viewport_size);
850 immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
851
852 immUniform1i("colors_len", 0); /* "simple" mode. */
853 immUniformColor4f(1.0f, 1.0f, 1.0f, 1.0f);
854 immUniform1f("dash_width", 2.0f);
855 immUniform1f("udash_factor", 0.5f);
856
858 immVertex3fv(shdr_pos, t->center_global);
859 immVertex3fv(shdr_pos, vec);
860 immEnd();
861
863
864 if (depth_test_enabled) {
866 }
867 }
868
869 if (tc->mode & CON_AXIS0) {
870 drawLine(t, t->center_global, t->spacemtx[0], 'X', DRAWLIGHT);
871 }
872 if (tc->mode & CON_AXIS1) {
873 drawLine(t, t->center_global, t->spacemtx[1], 'Y', DRAWLIGHT);
874 }
875 if (tc->mode & CON_AXIS2) {
876 drawLine(t, t->center_global, t->spacemtx[2], 'Z', DRAWLIGHT);
877 }
878 }
879}
880
882{
883 if (t->flag & T_PROP_EDIT) {
884 const RegionView3D *rv3d = nullptr;
885 float tmat[4][4], imat[4][4];
886
887 if (t->spacetype == SPACE_VIEW3D) {
888 if (t->region && (t->region->regiontype == RGN_TYPE_WINDOW)) {
889 rv3d = static_cast<const RegionView3D *>(t->region->regiondata);
890 }
891 }
892
893 if (rv3d != nullptr) {
894 copy_m4_m4(tmat, rv3d->viewmat);
895 invert_m4_m4(imat, tmat);
896 }
897 else {
898 unit_m4(tmat);
899 unit_m4(imat);
900 }
901
903
904 if (t->spacetype == SPACE_VIEW3D) {
905 /* Pass. */
906 }
907 else if (t->spacetype == SPACE_IMAGE) {
908 GPU_matrix_scale_2f(1.0f / t->aspect[0], 1.0f / t->aspect[1]);
909 }
910
911 eGPUDepthTest depth_test_enabled = GPU_depth_test_get();
912 if (depth_test_enabled) {
914 }
915
917
919
920 float viewport[4];
921 GPU_viewport_size_get_f(viewport);
923
924 immUniform2fv("viewportSize", &viewport[2]);
925 immUniform1f("lineWidth", 3.0f * U.pixelsize);
926
929
930 immUniform1f("lineWidth", 1.0f * U.pixelsize);
933
935
936 if (depth_test_enabled) {
938 }
939
941 }
942}
943
945{
946 if ((t->flag & T_PROP_EDIT) == 0) {
947 return;
948 }
949
951
953
954 float viewport[4];
955 GPU_viewport_size_get_f(viewport);
957
958 immUniform2fv("viewportSize", &viewport[2]);
959
960 View2D *v2d = &t->region->v2d;
961 const float x1 = t->center_global[0] - t->prop_size;
962 const float y1 = v2d->cur.ymin;
963 const float x2 = t->center_global[0] + t->prop_size;
964 const float y2 = v2d->cur.ymax;
965
966 immUniform1f("lineWidth", 3.0f * U.pixelsize);
968 imm_draw_box_wire_3d(pos, x1, y1, x2, y2);
969
970 immUniform1f("lineWidth", 1.0f * U.pixelsize);
972 imm_draw_box_wire_3d(pos, x1, y1, x2, y2);
973
976}
977
979{
980 /* Draw the first one lighter because that's the one who controls the others.
981 * Meaning the transformation is projected on that one and just copied on the others
982 * constraint space.
983 * In a nutshell, the object with light axis is controlled by the user and the others follow.
984 * Without drawing the first light, users have little clue what they are doing.
985 */
986 short options = DRAWLIGHT;
987 float tmp_axismtx[3][3];
988
990 TransData *td = tc->data;
991 for (int i = 0; i < tc->data_len; i++, td++) {
992 float co[3];
993 const float(*axismtx)[3];
994
995 if (t->flag & T_PROP_EDIT) {
996 /* We're sorted, so skip the rest. */
997 if (td->factor == 0.0f) {
998 break;
999 }
1000 }
1001
1002 if (t->options & CTX_GPENCIL_STROKES) {
1003 /* Only draw a constraint line for one point, otherwise we can't see anything. */
1004 if ((options & DRAWLIGHT) == 0) {
1005 break;
1006 }
1007 }
1008
1009 if (t->options & CTX_SEQUENCER_IMAGE) {
1010 /* Because we construct an "L" shape to deform the sequence, we should skip
1011 * all points except the first vertex. Otherwise we will draw the same axis constraint line
1012 * 3 times for each strip.
1013 */
1014 if (i % 3 != 0) {
1015 continue;
1016 }
1017 }
1018
1019 if (t->flag & T_EDIT) {
1020 mul_v3_m4v3(co, tc->mat, td->center);
1021
1022 mul_m3_m3m3(tmp_axismtx, tc->mat3_unit, td->axismtx);
1023 axismtx = tmp_axismtx;
1024 }
1025 else {
1026 if (t->options & CTX_POSE_BONE) {
1027 mul_v3_m4v3(co, tc->mat, td->center);
1028 }
1029 else {
1030 copy_v3_v3(co, td->center);
1031 }
1032 axismtx = transform_object_axismtx_get(t, tc, td);
1033 }
1034
1035 if (t->con.mode & CON_AXIS0) {
1036 drawLine(t, co, axismtx[0], 'X', options);
1037 }
1038 if (t->con.mode & CON_AXIS1) {
1039 drawLine(t, co, axismtx[1], 'Y', options);
1040 }
1041 if (t->con.mode & CON_AXIS2) {
1042 drawLine(t, co, axismtx[2], 'Z', options);
1043 }
1045 }
1046 }
1047}
1048
1050
1051/* -------------------------------------------------------------------- */
1054
1056{
1057 t->con.mode |= CON_APPLY;
1058 *t->con.text = ' ';
1059
1060 /* When `dims` is zero, no constraints are set (or not set *yet*).
1061 * In this case `t->num.idx_max` is unlikely to be used.
1062 * Set to `t->idx_max` as it's the default when transform starts
1063 * to prevent numeric errors, see: #144916. */
1064 const short dims = getConstraintSpaceDimension(t);
1065 t->num.idx_max = (dims > 0) ? std::min<short>(dims - 1, t->idx_max) : t->idx_max;
1066}
1067
1069{
1070 if (t->orient_curr != O_DEFAULT) {
1072 }
1073
1074 t->con.mode &= ~(CON_APPLY | CON_SELECT);
1075 *t->con.text = '\0';
1076 t->num.idx_max = t->idx_max;
1077}
1078
1080
1081/* -------------------------------------------------------------------- */
1084
1086{
1087 if (t->orient_curr == O_DEFAULT) {
1089 }
1090
1092}
1093
1095{
1096 if (t->con.mode & CON_SELECT) {
1097 setNearestAxis(t);
1098 startConstraint(t);
1099 }
1100}
1101
1103{
1104 t->con.mode &= ~CON_SELECT;
1105 if (!(t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2))) {
1106 t->con.mode &= ~CON_APPLY;
1107 }
1108}
1109
1111{
1112 /* Clear any prior constraint flags. */
1113 t->con.mode &= ~(CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
1114
1115 /* No correction needed... just use whichever one is lower. */
1116 float2 dvec = t->mval - t->mouse.imval;
1117 if (abs(dvec.x) < abs(dvec.y)) {
1118 t->con.mode |= CON_AXIS1;
1119 STRNCPY(t->con.text, IFACE_(" along Y axis"));
1120 }
1121 else {
1122 t->con.mode |= CON_AXIS0;
1123 STRNCPY(t->con.text, IFACE_(" along X axis"));
1124 }
1125}
1126
1128{
1129 /* Clear any prior constraint flags. */
1130 t->con.mode &= ~(CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
1131
1132 float zfac;
1133 float mvec[3], proj[3];
1134 float len[3];
1135 int i;
1136
1137 /* Calculate mouse movement. */
1138 mvec[0] = t->mval[0] - t->mouse.imval[0];
1139 mvec[1] = t->mval[1] - t->mouse.imval[1];
1140 mvec[2] = 0.0f;
1141
1142 /* We need to correct axis length for the current zoom-level of view,
1143 * this to prevent projected values to be clipped behind the camera
1144 * and to overflow the short integers.
1145 * The formula used is a bit stupid, just a simplification of the subtraction
1146 * of two 2D points 30 pixels apart (that's the last factor in the formula) after
1147 * projecting them with #ED_view3d_win_to_delta and then get the length of that vector. */
1149 zfac = len_v3(t->persinv[0]) * 2.0f / t->region->winx * zfac * 30.0f;
1150
1151 for (i = 0; i < 3; i++) {
1152 float axis[3], axis_2d[2];
1153
1154 copy_v3_v3(axis, t->spacemtx[i]);
1155
1156 mul_v3_fl(axis, zfac);
1157 /* Now we can project to get window coordinate. */
1158 add_v3_v3(axis, t->center_global);
1159 projectFloatView(t, axis, axis_2d);
1160
1161 sub_v2_v2v2(axis, axis_2d, t->center2d);
1162 axis[2] = 0.0f;
1163
1164 if (normalize_v3(axis) > 1e-3f) {
1165 project_v3_v3v3(proj, mvec, axis);
1166 sub_v3_v3v3(axis, mvec, proj);
1167 len[i] = normalize_v3(axis);
1168 }
1169 else {
1170 len[i] = 1e10f;
1171 }
1172 }
1173
1174 if (len[0] <= len[1] && len[0] <= len[2]) {
1176 t->con.mode |= (CON_AXIS1 | CON_AXIS2);
1177 SNPRINTF(t->con.text, IFACE_(" locking %s X axis"), t->spacename);
1178 }
1179 else {
1180 t->con.mode |= CON_AXIS0;
1181 SNPRINTF(t->con.text, IFACE_(" along %s X axis"), t->spacename);
1182 }
1183 }
1184 else if (len[1] <= len[0] && len[1] <= len[2]) {
1186 t->con.mode |= (CON_AXIS0 | CON_AXIS2);
1187 SNPRINTF(t->con.text, IFACE_(" locking %s Y axis"), t->spacename);
1188 }
1189 else {
1190 t->con.mode |= CON_AXIS1;
1191 SNPRINTF(t->con.text, IFACE_(" along %s Y axis"), t->spacename);
1192 }
1193 }
1194 else if (len[2] <= len[1] && len[2] <= len[0]) {
1196 t->con.mode |= (CON_AXIS0 | CON_AXIS1);
1197 SNPRINTF(t->con.text, IFACE_(" locking %s Z axis"), t->spacename);
1198 }
1199 else {
1200 t->con.mode |= CON_AXIS2;
1201 SNPRINTF(t->con.text, IFACE_(" along %s Z axis"), t->spacename);
1202 }
1203 }
1204}
1205
1207{
1208 eTConstraint mode_prev = t->con.mode;
1209
1210 /* Constraint setting - depends on spacetype. */
1211 if (t->spacetype == SPACE_VIEW3D) {
1212 /* 3d-view. */
1214 }
1215 else {
1216 /* Assume that this means a 2D-Editor. */
1218 }
1219
1220 if (mode_prev != t->con.mode) {
1223 }
1224}
1225
1227
1228/* -------------------------------------------------------------------- */
1231
1233{
1234 if ((t->con.mode & CON_APPLY) == 0) {
1235 return -1;
1236 }
1237 switch (int(t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2))) {
1238 case (CON_AXIS0):
1239 case (CON_AXIS1 | CON_AXIS2):
1240 return 0;
1241 case (CON_AXIS1):
1242 case (CON_AXIS0 | CON_AXIS2):
1243 return 1;
1244 case (CON_AXIS2):
1245 case (CON_AXIS0 | CON_AXIS1):
1246 return 2;
1247 default:
1248 return -1;
1249 }
1250}
1251
1253{
1254 int mode = t->con.mode;
1255
1256 if ((mode & (CON_AXIS0 | CON_AXIS1)) == (CON_AXIS0 | CON_AXIS1)) {
1257 return true;
1258 }
1259
1260 if ((mode & (CON_AXIS1 | CON_AXIS2)) == (CON_AXIS1 | CON_AXIS2)) {
1261 return true;
1262 }
1263
1264 if ((mode & (CON_AXIS0 | CON_AXIS2)) == (CON_AXIS0 | CON_AXIS2)) {
1265 return true;
1266 }
1267
1268 return false;
1269}
1270
1272{
1273 int n = 0;
1274
1275 if (t->con.mode & CON_AXIS0) {
1276 n++;
1277 }
1278
1279 if (t->con.mode & CON_AXIS1) {
1280 n++;
1281 }
1282
1283 if (t->con.mode & CON_AXIS2) {
1284 n++;
1285 }
1286
1287 return n;
1288 /* Someone willing to do it cryptically could do the following instead:
1289 *
1290 * `return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2);`
1291 *
1292 * Based on the assumptions that the axis flags are one after the other and start at 1
1293 */
1294}
1295
1297
1298} // namespace blender::ed::transform
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ATTR_FALLTHROUGH
MINLINE float max_ff(float a, float b)
#define DEG2RADF(_deg)
#define M_PI_2
#define M_PI
void plane_from_point_normal_v3(float r_plane[4], const float plane_co[3], const float plane_no[3])
Definition math_geom.cc:217
bool isect_ray_ray_v3(const float ray_origin_a[3], const float ray_direction_a[3], const float ray_origin_b[3], const float ray_direction_b[3], float *r_lambda_a, float *r_lambda_b)
float closest_to_ray_v3(float r_close[3], const float p[3], const float ray_orig[3], const float ray_dir[3])
bool isect_plane_plane_v3(const float plane_a[4], const float plane_b[4], float r_isect_co[3], float r_isect_no[3]) ATTR_WARN_UNUSED_RESULT
bool isect_ray_plane_v3_factor(const float ray_origin[3], const float ray_direction[3], const float plane_co[3], const float plane_no[3], float *r_lambda)
void mul_m3_v3(const float M[3][3], float r[3])
void unit_m3(float m[3][3])
bool invert_m3_m3(float inverse[3][3], const float mat[3][3])
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
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])
void unit_m4(float m[4][4])
float angle_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
void project_v3_v3v3(float out[3], const float p[3], const float v_proj[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float normalize_v3_v3_length(float r[3], const float a[3], float unit_length)
MINLINE void negate_v3(float r[3])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float mul_project_m4_v3_zfac(const float mat[4][4], const float co[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
MINLINE void zero_v3(float r[3])
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
unsigned char uchar
unsigned int uint
#define UNUSED_FUNCTION(x)
#define ELEM(...)
#define IFACE_(msgid)
@ SCE_SNAP_TO_EDGE
@ SCE_SNAP_TO_FACE
@ RGN_TYPE_WINDOW
@ SPACE_NODE
@ SPACE_SEQ
@ SPACE_IMAGE
@ SPACE_VIEW3D
@ V3D_ORIENT_NORMAL
@ V3D_ORIENT_CUSTOM
@ V3D_ORIENT_GLOBAL
@ V3D_ORIENT_CUSTOM_MATRIX
@ V3D_ORIENT_LOCAL
@ V3D_ORIENT_VIEW
@ V3D_ORIENT_CURSOR
@ V3D_ORIENT_GIMBAL
@ RV3D_ORTHO
bool hasNumInput(const NumInput *n)
Definition numinput.cc:170
void immEnd()
void immUniform2fv(const char *name, const float data[2])
void immUnbindProgram()
void immUniform2f(const char *name, float x, float y)
void immUniformThemeColorShadeAlpha(int color_id, int color_offset, int alpha_offset)
void immUniformColor4f(float r, float g, float b, float a)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immUniformColor3ubv(const unsigned char rgb[3])
void immUniform1i(const char *name, int x)
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 imm_drawcircball(const float cent[3], float radius, const float tmat[4][4], uint pos)
void imm_draw_box_wire_3d(uint pos, float x1, float y1, float x2, float y2)
void GPU_matrix_scale_2f(float x, float y)
void GPU_matrix_push()
void GPU_matrix_pop()
@ GPU_PRIM_LINES
@ GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR
@ GPU_SHADER_3D_POLYLINE_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
eGPUDepthTest GPU_depth_test_get()
Definition gpu_state.cc:244
eGPUDepthTest
Definition GPU_state.hh:110
@ GPU_DEPTH_LESS_EQUAL
Definition GPU_state.hh:114
@ GPU_DEPTH_NONE
Definition GPU_state.hh:111
void GPU_depth_test(eGPUDepthTest test)
Definition gpu_state.cc:68
void GPU_viewport_size_get_f(float coords[4])
Definition gpu_state.cc:273
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, blender::StringRef name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
@ TH_GRID
void UI_GetThemeColor3ubv(int colorid, unsigned char col[3])
void UI_make_axis_color(const unsigned char col[3], char axis, unsigned char r_col[3])
#define U
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert * v
static void mul(btAlignedObjectArray< T > &items, const Q &value)
SIMD_FORCE_INLINE btScalar norm() const
Return the norm (length) of the vector.
Definition btVector3.h:263
CCL_NAMESPACE_BEGIN struct Options options
#define fabsf(x)
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
uint pos
uint col
#define in
#define abs
#define out
static void constraint_plane_normal_calc(const TransInfo *t, float r_plane_no[3])
void postSelectConstraint(TransInfo *t)
static bool isPlaneProjectionViewAligned(const TransInfo *t, const float plane_no[3])
static short transform_orientation_or_default(const TransInfo *t)
int getConstraintSpaceDimension(const TransInfo *t)
void setUserConstraint(TransInfo *t, int mode, const char text_[])
static void setNearestAxis3d(TransInfo *t)
static void applyAxisConstraintSize(const TransInfo *t, const TransDataContainer *, const TransData *td, float r_smat[3][3])
static void planeProjection(const TransInfo *t, const float plane_no[3], const float in[3], float out[3])
bool isLockConstraint(const TransInfo *t)
static void applyAxisConstraintRot(const TransInfo *t, const TransDataContainer *, const TransData *td, float r_axis[3], float *r_angle)
bool validSnap(const TransInfo *t)
void setAxisMatrixConstraint(TransInfo *t, int mode, const char text[])
void setLocalConstraint(TransInfo *t, int mode, const char text[])
static void drawLine(TransInfo *t, const float center[3], const float dir[3], char axis, short options)
static void applyObjectConstraintVec(const TransInfo *t, const TransDataContainer *tc, const TransData *td, const float in[3], float out[3])
void transform_constraint_snap_axis_to_face(const TransInfo *t, const float axis[3], float r_out[3])
void setConstraint(TransInfo *t, int mode, const char text[])
void transform_constraint_snap_axis_to_edge(const TransInfo *t, const float axis[3], float r_out[3])
void projectFloatView(TransInfo *t, const float vec[3], float adr[2])
void convertViewVec(TransInfo *t, float r_vec[3], double dx, double dy)
static void axisProjection(const TransInfo *t, const float axis[3], const float in[3], float out[3])
static void applyAxisConstraintVec(const TransInfo *t, const TransDataContainer *, const TransData *td, const float in[3], float out[3])
static void view_vector_calc(const TransInfo *t, const float focus[3], float r_vec[3])
static void constraint_snap_plane_to_edge(const TransInfo *t, const float plane_no[3], float r_out[3])
void transform_gizmo_3d_model_from_constraint_and_mode_set(TransInfo *t)
static void setNearestAxis2d(TransInfo *t)
void transform_constraint_get_nearest(const TransInfo *t, const float3 &vec, float r_vec[3])
static void constraints_rotation_impl(const TransInfo *t, const float axismtx[3][3], float r_axis[3], float *r_angle)
bool transform_snap_is_active(const TransInfo *t)
static void projection_matrix_calc(const TransInfo *t, float r_pmtx[3][3])
void initSelectConstraint(TransInfo *t)
static void drawObjectConstraint(TransInfo *t)
static void viewAxisCorrectCenter(const TransInfo *t, float t_con_center[3])
void selectConstraint(TransInfo *t)
void constraintNumInput(TransInfo *t, float vec[3])
static void applyObjectConstraintRot(const TransInfo *t, const TransDataContainer *tc, const TransData *td, float r_axis[3], float *r_angle)
static const float(* transform_object_axismtx_get(const TransInfo *t, const TransDataContainer *, const TransData *td))[3]
const char * transform_orientations_spacename_get(TransInfo *t, const short orient_type)
void drawConstraint(TransInfo *t)
int constraintModeToIndex(const TransInfo *t)
void drawPropCircle(TransInfo *t)
void transform_orientations_current_set(TransInfo *t, const short orient_index)
static void applyObjectConstraintSize(const TransInfo *t, const TransDataContainer *tc, const TransData *td, float r_smat[3][3])
VecBase< float, 2 > float2
VecBase< float, 3 > float3
const btScalar eps
Definition poly34.cpp:11
void * regiondata
short idx_max
float viewmat[4][4]
void(* applyRot)(const TransInfo *t, const TransDataContainer *tc, const TransData *td, float r_axis[3], float *r_angle)
Definition transform.hh:590
void(* applySize)(const TransInfo *t, const TransDataContainer *tc, const TransData *td, float r_smat[3][3])
Definition transform.hh:585
void(* applyVec)(const TransInfo *t, const TransDataContainer *tc, const TransData *td, const float in[3], float r_out[3])
Definition transform.hh:579
void(* drawExtra)(TransInfo *t)
Definition transform.hh:571
struct blender::ed::transform::TransInfo::@040124034302070131153200326237043302276016250327 orient[3]
float ymax
float ymin
i
Definition text_draw.cc:230
#define TRANS_DATA_CONTAINER_FIRST_OK(t)
Definition transform.hh:37
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
Definition transform.hh:42
#define CONSTRAIN_EPSILON
#define DRAWLIGHT
uint len