Blender V5.0
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_utf8.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
72/* ************************** CONSTRAINTS ************************* */
73#define CONSTRAIN_EPSILON 0.0001f
74
75static void constraint_plane_normal_calc(const TransInfo *t, float r_plane_no[3])
76{
77 const float *constraint_vector[2];
78 int n = 0;
79 for (int i = 0; i < 3; i++) {
80 if (t->con.mode & (CON_AXIS0 << i)) {
81 constraint_vector[n++] = t->spacemtx[i];
82 if (n == 2) {
83 break;
84 }
85 }
86 }
87 BLI_assert(n == 2);
88
89 cross_v3_v3v3(r_plane_no, constraint_vector[0], constraint_vector[1]);
90 normalize_v3(r_plane_no);
91}
92
93void constraintNumInput(TransInfo *t, float vec[3])
94{
95 int mode = t->con.mode;
96 if (mode & CON_APPLY) {
97 float nval = (t->flag & T_NULL_ONE) ? 1.0f : 0.0f;
98
99 const int dims = getConstraintSpaceDimension(t);
100 if (dims == 2) {
101 int axis = mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
102 if (axis == (CON_AXIS0 | CON_AXIS1)) {
103 // vec[0] = vec[0]; /* Same. */
104 // vec[1] = vec[1]; /* Same. */
105 vec[2] = nval;
106 }
107 else if (axis == (CON_AXIS1 | CON_AXIS2)) {
108 vec[2] = vec[1];
109 vec[1] = vec[0];
110 vec[0] = nval;
111 }
112 else if (axis == (CON_AXIS0 | CON_AXIS2)) {
113 // vec[0] = vec[0]; /* Same. */
114 vec[2] = vec[1];
115 vec[1] = nval;
116 }
117 }
118 else if (dims == 1) {
119 if (mode & CON_AXIS0) {
120 // vec[0] = vec[0]; /* Same. */
121 vec[1] = nval;
122 vec[2] = nval;
123 }
124 else if (mode & CON_AXIS1) {
125 vec[1] = vec[0];
126 vec[0] = nval;
127 vec[2] = nval;
128 }
129 else if (mode & CON_AXIS2) {
130 vec[2] = vec[0];
131 vec[0] = nval;
132 vec[1] = nval;
133 }
134 }
135 }
136}
137
138static void viewAxisCorrectCenter(const TransInfo *t, float t_con_center[3])
139{
140 if (t->spacetype == SPACE_VIEW3D) {
141 // View3D *v3d = t->area->spacedata.first;
142 const float min_dist = 1.0f; /* `v3d->clip_start`. */
143 float dir[3];
144 float l;
145
146 sub_v3_v3v3(dir, t_con_center, t->viewinv[3]);
147 if (dot_v3v3(dir, t->viewinv[2]) < 0.0f) {
148 negate_v3(dir);
149 }
150 project_v3_v3v3(dir, dir, t->viewinv[2]);
151
152 l = len_v3(dir);
153
154 if (l < min_dist) {
155 float diff[3];
156 normalize_v3_v3_length(diff, t->viewinv[2], min_dist - l);
157 sub_v3_v3(t_con_center, diff);
158 }
159 }
160}
161
165static void axisProjection(const TransInfo *t,
166 const float axis[3],
167 const float in[3],
168 float out[3])
169{
170 float vec[3], factor, angle;
171 float t_con_center[3];
172
173 if (is_zero_v3(in)) {
174 return;
175 }
176
177 copy_v3_v3(t_con_center, t->center_global);
178
179 /* Checks for center being too close to the view center. */
180 viewAxisCorrectCenter(t, t_con_center);
181
182 angle = fabsf(angle_v3v3(axis, t->viewinv[2]));
183 if (angle > float(M_PI_2)) {
184 angle = float(M_PI) - angle;
185 }
186
187 /* For when view is parallel to constraint... will cause NaNs otherwise
188 * So we take vertical motion in 3D space and apply it to the
189 * constraint axis. Nice for camera grab + MMB. */
190 if (angle < DEG2RADF(5.0f)) {
191 project_v3_v3v3(vec, in, t->viewinv[1]);
192 factor = dot_v3v3(t->viewinv[1], vec) * 2.0f;
193 /* Since camera distance is quite relative, use quadratic relationship.
194 * holding shift can compensate. */
195 if (factor < 0.0f) {
196 factor *= -factor;
197 }
198 else {
199 factor *= factor;
200 }
201
202 /* -factor makes move down going backwards. */
203 normalize_v3_v3_length(out, axis, -factor);
204 }
205 else {
206 float v[3];
207 float norm[3], norm_center[3];
208 float plane[3];
209
210 view_vector_calc(t, t_con_center, norm_center);
211 cross_v3_v3v3(plane, norm_center, axis);
212
213 project_v3_v3v3(vec, in, plane);
214 sub_v3_v3v3(vec, in, vec);
215
216 add_v3_v3v3(v, vec, t_con_center);
218
219 /* Give arbitrary large value if projection is impossible. */
220 factor = dot_v3v3(axis, norm);
221 if (1.0f - fabsf(factor) < 0.0002f) {
222 copy_v3_v3(out, axis);
223 if (factor > 0) {
224 mul_v3_fl(out, 1000000000.0f);
225 }
226 else {
227 mul_v3_fl(out, -1000000000.0f);
228 }
229 }
230 else {
231 /* Use ray-ray intersection instead of line-line because this gave
232 * precision issues adding small values to large numbers. */
233 float mul;
234 if (isect_ray_ray_v3(t_con_center, axis, v, norm, &mul, nullptr)) {
235 mul_v3_v3fl(out, axis, mul);
236 }
237 else {
238 /* In practice this should never fail. */
239 BLI_assert(0);
240 }
241
242 /* Possible some values become nan when viewpoint and object are both zero. */
243 if (!isfinite(out[0])) {
244 out[0] = 0.0f;
245 }
246 if (!isfinite(out[1])) {
247 out[1] = 0.0f;
248 }
249 if (!isfinite(out[2])) {
250 out[2] = 0.0f;
251 }
252 }
253 }
254}
255
260 const float plane_no[3],
261 float r_out[3])
262{
263 float lambda;
264 const float *edge_snap_point = t->tsnap.snap_target;
265 const float *edge_dir = t->tsnap.snapNormal;
266 bool is_aligned = fabsf(dot_v3v3(edge_dir, plane_no)) < CONSTRAIN_EPSILON;
267 if (!is_aligned && isect_ray_plane_v3_factor(
268 edge_snap_point, edge_dir, t->tsnap.snap_source, plane_no, &lambda))
269 {
270 madd_v3_v3v3fl(r_out, edge_snap_point, edge_dir, lambda);
271 sub_v3_v3(r_out, t->tsnap.snap_source);
272 }
273}
274
275static void UNUSED_FUNCTION(constraint_snap_plane_to_face(const TransInfo *t,
276 const float plane[4],
277 float r_out[3]))
278{
279 float face_plane[4], isect_orig[3], isect_dir[3];
280 const float *face_snap_point = t->tsnap.snap_target;
281 const float *face_normal = t->tsnap.snapNormal;
282 plane_from_point_normal_v3(face_plane, face_snap_point, face_normal);
283 bool is_aligned = fabsf(dot_v3v3(plane, face_plane)) > (1.0f - CONSTRAIN_EPSILON);
284 if (!is_aligned && isect_plane_plane_v3(plane, face_plane, isect_orig, isect_dir)) {
285 closest_to_ray_v3(r_out, face_snap_point, isect_orig, isect_dir);
286 sub_v3_v3(r_out, t->tsnap.snap_source);
287 }
288}
289
291 const float axis[3],
292 float r_out[3])
293{
294 float lambda;
295 const float *edge_snap_point = t->tsnap.snap_target;
296 const float *edge_dir = t->tsnap.snapNormal;
297 bool is_aligned = fabsf(dot_v3v3(axis, edge_dir)) > (1.0f - CONSTRAIN_EPSILON);
298 if (!is_aligned &&
299 isect_ray_ray_v3(t->tsnap.snap_source, axis, edge_snap_point, edge_dir, &lambda, nullptr))
300 {
301 mul_v3_v3fl(r_out, axis, lambda);
302 }
303}
304
306 const float axis[3],
307 float r_out[3])
308{
309 float lambda;
310 const float *face_snap_point = t->tsnap.snap_target;
311 const float *face_normal = t->tsnap.snapNormal;
312 bool is_aligned = fabsf(dot_v3v3(axis, face_normal)) < CONSTRAIN_EPSILON;
313 if (!is_aligned &&
314 isect_ray_plane_v3_factor(t->tsnap.snap_source, axis, face_snap_point, face_normal, &lambda))
315 {
316 mul_v3_v3fl(r_out, axis, lambda);
317 }
318}
319
324static bool isPlaneProjectionViewAligned(const TransInfo *t, const float plane_no[3])
325{
326 const float eps = 0.001f;
327 float view_to_plane[3];
328 view_vector_calc(t, t->center_global, view_to_plane);
329
330 float factor = dot_v3v3(plane_no, view_to_plane);
331 return fabsf(factor) < eps;
332}
333
334static void planeProjection(const TransInfo *t,
335 const float plane_no[3],
336 const float in[3],
337 float out[3])
338{
339
340 float pos[3], view_vec[3], factor;
341
343 view_vector_calc(t, pos, view_vec);
344
345 if (isect_ray_plane_v3_factor(pos, view_vec, t->center_global, plane_no, &factor)) {
346 madd_v3_v3v3fl(out, in, view_vec, factor);
347 }
348}
349
351{
352 short orientation = t->orient[t->orient_curr].type;
353 if (orientation == V3D_ORIENT_CUSTOM_MATRIX) {
354 /* Use the real value of the "orient_type". */
355 orientation = t->orient[O_DEFAULT].type;
356 }
357 return orientation;
358}
359
361 const TransDataContainer *tc,
362 const TransData *td))[3]
363{
365 BLI_assert(t->orient_type_mask & (1 << V3D_ORIENT_GIMBAL));
366 if (t->options & (CTX_POSE_BONE | CTX_OBJECT)) {
367 TransDataExtension *td_ext = &tc->data_ext[td - tc->data];
368 return td_ext->axismtx_gimbal;
369 }
370 }
371 return td->axismtx;
372}
373
374void transform_constraint_get_nearest(const TransInfo *t, const float3 &vec, float r_vec[3])
375{
376 bool is_snap_to_point = false, is_snap_to_edge = false, is_snap_to_face = false;
377
379 if (validSnap(t)) {
380 is_snap_to_edge = (t->tsnap.target_type & SCE_SNAP_TO_EDGE) != 0;
381 is_snap_to_face = (t->tsnap.target_type & SCE_SNAP_TO_FACE) != 0;
382 is_snap_to_point = !is_snap_to_edge && !is_snap_to_face;
383 }
384 }
385
386 /* Fallback for when axes are aligned. */
387 mul_v3_m3v3(r_vec, t->con.pmtx, vec);
388
389 if (is_snap_to_point) {
390 /* Pass. With snap points, a projection is alright, no adjustments needed. */
391 }
392 else {
393 const int dims = getConstraintSpaceDimension(t);
394 if (dims == 2) {
395 if (!is_zero_v3(r_vec)) {
396 float plane_no[3];
397 constraint_plane_normal_calc(t, plane_no);
398
399 if (is_snap_to_edge) {
400 constraint_snap_plane_to_edge(t, plane_no, r_vec);
401 }
402 else if (is_snap_to_face) {
403 /* Disabled, as it has not proven to be really useful. (See #82386). */
404 // constraint_snap_plane_to_face(t, plane, out);
405 }
406 else if (!isPlaneProjectionViewAligned(t, plane_no)) {
407 /* View alignment correction. */
408 planeProjection(t, plane_no, vec, r_vec);
409 }
410 }
411 }
412 else if (dims == 1) {
413 float c[3];
414
415 if (t->con.mode & CON_AXIS0) {
416 copy_v3_v3(c, t->spacemtx[0]);
417 }
418 else if (t->con.mode & CON_AXIS1) {
419 copy_v3_v3(c, t->spacemtx[1]);
420 }
421 else {
423 copy_v3_v3(c, t->spacemtx[2]);
424 }
425
426 if (is_snap_to_edge) {
428 }
429 else if (is_snap_to_face) {
431 }
432 else {
433 /* View alignment correction. */
434 axisProjection(t, c, vec, r_vec);
435 }
436 }
437 }
438}
439
448 const TransDataContainer * /*tc*/,
449 const TransData *td,
450 const float in[3],
451 float out[3])
452{
453 if (td || !(t->con.mode & CON_APPLY)) {
454 copy_v3_v3(out, in);
455 return;
456 }
457
459}
460
472 const TransDataContainer *tc,
473 const TransData *td,
474 const float in[3],
475 float out[3])
476{
477 if (!td) {
478 applyAxisConstraintVec(t, tc, td, in, out);
479 }
480 else {
481 /* Specific TransData's space. */
482 copy_v3_v3(out, in);
483 if (t->con.mode & CON_APPLY) {
485 const float (*axismtx)[3] = transform_object_axismtx_get(t, tc, td);
486 mul_m3_v3(axismtx, out);
487 if (t->flag & T_EDIT) {
488 mul_m3_v3(tc->mat3_unit, out);
489 }
490 }
491 }
492}
493
498 const TransDataContainer * /*tc*/,
499 const TransData *td,
500 float r_smat[3][3])
501{
502 if (!td && t->con.mode & CON_APPLY) {
503 float tmat[3][3];
504
505 if (!(t->con.mode & CON_AXIS0)) {
506 r_smat[0][0] = 1.0f;
507 }
508 if (!(t->con.mode & CON_AXIS1)) {
509 r_smat[1][1] = 1.0f;
510 }
511 if (!(t->con.mode & CON_AXIS2)) {
512 r_smat[2][2] = 1.0f;
513 }
514
515 mul_m3_m3m3(tmat, r_smat, t->spacemtx_inv);
516 mul_m3_m3m3(r_smat, t->spacemtx, tmat);
517 }
518}
519
524 const TransDataContainer *tc,
525 const TransData *td,
526 float r_smat[3][3])
527{
528 if (td && t->con.mode & CON_APPLY) {
529 float tmat[3][3];
530 float imat[3][3];
531
532 const float (*axismtx)[3] = transform_object_axismtx_get(t, tc, td);
533 invert_m3_m3(imat, axismtx);
534
535 if (!(t->con.mode & CON_AXIS0)) {
536 r_smat[0][0] = 1.0f;
537 }
538 if (!(t->con.mode & CON_AXIS1)) {
539 r_smat[1][1] = 1.0f;
540 }
541 if (!(t->con.mode & CON_AXIS2)) {
542 r_smat[2][2] = 1.0f;
543 }
544
545 mul_m3_m3m3(tmat, r_smat, imat);
546 if (t->flag & T_EDIT) {
547 mul_m3_m3m3(r_smat, tc->mat3_unit, r_smat);
548 }
549 mul_m3_m3m3(r_smat, axismtx, tmat);
550 }
551}
552
554 const float axismtx[3][3],
555 float r_axis[3])
556{
558 int mode = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
559
560 switch (mode) {
561 case CON_AXIS0:
562 case (CON_AXIS1 | CON_AXIS2):
563 copy_v3_v3(r_axis, axismtx[0]);
564 break;
565 case CON_AXIS1:
566 case (CON_AXIS0 | CON_AXIS2):
567 copy_v3_v3(r_axis, axismtx[1]);
568 break;
569 case CON_AXIS2:
570 case (CON_AXIS0 | CON_AXIS1):
571 copy_v3_v3(r_axis, axismtx[2]);
572 break;
573 }
574}
575
585 const TransDataContainer * /*tc*/,
586 const TransData *td,
587 float r_axis[3])
588{
589 if (!td && t->con.mode & CON_APPLY) {
590 constraints_rotation_impl(t, t->spacemtx, r_axis);
591 }
592}
593
603 const TransDataContainer *tc,
604 const TransData *td,
605 float r_axis[3])
606{
607 if (t->con.mode & CON_APPLY) {
608 float tmp_axismtx[3][3];
609 const float (*axismtx)[3];
610
611 /* On setup call, use first object. */
612 if (td == nullptr) {
613 BLI_assert(tc == nullptr);
615 td = tc->data;
616 }
617
618 if (t->flag & T_EDIT) {
619 mul_m3_m3m3(tmp_axismtx, tc->mat3_unit, td->axismtx);
620 axismtx = tmp_axismtx;
621 }
622 else {
623 axismtx = transform_object_axismtx_get(t, tc, td);
624 }
625
626 constraints_rotation_impl(t, axismtx, r_axis);
627 }
628}
629
631
632/* -------------------------------------------------------------------- */
635
636void setConstraint(TransInfo *t, int mode, const char text[])
637{
638 BLI_strncpy_utf8(t->con.text + 1, text, sizeof(t->con.text) - 1);
639 t->con.mode = eTConstraint(mode);
641
643
644 t->con.drawExtra = nullptr;
648 t->redraw = TREDRAW_HARD;
649}
650
651void setAxisMatrixConstraint(TransInfo *t, int mode, const char text[])
652{
653 BLI_strncpy_utf8(t->con.text + 1, text, sizeof(t->con.text) - 1);
654 t->con.mode = eTConstraint(mode);
656
658
663 t->redraw = TREDRAW_HARD;
664}
665
666void setLocalConstraint(TransInfo *t, int mode, const char text[])
667{
668 if ((t->flag & T_EDIT) || t->data_len_all == 1) {
669 /* Although in edit-mode each object has its local space, use the
670 * orientation of the active object. */
671 setConstraint(t, mode, text);
672 }
673 else {
675 }
676}
677
678void setUserConstraint(TransInfo *t, int mode, const char text_[])
679{
680 char text[256];
681 const short orientation = transform_orientation_or_default(t);
682 const char *spacename = transform_orientations_spacename_get(t, orientation);
683 SNPRINTF_UTF8(text, text_, spacename);
684
685 switch (orientation) {
686 case V3D_ORIENT_LOCAL:
688 setLocalConstraint(t, mode, text);
689 break;
691 if (checkUseAxisMatrix(t)) {
693 break;
694 }
697 case V3D_ORIENT_VIEW:
701 default: {
702 setConstraint(t, mode, text);
703 break;
704 }
705 }
706 t->con.mode |= CON_USER;
707}
708
710
711/* -------------------------------------------------------------------- */
714
715static void drawLine(
716 TransInfo *t, const float center[3], const float dir[3], char axis, short options)
717{
719 return;
720 }
721
722 float v1[3], v2[3], v3[3];
723 uchar col[3], col2[3];
724
725 if (t->spacetype == SPACE_VIEW3D) {
726 View3D *v3d = static_cast<View3D *>(t->view);
727
728 copy_v3_v3(v3, dir);
729 mul_v3_fl(v3, v3d->clip_end);
730
731 sub_v3_v3v3(v2, center, v3);
732 add_v3_v3v3(v1, center, v3);
733 }
734 else if (t->spacetype == SPACE_SEQ) {
735 View2D *v2d = static_cast<View2D *>(t->view);
736
737 copy_v3_v3(v3, dir);
738 float max_dist = max_ff(BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur));
739 mul_v3_fl(v3, max_dist);
740
741 sub_v3_v3v3(v2, center, v3);
742 add_v3_v3v3(v1, center, v3);
743 }
744
746
747 if (options & DRAWLIGHT) {
748 col[0] = col[1] = col[2] = 220;
749 }
750 else {
752 }
753 UI_make_axis_color(col, axis, col2);
754
756 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
757
758 float viewport[4];
759 GPU_viewport_size_get_f(viewport);
761
763 immUniform2fv("viewportSize", &viewport[2]);
764 immUniform1f("lineWidth", U.pixelsize * 2.0f);
765
767
769 immVertex3fv(pos, v1);
771 immEnd();
772
774
776}
777
779{
780 TransCon *tc = &(t->con);
781
783 return;
784 }
785 if (!(tc->mode & CON_APPLY)) {
786 return;
787 }
788 if (t->flag & T_NO_CONSTRAINT) {
789 return;
790 }
791
792 if (tc->drawExtra) {
793 tc->drawExtra(t);
794 }
795 else {
796 if (tc->mode & CON_SELECT) {
797 float vec[3];
798
799 convertViewVec(t, vec, (t->mval[0] - t->mouse.imval[0]), (t->mval[1] - t->mouse.imval[1]));
800 add_v3_v3(vec, t->center_global);
801
802 drawLine(t, t->center_global, t->spacemtx[0], 'X', 0);
803 drawLine(t, t->center_global, t->spacemtx[1], 'Y', 0);
804 drawLine(t, t->center_global, t->spacemtx[2], 'Z', 0);
805
806 GPUDepthTest depth_test_enabled = GPU_depth_test_get();
807 if (depth_test_enabled) {
809 }
810
811 const uint shdr_pos = GPU_vertformat_attr_add(
812 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
813
815
816 float viewport_size[4];
817 GPU_viewport_size_get_f(viewport_size);
818 immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
819
820 immUniform1i("colors_len", 0); /* "simple" mode. */
821 immUniformColor4f(1.0f, 1.0f, 1.0f, 1.0f);
822 immUniform1f("dash_width", 2.0f);
823 immUniform1f("udash_factor", 0.5f);
824
826 immVertex3fv(shdr_pos, t->center_global);
827 immVertex3fv(shdr_pos, vec);
828 immEnd();
829
831
832 if (depth_test_enabled) {
834 }
835 }
836
837 if (tc->mode & CON_AXIS0) {
838 drawLine(t, t->center_global, t->spacemtx[0], 'X', DRAWLIGHT);
839 }
840 if (tc->mode & CON_AXIS1) {
841 drawLine(t, t->center_global, t->spacemtx[1], 'Y', DRAWLIGHT);
842 }
843 if (tc->mode & CON_AXIS2) {
844 drawLine(t, t->center_global, t->spacemtx[2], 'Z', DRAWLIGHT);
845 }
846 }
847}
848
850{
851 if (t->flag & T_PROP_EDIT) {
852 const RegionView3D *rv3d = nullptr;
853 float tmat[4][4], imat[4][4];
854
855 if (t->spacetype == SPACE_VIEW3D) {
856 if (t->region && (t->region->regiontype == RGN_TYPE_WINDOW)) {
857 rv3d = static_cast<const RegionView3D *>(t->region->regiondata);
858 }
859 }
860
861 if (rv3d != nullptr) {
862 copy_m4_m4(tmat, rv3d->viewmat);
863 invert_m4_m4(imat, tmat);
864 }
865 else {
866 unit_m4(tmat);
867 unit_m4(imat);
868 }
869
871
872 if (t->spacetype == SPACE_VIEW3D) {
873 /* Pass. */
874 }
875 else if (t->spacetype == SPACE_IMAGE) {
876 GPU_matrix_scale_2f(1.0f / t->aspect[0], 1.0f / t->aspect[1]);
877 }
878
879 GPUDepthTest depth_test_enabled = GPU_depth_test_get();
880 if (depth_test_enabled) {
882 }
883
885 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
886
888
889 float viewport[4];
890 GPU_viewport_size_get_f(viewport);
892
893 immUniform2fv("viewportSize", &viewport[2]);
894 immUniform1f("lineWidth", 3.0f * U.pixelsize);
895
898
899 immUniform1f("lineWidth", 1.0f * U.pixelsize);
902
904
905 if (depth_test_enabled) {
907 }
908
910 }
911}
912
914{
915 if ((t->flag & T_PROP_EDIT) == 0) {
916 return;
917 }
918
920 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
921
923
924 float viewport[4];
925 GPU_viewport_size_get_f(viewport);
927
928 immUniform2fv("viewportSize", &viewport[2]);
929
930 View2D *v2d = &t->region->v2d;
931 const float x1 = t->center_global[0] - t->prop_size;
932 const float y1 = v2d->cur.ymin;
933 const float x2 = t->center_global[0] + t->prop_size;
934 const float y2 = v2d->cur.ymax;
935
936 immUniform1f("lineWidth", 3.0f * U.pixelsize);
938 imm_draw_box_wire_3d(pos, x1, y1, x2, y2);
939
940 immUniform1f("lineWidth", 1.0f * U.pixelsize);
942 imm_draw_box_wire_3d(pos, x1, y1, x2, y2);
943
946}
947
949{
950 /* Draw the first one lighter because that's the one who controls the others.
951 * Meaning the transformation is projected on that one and just copied on the others
952 * constraint space.
953 * In a nutshell, the object with light axis is controlled by the user and the others follow.
954 * Without drawing the first light, users have little clue what they are doing.
955 */
956 short options = DRAWLIGHT;
957 float tmp_axismtx[3][3];
958
960 TransData *td = tc->data;
961 for (int i = 0; i < tc->data_len; i++, td++) {
962 float co[3];
963 const float (*axismtx)[3];
964
965 if (t->flag & T_PROP_EDIT) {
966 /* We're sorted, so skip the rest. */
967 if (td->factor == 0.0f) {
968 break;
969 }
970 }
971
972 if (t->options & CTX_GPENCIL_STROKES) {
973 /* Only draw a constraint line for one point, otherwise we can't see anything. */
974 if ((options & DRAWLIGHT) == 0) {
975 break;
976 }
977 }
978
979 if (t->options & CTX_SEQUENCER_IMAGE) {
980 /* Because we construct an "L" shape to deform the sequence, we should skip
981 * all points except the first vertex. Otherwise we will draw the same axis constraint line
982 * 3 times for each strip.
983 */
984 if (i % 3 != 0) {
985 continue;
986 }
987 }
988
989 if (t->flag & T_EDIT) {
990 mul_v3_m4v3(co, tc->mat, td->center);
991
992 mul_m3_m3m3(tmp_axismtx, tc->mat3_unit, td->axismtx);
993 axismtx = tmp_axismtx;
994 }
995 else {
996 if (t->options & CTX_POSE_BONE) {
997 mul_v3_m4v3(co, tc->mat, td->center);
998 }
999 else {
1000 copy_v3_v3(co, td->center);
1001 }
1002 axismtx = transform_object_axismtx_get(t, tc, td);
1003 }
1004
1005 if (t->con.mode & CON_AXIS0) {
1006 drawLine(t, co, axismtx[0], 'X', options);
1007 }
1008 if (t->con.mode & CON_AXIS1) {
1009 drawLine(t, co, axismtx[1], 'Y', options);
1010 }
1011 if (t->con.mode & CON_AXIS2) {
1012 drawLine(t, co, axismtx[2], 'Z', options);
1013 }
1015 }
1016 }
1017}
1018
1020
1021/* -------------------------------------------------------------------- */
1024
1026{
1027 t->con.mode |= CON_APPLY;
1028 *t->con.text = ' ';
1029
1030 /* When `dims` is zero, no constraints are set (or not set *yet*).
1031 * In this case `t->num.idx_max` is unlikely to be used.
1032 * Set to `t->idx_max` as it's the default when transform starts
1033 * to prevent numeric errors, see: #144916. */
1034 const short dims = getConstraintSpaceDimension(t);
1035 t->num.idx_max = (dims > 0) ? std::min<short>(dims - 1, t->idx_max) : t->idx_max;
1036}
1037
1039{
1040 if (t->orient_curr != O_DEFAULT) {
1042 }
1043
1044 t->con.mode &= ~(CON_APPLY | CON_SELECT);
1045 *t->con.text = '\0';
1046 t->num.idx_max = t->idx_max;
1047}
1048
1050
1051/* -------------------------------------------------------------------- */
1054
1056{
1057 if (t->orient_curr == O_DEFAULT) {
1059 }
1060
1062}
1063
1065{
1066 if (t->con.mode & CON_SELECT) {
1067 setNearestAxis(t);
1068 startConstraint(t);
1069 }
1070}
1071
1073{
1074 t->con.mode &= ~CON_SELECT;
1075 if (!(t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2))) {
1076 t->con.mode &= ~CON_APPLY;
1077 }
1078}
1079
1081{
1082 /* Clear any prior constraint flags. */
1083 t->con.mode &= ~(CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
1084
1085 /* No correction needed... just use whichever one is lower. */
1086 float2 dvec = t->mval - t->mouse.imval;
1087 if (abs(dvec.x) < abs(dvec.y)) {
1088 t->con.mode |= CON_AXIS1;
1089 STRNCPY_UTF8(t->con.text, IFACE_(" along Y axis"));
1090 }
1091 else {
1092 t->con.mode |= CON_AXIS0;
1093 STRNCPY_UTF8(t->con.text, IFACE_(" along X axis"));
1094 }
1095}
1096
1098{
1099 /* Clear any prior constraint flags. */
1100 t->con.mode &= ~(CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
1101
1102 float zfac;
1103 float mvec[3], proj[3];
1104 float len[3];
1105 int i;
1106
1107 /* Calculate mouse movement. */
1108 mvec[0] = t->mval[0] - t->mouse.imval[0];
1109 mvec[1] = t->mval[1] - t->mouse.imval[1];
1110 mvec[2] = 0.0f;
1111
1112 /* We need to correct axis length for the current zoom-level of view,
1113 * this to prevent projected values to be clipped behind the camera
1114 * and to overflow the short integers.
1115 * The formula used is a bit stupid, just a simplification of the subtraction
1116 * of two 2D points 30 pixels apart (that's the last factor in the formula) after
1117 * projecting them with #ED_view3d_win_to_delta and then get the length of that vector. */
1119 zfac = len_v3(t->persinv[0]) * 2.0f / t->region->winx * zfac * 30.0f;
1120
1121 for (i = 0; i < 3; i++) {
1122 float axis[3], axis_2d[2];
1123
1124 copy_v3_v3(axis, t->spacemtx[i]);
1125
1126 mul_v3_fl(axis, zfac);
1127 /* Now we can project to get window coordinate. */
1128 add_v3_v3(axis, t->center_global);
1129 projectFloatView(t, axis, axis_2d);
1130
1131 sub_v2_v2v2(axis, axis_2d, t->center2d);
1132 axis[2] = 0.0f;
1133
1134 if (normalize_v3(axis) > 1e-3f) {
1135 project_v3_v3v3(proj, mvec, axis);
1136 sub_v3_v3v3(axis, mvec, proj);
1137 len[i] = normalize_v3(axis);
1138 }
1139 else {
1140 len[i] = 1e10f;
1141 }
1142 }
1143
1144 if (len[0] <= len[1] && len[0] <= len[2]) {
1146 t->con.mode |= (CON_AXIS1 | CON_AXIS2);
1147 SNPRINTF_UTF8(t->con.text, IFACE_(" locking %s X axis"), t->spacename);
1148 }
1149 else {
1150 t->con.mode |= CON_AXIS0;
1151 SNPRINTF_UTF8(t->con.text, IFACE_(" along %s X axis"), t->spacename);
1152 }
1153 }
1154 else if (len[1] <= len[0] && len[1] <= len[2]) {
1156 t->con.mode |= (CON_AXIS0 | CON_AXIS2);
1157 SNPRINTF_UTF8(t->con.text, IFACE_(" locking %s Y axis"), t->spacename);
1158 }
1159 else {
1160 t->con.mode |= CON_AXIS1;
1161 SNPRINTF_UTF8(t->con.text, IFACE_(" along %s Y axis"), t->spacename);
1162 }
1163 }
1164 else if (len[2] <= len[1] && len[2] <= len[0]) {
1166 t->con.mode |= (CON_AXIS0 | CON_AXIS1);
1167 SNPRINTF_UTF8(t->con.text, IFACE_(" locking %s Z axis"), t->spacename);
1168 }
1169 else {
1170 t->con.mode |= CON_AXIS2;
1171 SNPRINTF_UTF8(t->con.text, IFACE_(" along %s Z axis"), t->spacename);
1172 }
1173 }
1174}
1175
1177{
1178 eTConstraint mode_prev = t->con.mode;
1179
1180 /* Constraint setting - depends on spacetype. */
1181 if (t->spacetype == SPACE_VIEW3D) {
1182 /* 3d-view. */
1184 }
1185 else {
1186 /* Assume that this means a 2D-Editor. */
1188 }
1189
1190 if (mode_prev != t->con.mode) {
1193 }
1194}
1195
1197
1198/* -------------------------------------------------------------------- */
1201
1203{
1204 if ((t->con.mode & CON_APPLY) == 0) {
1205 return -1;
1206 }
1207 switch (int(t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2))) {
1208 case (CON_AXIS0):
1209 case (CON_AXIS1 | CON_AXIS2):
1210 return 0;
1211 case (CON_AXIS1):
1212 case (CON_AXIS0 | CON_AXIS2):
1213 return 1;
1214 case (CON_AXIS2):
1215 case (CON_AXIS0 | CON_AXIS1):
1216 return 2;
1217 default:
1218 return -1;
1219 }
1220}
1221
1223{
1224 int mode = t->con.mode;
1225
1226 if ((mode & (CON_AXIS0 | CON_AXIS1)) == (CON_AXIS0 | CON_AXIS1)) {
1227 return true;
1228 }
1229
1230 if ((mode & (CON_AXIS1 | CON_AXIS2)) == (CON_AXIS1 | CON_AXIS2)) {
1231 return true;
1232 }
1233
1234 if ((mode & (CON_AXIS0 | CON_AXIS2)) == (CON_AXIS0 | CON_AXIS2)) {
1235 return true;
1236 }
1237
1238 return false;
1239}
1240
1242{
1243 int n = 0;
1244
1245 if (t->con.mode & CON_AXIS0) {
1246 n++;
1247 }
1248
1249 if (t->con.mode & CON_AXIS1) {
1250 n++;
1251 }
1252
1253 if (t->con.mode & CON_AXIS2) {
1254 n++;
1255 }
1256
1257 return n;
1258 /* Someone willing to do it cryptically could do the following instead:
1259 *
1260 * `return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2);`
1261 *
1262 * Based on the assumptions that the axis flags are one after the other and start at 1
1263 */
1264}
1265
1267
1268} // 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_UTF8(dst, format,...)
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
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
void immEnd()
void immUniform2fv(const char *name, const float data[2])
void immUnbindProgram()
void immBindBuiltinProgram(GPUBuiltinShader shader_id)
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 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
GPUDepthTest
Definition GPU_state.hh:110
@ GPU_DEPTH_LESS_EQUAL
Definition GPU_state.hh:114
@ GPU_DEPTH_NONE
Definition GPU_state.hh:111
@ 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_depth_test(GPUDepthTest test)
Definition gpu_state.cc:68
GPUDepthTest GPU_depth_test_get()
Definition gpu_state.cc:244
void GPU_viewport_size_get_f(float coords[4])
Definition gpu_state.cc:273
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
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
nullptr float
CCL_NAMESPACE_BEGIN struct Options options
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
uint pos
uint col
#define in
#define out
#define abs
static void constraint_plane_normal_calc(const TransInfo *t, float r_plane_no[3])
static void applyAxisConstraintRot(const TransInfo *t, const TransDataContainer *, const TransData *td, float r_axis[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)
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 view_vector_calc(const TransInfo *t, const float focus[3], float r_vec[3])
static const float(* transform_object_axismtx_get(const TransInfo *t, const TransDataContainer *tc, const TransData *td))[3]
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 applyObjectConstraintRot(const TransInfo *t, const TransDataContainer *tc, const TransData *td, float r_axis[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])
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 constraints_rotation_impl(const TransInfo *t, const float axismtx[3][3], float r_axis[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
#define fabsf
void * regiondata
short idx_max
float viewmat[4][4]
void(* applyRot)(const TransInfo *t, const TransDataContainer *tc, const TransData *td, float r_axis[3])
Definition transform.hh:596
void(* applySize)(const TransInfo *t, const TransDataContainer *tc, const TransData *td, float r_smat[3][3])
Definition transform.hh:591
void(* applyVec)(const TransInfo *t, const TransDataContainer *tc, const TransData *td, const float in[3], float r_out[3])
Definition transform.hh:585
void(* drawExtra)(TransInfo *t)
Definition transform.hh:577
struct blender::ed::transform::TransInfo::@342160341045112220003361360177273117120157367076 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