Blender V5.0
transform_orientations.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cctype>
10#include <cstddef>
11#include <cstring>
12
13#include "MEM_guardedalloc.h"
14
15#include "DNA_armature_types.h"
16#include "DNA_curve_types.h"
17#include "DNA_meta_types.h"
18#include "DNA_object_types.h"
19#include "DNA_scene_types.h"
20#include "DNA_screen_types.h"
21#include "DNA_sequence_types.h"
22#include "DNA_space_types.h"
23#include "DNA_view3d_types.h"
24
25#include "BLI_listbase.h"
26#include "BLI_math_geom.h"
27#include "BLI_math_matrix.h"
28#include "BLI_math_rotation.h"
29#include "BLI_math_vector.h"
30#include "BLI_string_utf8.h"
31#include "BLI_string_utils.hh"
32#include "BLI_utildefines.h"
33
34#include "BKE_action.hh"
35#include "BKE_armature.hh"
36#include "BKE_context.hh"
37#include "BKE_curve.hh"
38#include "BKE_editmesh.hh"
39#include "BKE_layer.hh"
40#include "BKE_report.hh"
41#include "BKE_scene.hh"
42
43#include "BLT_translation.hh"
44
45#include "ED_armature.hh"
46
47#include "ANIM_armature.hh"
49
50#include "SEQ_select.hh"
51
52#include "SEQ_transform.hh"
53#include "transform.hh"
55
56namespace blender::ed::transform {
57
58/* *********************** TransSpace ************************** */
59
61{
62 Scene *scene = CTX_data_scene(C);
63 ListBase *transform_orientations = &scene->transform_spaces;
64
65 BLI_freelistN(transform_orientations);
66
67 for (int i = 0; i < ARRAY_SIZE(scene->orientation_slots); i++) {
68 TransformOrientationSlot *orient_slot = &scene->orientation_slots[i];
69 if (orient_slot->type == V3D_ORIENT_CUSTOM) {
70 orient_slot->type = V3D_ORIENT_GLOBAL; /* Fallback to global. */
71 orient_slot->index_custom = -1;
72 }
73 }
74}
75
77{
78 return static_cast<TransformOrientation *>(
80}
81
82static void uniqueOrientationName(ListBase *lb, char *name)
83{
85 [&](const StringRefNull check_name) {
86 return findOrientationName(lb, check_name.c_str()) != nullptr;
87 },
89 '.',
90 name,
92}
93
95 ReportList * /*reports*/,
96 const char *name,
97 const bool overwrite)
98{
100 float mat[3][3];
101
102 if (!rv3d) {
103 return nullptr;
104 }
105
106 copy_m3_m4(mat, rv3d->viewinv);
107 normalize_m3(mat);
108
109 if (name[0] == 0) {
110 View3D *v3d = CTX_wm_view3d(C);
111 if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
112 /* If an object is used as camera, then this space is the same as object space! */
113 name = v3d->camera->id.name + 2;
114 }
115 else {
116 name = DATA_("Custom View");
117 }
118 }
119
120 return addMatrixSpace(C, mat, name, overwrite);
121}
122
124 ReportList * /*reports*/,
125 const char *name,
126 const bool overwrite)
127{
128 Base *base = CTX_data_active_base(C);
129 Object *ob;
130 float mat[3][3];
131
132 if (base == nullptr) {
133 return nullptr;
134 }
135
136 ob = base->object;
137
138 copy_m3_m4(mat, ob->object_to_world().ptr());
139 normalize_m3(mat);
140
141 /* Use object name if no name is given. */
142 if (name[0] == 0) {
143 name = ob->id.name + 2;
144 }
145
146 return addMatrixSpace(C, mat, name, overwrite);
147}
148
150 ReportList *reports,
151 const char *name,
152 const bool overwrite)
153{
154 float mat[3][3];
155 float normal[3], plane[3];
156
157 getTransformOrientation(C, normal, plane);
158
159 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
160 BKE_reports_prepend(reports, "Cannot use zero-length bone");
161 return nullptr;
162 }
163
164 if (name[0] == 0) {
165 name = DATA_("Bone");
166 }
167
168 return addMatrixSpace(C, mat, name, overwrite);
169}
170
172 ReportList *reports,
173 const char *name,
174 const bool overwrite)
175{
176 float mat[3][3];
177 float normal[3], plane[3];
178
179 getTransformOrientation(C, normal, plane);
180
181 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
182 BKE_reports_prepend(reports, "Cannot use zero-length curve");
183 return nullptr;
184 }
185
186 if (name[0] == 0) {
187 name = DATA_("Curve");
188 }
189
190 return addMatrixSpace(C, mat, name, overwrite);
191}
192
194 ReportList *reports,
195 const char *name,
196 const bool overwrite)
197{
198 float mat[3][3];
199 float normal[3], plane[3];
200 int type;
201
202 type = getTransformOrientation(C, normal, plane);
203
204 switch (type) {
205 case ORIENTATION_VERT:
206 if (createSpaceNormal(mat, normal) == 0) {
207 BKE_reports_prepend(reports, "Cannot use vertex with zero-length normal");
208 return nullptr;
209 }
210
211 if (name[0] == 0) {
212 name = DATA_("Vertex");
213 }
214 break;
215 case ORIENTATION_EDGE:
216 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
217 BKE_reports_prepend(reports, "Cannot use zero-length edge");
218 return nullptr;
219 }
220
221 if (name[0] == 0) {
222 name = DATA_("Edge");
223 }
224 break;
225 case ORIENTATION_FACE:
226 if (createSpaceNormalTangent(mat, normal, plane) == 0) {
227 BKE_reports_prepend(reports, "Cannot use zero-area face");
228 return nullptr;
229 }
230
231 if (name[0] == 0) {
232 name = DATA_("Face");
233 }
234 break;
235 default:
236 return nullptr;
237 }
238
239 return addMatrixSpace(C, mat, name, overwrite);
240}
241
242static bool test_rotmode_euler(short rotmode)
243{
244 return ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT) ? false : true;
245}
246
250static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle)
251{
252 /* X/Y are arbitrary axes, most importantly Z is the axis of rotation. */
253
254 float cross_vec[3];
255 float quat[4];
256
257 /* This is an un-scientific method to get a vector to cross with XYZ intentionally YZX. */
258 cross_vec[0] = axis[1];
259 cross_vec[1] = axis[2];
260 cross_vec[2] = axis[0];
261
262 /* X-axis. */
263 cross_v3_v3v3(gmat[0], cross_vec, axis);
264 normalize_v3(gmat[0]);
265 axis_angle_to_quat(quat, axis, angle);
266 mul_qt_v3(quat, gmat[0]);
267
268 /* Y-axis. */
269 axis_angle_to_quat(quat, axis, M_PI_2);
270 copy_v3_v3(gmat[1], gmat[0]);
271 mul_qt_v3(quat, gmat[1]);
272
273 /* Z-axis. */
274 copy_v3_v3(gmat[2], axis);
275
276 normalize_m3(gmat);
277}
278
279bool gimbal_axis_pose(Object *ob, const bPoseChannel *pchan, float gmat[3][3])
280{
281 float mat[3][3], tmat[3][3], obmat[3][3];
282 if (test_rotmode_euler(pchan->rotmode)) {
283 eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode);
284 }
285 else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
286 axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle);
287 }
288 else { /* Quaternion. */
289 return false;
290 }
291
292 /* Apply bone transformation. */
293 mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat);
294
295 if (pchan->parent) {
296 float parent_mat[3][3];
297
298 copy_m3_m4(parent_mat,
299 (pchan->bone->flag & BONE_HINGE) ? pchan->parent->bone->arm_mat :
300 pchan->parent->pose_mat);
301 mul_m3_m3m3(mat, parent_mat, tmat);
302
303 /* Needed if object transformation isn't identity. */
304 copy_m3_m4(obmat, ob->object_to_world().ptr());
305 mul_m3_m3m3(gmat, obmat, mat);
306 }
307 else {
308 /* Needed if object transformation isn't identity. */
309 copy_m3_m4(obmat, ob->object_to_world().ptr());
310 mul_m3_m3m3(gmat, obmat, tmat);
311 }
312
313 normalize_m3(gmat);
314 return true;
315}
316
317bool gimbal_axis_object(Object *ob, float gmat[3][3])
318{
319 if (test_rotmode_euler(ob->rotmode)) {
320 eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode);
321 }
322 else if (ob->rotmode == ROT_MODE_AXISANGLE) {
324 }
325 else { /* Quaternion. */
326 return false;
327 }
328
329 if (ob->parent) {
330 float parent_mat[3][3];
331 copy_m3_m4(parent_mat, ob->parent->object_to_world().ptr());
332 normalize_m3(parent_mat);
333 mul_m3_m3m3(gmat, parent_mat, gmat);
334 }
335 return true;
336}
337
339 const float x[3],
340 const float y[3],
341 const float z[3])
342{
343 bool is_zero[3] = {true, true, true};
344 zero_m3(mat);
345 if (x) {
346 is_zero[0] = normalize_v3_v3(mat[0], x) == 0.0f;
347 }
348 if (y) {
349 is_zero[1] = normalize_v3_v3(mat[1], y) == 0.0f;
350 }
351 if (z) {
352 is_zero[2] = normalize_v3_v3(mat[2], z) == 0.0f;
353 }
354
355 int zero_axis = is_zero[0] + is_zero[1] + is_zero[2];
356 if (zero_axis == 0) {
357 return true;
358 }
359
360 if (zero_axis == 1) {
361 int axis = is_zero[0] ? 0 : is_zero[1] ? 1 : 2;
362 cross_v3_v3v3(mat[axis], mat[(axis + 1) % 3], mat[(axis + 2) % 3]);
363 if (normalize_v3(mat[axis]) != 0.0f) {
364 return true;
365 }
366 }
367 else if (zero_axis == 2) {
368 int axis, a, b;
369 axis = !is_zero[0] ? 0 : !is_zero[1] ? 1 : 2;
370 a = (axis + 1) % 3;
371 b = (axis + 2) % 3;
372
373 mat[a][a] = 1.0f;
374 mat[b][b] = 1.0f;
375 project_plane_v3_v3v3(mat[a], mat[a], mat[axis]);
376 project_plane_v3_v3v3(mat[b], mat[b], mat[axis]);
377 if ((normalize_v3(mat[a]) != 0.0f) && (normalize_v3(mat[b]) != 0.0f)) {
378 return true;
379 }
380 }
381
382 unit_m3(mat);
383 return false;
384}
385
386bool createSpaceNormal(float mat[3][3], const float normal[3])
387{
388 float tangent[3] = {0.0f, 0.0f, 1.0f};
389
390 copy_v3_v3(mat[2], normal);
391 if (normalize_v3(mat[2]) == 0.0f) {
392 return false; /* Error return. */
393 }
394
395 cross_v3_v3v3(mat[0], mat[2], tangent);
396 if (is_zero_v3(mat[0])) {
397 tangent[0] = 1.0f;
398 tangent[1] = tangent[2] = 0.0f;
399 cross_v3_v3v3(mat[0], tangent, mat[2]);
400 }
401
402 cross_v3_v3v3(mat[1], mat[2], mat[0]);
403
404 normalize_m3(mat);
405
406 return true;
407}
408
409bool createSpaceNormalTangent(float mat[3][3], const float normal[3], const float tangent[3])
410{
411 BLI_ASSERT_UNIT_V3(normal);
412 BLI_ASSERT_UNIT_V3(tangent);
413
414 if (UNLIKELY(is_zero_v3(normal))) {
415 /* Error return. */
416 return false;
417 }
418 copy_v3_v3(mat[2], normal);
419
420 /* Negate so we can use values from the matrix as input. */
421 negate_v3_v3(mat[1], tangent);
422
423 /* Preempt zero length tangent from causing trouble. */
424 if (UNLIKELY(is_zero_v3(mat[1]))) {
425 mat[1][2] = 1.0f;
426 }
427
428 cross_v3_v3v3(mat[0], mat[2], mat[1]);
429 if (UNLIKELY(normalize_v3(mat[0]) == 0.0f)) {
430 /* Error return from co-linear normal & tangent. */
431 return false;
432 }
433
434 /* Make the tangent orthogonal. */
435 cross_v3_v3v3(mat[1], mat[2], mat[0]);
436
437 if (UNLIKELY(normalize_v3(mat[1]) == 0.0f)) {
438 /* Error return as it's possible making the tangent orthogonal to the normal
439 * causes it to be zero length. */
440 return false;
441 }
442
443 /* Final matrix must be normalized, do inline. */
444 // normalize_m3(mat);
445
446 return true;
447}
448
450 const float normal[3],
451 const float tangent[3])
452{
453 if (createSpaceNormalTangent(mat, normal, tangent)) {
454 return;
455 }
456 if (!is_zero_v3(normal)) {
457 axis_dominant_v3_to_m3(mat, normal);
458 invert_m3(mat);
459 return;
460 }
461 /* Last resort. */
462 unit_m3(mat);
463}
464
466 ReportList *reports,
467 const char *name,
468 const bool use_view,
469 const bool activate,
470 const bool overwrite)
471{
472 TransformOrientation *ts = nullptr;
473
474 if (use_view) {
475 ts = createViewSpace(C, reports, name, overwrite);
476 }
477 else {
478 Object *obedit = CTX_data_edit_object(C);
480 if (obedit) {
481 if (obedit->type == OB_MESH) {
482 ts = createMeshSpace(C, reports, name, overwrite);
483 }
484 else if (obedit->type == OB_ARMATURE) {
485 ts = createBoneSpace(C, reports, name, overwrite);
486 }
487 else if (obedit->type == OB_CURVES_LEGACY) {
488 ts = createCurveSpace(C, reports, name, overwrite);
489 }
490 }
491 else if (ob && (ob->mode & OB_MODE_POSE)) {
492 ts = createBoneSpace(C, reports, name, overwrite);
493 }
494 else {
495 ts = createObjectSpace(C, reports, name, overwrite);
496 }
497 }
498
499 if (activate && ts != nullptr) {
501 }
502 return (ts != nullptr);
503}
504
506 float mat[3][3],
507 const char *name,
508 const bool overwrite)
509{
510 TransformOrientation *ts = nullptr;
511 Scene *scene = CTX_data_scene(C);
512 ListBase *transform_orientations = &scene->transform_spaces;
513 char name_unique[sizeof(ts->name)];
514
515 if (overwrite) {
516 ts = findOrientationName(transform_orientations, name);
517 }
518 else {
519 STRNCPY_UTF8(name_unique, name);
520 uniqueOrientationName(transform_orientations, name_unique);
521 name = name_unique;
522 }
523
524 /* If not, create a new one. */
525 if (ts == nullptr) {
526 ts = MEM_callocN<TransformOrientation>("UserTransSpace from matrix");
527 BLI_addtail(transform_orientations, ts);
528 STRNCPY_UTF8(ts->name, name);
529 }
530
531 /* Copy matrix into transform space. */
532 copy_m3_m3(ts->mat, mat);
533
534 return ts;
535}
536
541
547
558
560{
561 Scene *scene = CTX_data_scene(C);
562 ListBase *transform_orientations = &scene->transform_spaces;
563 return BLI_listbase_count(transform_orientations);
564}
565
566void applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char r_name[64])
567{
568 if (r_name) {
569 BLI_strncpy_utf8(r_name, ts->name, MAX_NAME);
570 }
571 copy_m3_m3(r_mat, ts->mat);
572}
573
575{
576 int cleared = 0;
577 animrig::pose_bone_descendent_iterator(pose, pose_bone, [&](bPoseChannel &child) {
578 if (&child == &pose_bone) {
579 return;
580 }
581 if (child.runtime.flag & POSE_RUNTIME_TRANSFORM) {
583 cleared++;
584 }
585 });
586 return cleared;
587}
588
589/* Updates all `POSE_RUNTIME_TRANSFORM` flags.
590 * Returns total number of bones with `POSE_RUNTIME_TRANSFORM`.
591 * NOTE: `transform_convert_pose_transflags_update` has a similar logic. */
593 bArmature *arm,
594 ListBase /* bPoseChannel */ *lb)
595{
596 int total = 0;
597
598 LISTBASE_FOREACH (bPoseChannel *, pchan, lb) {
599 pchan->runtime.flag &= ~POSE_RUNTIME_TRANSFORM;
600 if (!ANIM_bone_in_visible_collection(arm, pchan->bone)) {
601 continue;
602 }
603 if (pchan->flag & POSE_SELECTED) {
604 pchan->runtime.flag |= POSE_RUNTIME_TRANSFORM;
605 total++;
606 }
607 }
608
609 /* No transform on children if any parent bone is selected. */
610 LISTBASE_FOREACH (bPoseChannel *, pchan, lb) {
611 if (pchan->runtime.flag & POSE_RUNTIME_TRANSFORM) {
612 total -= bone_children_clear_transflag(*ob.pose, *pchan);
613 }
614 }
615 return total;
616}
617
618void calc_orientation_from_type(const bContext *C, float r_mat[3][3])
619{
620 ARegion *region = CTX_wm_region(C);
621 Scene *scene = CTX_data_scene(C);
622 ViewLayer *view_layer = CTX_data_view_layer(C);
623 Object *obedit = CTX_data_edit_object(C);
624 View3D *v3d = CTX_wm_view3d(C);
625 RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
626 BKE_view_layer_synced_ensure(scene, view_layer);
628 const short orient_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
629 const int pivot_point = scene->toolsettings->transform_pivot_point;
630
632 scene, view_layer, v3d, rv3d, ob, obedit, orient_index, pivot_point, r_mat);
633}
634
635static void handle_armature_parent_orientation(Object *ob, float r_mat[3][3])
636{
637 bPoseChannel *active_pchan = BKE_pose_channel_active(ob, false);
638
639 /* Check if target bone is a child. */
640 if (active_pchan && active_pchan->parent) {
641 /* For child, show parent local regardless if "local location" is set for parent bone. */
643 float ob_orientations_mat[3][3];
644 transform_orientations_create_from_axis(ob_orientations_mat,
645 UNPACK3(ob->object_to_world().ptr()));
646 mul_m3_m3_pre(r_mat, ob_orientations_mat);
647 return;
648 }
649
650 /* For root, use local transform of armature object. */
651 transform_orientations_create_from_axis(r_mat, UNPACK3(ob->object_to_world().ptr()));
652}
653
654static void handle_object_parent_orientation(Object *ob, float r_mat[3][3])
655{
656 /* If object has parent, then orient to parent. */
657 if (ob->parent) {
658 transform_orientations_create_from_axis(r_mat, UNPACK3(ob->parent->object_to_world().ptr()));
659 }
660 else {
661 /* If object doesn't have parent, then orient to world. */
662 unit_m3(r_mat);
663 }
664}
665
667 ViewLayer *view_layer,
668 const View3D *v3d,
669 const RegionView3D *rv3d,
670 Object *ob,
671 Object *obedit,
672 const short orientation_index,
673 const int pivot_point,
674 float r_mat[3][3])
675{
676 switch (orientation_index) {
677 case V3D_ORIENT_GIMBAL: {
678
679 if (ob) {
680 if (ob->mode & OB_MODE_POSE) {
682
683 if (pchan && gimbal_axis_pose(ob, pchan, r_mat)) {
684 break;
685 }
686 }
687 else {
688 if (gimbal_axis_object(ob, r_mat)) {
689 break;
690 }
691 }
692 }
693 /* If not gimbal, fall through to normal. */
695 }
696 case V3D_ORIENT_PARENT: {
697 if (ob) {
698 if (ob->mode & OB_MODE_POSE) {
700 break;
701 }
703 break;
704 }
705 /* No break; we define 'parent' as 'normal' otherwise. */
707 }
708 case V3D_ORIENT_NORMAL: {
709 if (obedit || (ob && ob->mode & OB_MODE_POSE)) {
710 ED_getTransformOrientationMatrix(scene, view_layer, v3d, ob, obedit, pivot_point, r_mat);
711 break;
712 }
713 /* No break we define 'normal' as 'local' in Object mode. */
715 }
716 case V3D_ORIENT_LOCAL: {
717 if (ob) {
718 if (ob->mode & OB_MODE_POSE) {
719 /* Each bone moves on its own local axis, but to avoid confusion,
720 * use the active bone's axis for display #33575, this works as expected on a single
721 * bone and users who select many bones will understand what's going on and what local
722 * means when they start transforming. */
723 ED_getTransformOrientationMatrix(scene, view_layer, v3d, ob, obedit, pivot_point, r_mat);
724 }
725 else {
726 transform_orientations_create_from_axis(r_mat, UNPACK3(ob->object_to_world().ptr()));
727 }
728 break;
729 }
730 /* If not local, fall through to global. */
732 }
733 case V3D_ORIENT_GLOBAL: {
734 unit_m3(r_mat);
735 break;
736 }
737 case V3D_ORIENT_VIEW: {
738 if (rv3d != nullptr) {
739 copy_m3_m4(r_mat, rv3d->viewinv);
740 normalize_m3(r_mat);
741 }
742 else {
743 unit_m3(r_mat);
744 }
745 break;
746 }
747 case V3D_ORIENT_CURSOR: {
748 copy_m3_m3(r_mat, scene->cursor.matrix<float3x3>().ptr());
749 break;
750 }
752 /* Do nothing. */;
753 break;
754 }
756 default: {
757 BLI_assert(orientation_index >= V3D_ORIENT_CUSTOM);
758 int orientation_index_custom = orientation_index - V3D_ORIENT_CUSTOM;
760 scene, orientation_index_custom);
761 applyTransformOrientation(custom_orientation, r_mat, nullptr);
762 break;
763 }
764 }
765
766 return orientation_index;
767}
768
770 TransInfo *t,
771 short orient_index,
772 const float custom[3][3],
773 float r_spacemtx[3][3])
774{
775 if (orient_index == V3D_ORIENT_CUSTOM_MATRIX) {
776 copy_m3_m3(r_spacemtx, custom);
778 }
779
781 Scene *scene = t->scene;
782 Strip *strip = seq::select_active_get(scene);
783 if (strip && strip->data->transform && orient_index == V3D_ORIENT_LOCAL) {
786 r_spacemtx, 'Z', strip->data->transform->rotation * mirror[0] * mirror[1]);
787 return orient_index;
788 }
789 }
790
792 Object *obedit = CTX_data_edit_object(C);
793 Scene *scene = t->scene;
794 View3D *v3d = nullptr;
795 RegionView3D *rv3d = nullptr;
796
797 if ((t->spacetype == SPACE_VIEW3D) && t->region && (t->region->regiontype == RGN_TYPE_WINDOW)) {
798 v3d = static_cast<View3D *>(t->view);
799 rv3d = static_cast<RegionView3D *>(t->region->regiondata);
800
801 if (ob && (ob->mode & OB_MODE_ALL_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) {
803 if (ob_armature) {
804 /* The armature matrix is used for GIMBAL, NORMAL and LOCAL orientations. */
805 ob = ob_armature;
806 }
807 }
808 }
809
810 const short orient_index_result = calc_orientation_from_type_ex(
811 scene, t->view_layer, v3d, rv3d, ob, obedit, orient_index, t->around, r_spacemtx);
812
813 if (rv3d && (t->options & CTX_PAINT_CURVE)) {
814 /* Screen space in the 3d region. */
815 if (orient_index_result == V3D_ORIENT_VIEW) {
816 unit_m3(r_spacemtx);
817 }
818 else {
819 mul_m3_m4m3(r_spacemtx, rv3d->viewmat, r_spacemtx);
820 normalize_m3(r_spacemtx);
821 }
822 }
823
824 return orient_index_result;
825}
826
827const char *transform_orientations_spacename_get(TransInfo *t, const short orient_type)
828{
829 switch (orient_type) {
831 return RPT_("global");
833 return RPT_("gimbal");
835 return RPT_("normal");
836 case V3D_ORIENT_LOCAL:
837 return RPT_("local");
838 case V3D_ORIENT_VIEW:
839 return RPT_("view");
841 return RPT_("cursor");
843 return RPT_("parent");
845 return RPT_("custom");
847 default:
848 BLI_assert(orient_type >= V3D_ORIENT_CUSTOM);
850 t->scene, orient_type - V3D_ORIENT_CUSTOM);
851 return ts->name;
852 }
853}
854
855void transform_orientations_current_set(TransInfo *t, const short orient_index)
856{
857 const short orientation = t->orient[orient_index].type;
858 const char *spacename = transform_orientations_spacename_get(t, orientation);
859
860 STRNCPY_UTF8(t->spacename, spacename);
861 copy_m3_m3(t->spacemtx, t->orient[orient_index].matrix);
863 t->orient_curr = eTOType(orient_index);
864}
865
870 BMesh *bm, BMElem **elems, const uint n, const BMIterType itype, const char htype)
871{
872 BMIter iter;
873 BMElem *ele;
874 uint i;
875
878
879 if (!BLI_listbase_is_empty(&bm->selected)) {
880 /* Quick check. */
881 i = 0;
882 LISTBASE_FOREACH_BACKWARD (BMEditSelection *, ese, &bm->selected) {
883 /* Shouldn't need this check. */
884 if (BM_elem_flag_test(ese->ele, BM_ELEM_SELECT)) {
885
886 /* Only use contiguous selection. */
887 if (ese->htype != htype) {
888 i = 0;
889 break;
890 }
891
892 elems[i++] = ese->ele;
893 if (n == i) {
894 break;
895 }
896 }
897 else {
898 BLI_assert(0);
899 }
900 }
901
902 if (i == 0) {
903 /* Pass. */
904 }
905 else if (i == n) {
906 return i;
907 }
908 }
909
910 i = 0;
911 BM_ITER_MESH (ele, &iter, bm, itype) {
912 BLI_assert(ele->head.htype == htype);
914 elems[i++] = ele;
915 if (n == i) {
916 break;
917 }
918 }
919 }
920
921 return i;
922}
923
925{
927 bm, (BMElem **)elems, min_ii(n, bm->totvertsel), BM_VERTS_OF_MESH, BM_VERT);
928}
930{
932 bm, (BMElem **)elems, min_ii(n, bm->totedgesel), BM_EDGES_OF_MESH, BM_EDGE);
933}
934#if 0
935static uint bm_mesh_faces_select_get_n(BMesh *bm, BMVert **elems, const uint n)
936{
938 bm, (BMElem **)elems, min_ii(n, bm->totfacesel), BM_FACES_OF_MESH, BM_FACE);
939}
940#endif
941
943 ViewLayer *view_layer,
944 const View3D *v3d,
945 Object *ob,
946 Object *obedit,
947 const short around,
948 float r_normal[3],
949 float r_plane[3])
950{
952 const bool activeOnly = (around == V3D_AROUND_ACTIVE);
953
954 zero_v3(r_normal);
955 zero_v3(r_plane);
956
957 if (obedit) {
958 float imat[3][3], mat[3][3];
959
960 /* We need the transpose of the inverse for a normal... */
961 copy_m3_m4(imat, ob->object_to_world().ptr());
962
963 invert_m3_m3(mat, imat);
964 transpose_m3(mat);
965
966 ob = obedit;
967
968 if (ob->type == OB_MESH) {
970 BMEditSelection ese;
971
972 /* Use last selected with active. */
973 if (activeOnly && BM_select_history_active_get(em->bm, &ese)) {
974 BM_editselection_normal(&ese, r_normal);
975 BM_editselection_plane(&ese, r_plane);
976
977 switch (ese.htype) {
978 case BM_VERT:
980 break;
981 case BM_EDGE:
983 break;
984 case BM_FACE:
986 break;
987 }
988 }
989 else {
990 if (em->bm->totfacesel >= 1) {
991 BMFace *efa;
992 BMIter iter;
993
994 float normal[3] = {0.0f};
995 float plane_pair[2][3] = {{0.0f}};
996 int face_count = 0;
997
998 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1000 float tangent_pair[2][3];
1001 BM_face_calc_tangent_pair_auto(efa, tangent_pair[0], tangent_pair[1]);
1002 add_v3_v3(normal, efa->no);
1003 add_v3_v3(plane_pair[0], tangent_pair[0]);
1004 add_v3_v3(plane_pair[1], tangent_pair[1]);
1005 face_count++;
1006 }
1007 }
1008
1009 /* Pick the best plane (least likely to be co-linear),
1010 * since this can result in failure to construct a usable matrix, see: #96535. */
1011 int plane_index;
1012 if (face_count == 1) {
1013 /* Special case so a single face always matches
1014 * the active-element orientation, see: #134948. */
1015 plane_index = 0;
1016 }
1017 else {
1018 float normal_unit[3];
1019 float plane_unit_pair[2][3], plane_ortho_pair[2][3];
1020
1021 normalize_v3_v3(normal_unit, normal);
1022 normalize_v3_v3(plane_unit_pair[0], plane_pair[0]);
1023 normalize_v3_v3(plane_unit_pair[1], plane_pair[1]);
1024
1025 cross_v3_v3v3(plane_ortho_pair[0], normal_unit, plane_unit_pair[0]);
1026 cross_v3_v3v3(plane_ortho_pair[1], normal_unit, plane_unit_pair[1]);
1027
1028 plane_index = (len_squared_v3(plane_ortho_pair[0]) >
1029 len_squared_v3(plane_ortho_pair[1])) ?
1030 0 :
1031 1;
1032 }
1033
1034 add_v3_v3(r_normal, normal);
1035 add_v3_v3(r_plane, plane_pair[plane_index]);
1036
1038 }
1039 else if (em->bm->totvertsel == 3) {
1040 BMVert *v_tri[3];
1041
1042 if (bm_mesh_verts_select_get_n(em->bm, v_tri, 3) == 3) {
1043 BMEdge *e = nullptr;
1044 float no_test[3];
1045
1046 normal_tri_v3(r_normal, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co);
1047
1048 /* Check if the normal is pointing opposite to vert normals. */
1049 no_test[0] = v_tri[0]->no[0] + v_tri[1]->no[0] + v_tri[2]->no[0];
1050 no_test[1] = v_tri[0]->no[1] + v_tri[1]->no[1] + v_tri[2]->no[1];
1051 no_test[2] = v_tri[0]->no[2] + v_tri[1]->no[2] + v_tri[2]->no[2];
1052 if (dot_v3v3(no_test, r_normal) < 0.0f) {
1053 negate_v3(r_normal);
1054 }
1055
1056 if (em->bm->totedgesel >= 1) {
1057 /* Find an edge that's a part of v_tri (no need to search all edges). */
1058 float e_length;
1059 int j;
1060
1061 for (j = 0; j < 3; j++) {
1062 BMEdge *e_test = BM_edge_exists(v_tri[j], v_tri[(j + 1) % 3]);
1063 if (e_test && BM_elem_flag_test(e_test, BM_ELEM_SELECT)) {
1064 const float e_test_length = BM_edge_calc_length_squared(e_test);
1065 if ((e == nullptr) || (e_length < e_test_length)) {
1066 e = e_test;
1067 e_length = e_test_length;
1068 }
1069 }
1070 }
1071 }
1072
1073 if (e) {
1074 BMVert *v_pair[2];
1075 if (BM_edge_is_boundary(e)) {
1076 BM_edge_ordered_verts(e, &v_pair[0], &v_pair[1]);
1077 }
1078 else {
1079 v_pair[0] = e->v1;
1080 v_pair[1] = e->v2;
1081 }
1082 sub_v3_v3v3(r_plane, v_pair[0]->co, v_pair[1]->co);
1083 }
1084 else {
1085 BM_vert_tri_calc_tangent_from_edge(v_tri, r_plane);
1086 }
1087 }
1088 else {
1089 BLI_assert(0);
1090 }
1091
1093 }
1094 else if (em->bm->totedgesel == 1 || em->bm->totvertsel == 2) {
1095 BMVert *v_pair[2] = {nullptr, nullptr};
1096 BMEdge *eed = nullptr;
1097
1098 if (em->bm->totedgesel == 1) {
1099 if (bm_mesh_edges_select_get_n(em->bm, &eed, 1) == 1) {
1100 v_pair[0] = eed->v1;
1101 v_pair[1] = eed->v2;
1102 }
1103 }
1104 else {
1105 BLI_assert(em->bm->totvertsel == 2);
1106 bm_mesh_verts_select_get_n(em->bm, v_pair, 2);
1107 }
1108
1109 /* Should never fail. */
1110 if (LIKELY(v_pair[0] && v_pair[1])) {
1111 bool v_pair_swap = false;
1122
1123 /* Be deterministic where possible and ensure `v_pair[0]` is active. */
1124 if (BM_mesh_active_vert_get(em->bm) == v_pair[1]) {
1125 v_pair_swap = true;
1126 }
1127 else if (eed && BM_edge_is_boundary(eed)) {
1128 /* Predictable direction for boundary edges. */
1129 if (eed->l->v != v_pair[0]) {
1130 v_pair_swap = true;
1131 }
1132 }
1133
1134 if (v_pair_swap) {
1135 std::swap(v_pair[0], v_pair[1]);
1136 }
1137
1138 add_v3_v3v3(r_normal, v_pair[1]->no, v_pair[0]->no);
1139 sub_v3_v3v3(r_plane, v_pair[1]->co, v_pair[0]->co);
1140
1141 if (normalize_v3(r_plane) != 0.0f) {
1142 /* For edges it'd important the resulting matrix can rotate around the edge,
1143 * project onto the plane so we can use a fallback value. */
1144 project_plane_normalized_v3_v3v3(r_normal, r_normal, r_plane);
1145 if (UNLIKELY(normalize_v3(r_normal) == 0.0f)) {
1146 /* In the case the normal and plane are aligned,
1147 * use a fallback normal which is orthogonal to the plane. */
1148 ortho_v3_v3(r_normal, r_plane);
1149 }
1150 }
1151 }
1152
1154 }
1155 else if (em->bm->totvertsel == 1) {
1156 BMVert *v = nullptr;
1157
1158 if (bm_mesh_verts_select_get_n(em->bm, &v, 1) == 1) {
1159 copy_v3_v3(r_normal, v->no);
1160 BMEdge *e_pair[2];
1161
1162 if (BM_vert_edge_pair(v, &e_pair[0], &e_pair[1])) {
1163 bool v_pair_swap = false;
1164 BMVert *v_pair[2] = {
1165 BM_edge_other_vert(e_pair[0], v),
1166 BM_edge_other_vert(e_pair[1], v),
1167 };
1168 float dir_pair[2][3];
1169
1170 if (BM_edge_is_boundary(e_pair[0])) {
1171 if (e_pair[0]->l->v != v) {
1172 v_pair_swap = true;
1173 }
1174 }
1175 else {
1176 if (BM_edge_calc_length_squared(e_pair[0]) <
1177 BM_edge_calc_length_squared(e_pair[1]))
1178 {
1179 v_pair_swap = true;
1180 }
1181 }
1182
1183 if (v_pair_swap) {
1184 std::swap(v_pair[0], v_pair[1]);
1185 }
1186
1187 sub_v3_v3v3(dir_pair[0], v->co, v_pair[0]->co);
1188 sub_v3_v3v3(dir_pair[1], v_pair[1]->co, v->co);
1189 normalize_v3(dir_pair[0]);
1190 normalize_v3(dir_pair[1]);
1191
1192 add_v3_v3v3(r_plane, dir_pair[0], dir_pair[1]);
1193 }
1194 }
1195
1197 }
1198 else if (em->bm->totvertsel > 3) {
1199 BMIter iter;
1200 BMVert *v;
1201
1202 zero_v3(r_normal);
1203
1204 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
1206 add_v3_v3(r_normal, v->no);
1207 }
1208 }
1209 normalize_v3(r_normal);
1211 }
1212 }
1213
1214 /* Not needed but this matches 2.68 and older behavior. */
1215 negate_v3(r_plane);
1216
1217 } /* End edit-mesh. */
1218 else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
1219 Curve *cu = static_cast<Curve *>(obedit->data);
1220 Nurb *nu = nullptr;
1221 int a;
1222 ListBase *nurbs = BKE_curve_editNurbs_get(cu);
1223
1224 void *vert_act = nullptr;
1225 if (activeOnly && BKE_curve_nurb_vert_active_get(cu, &nu, &vert_act)) {
1226 if (nu->type == CU_BEZIER) {
1227 BezTriple *bezt = static_cast<BezTriple *>(vert_act);
1228 BKE_nurb_bezt_calc_normal(nu, bezt, r_normal);
1229 BKE_nurb_bezt_calc_plane(nu, bezt, r_plane);
1230 }
1231 else {
1232 BPoint *bp = static_cast<BPoint *>(vert_act);
1233 BKE_nurb_bpoint_calc_normal(nu, bp, r_normal);
1234 BKE_nurb_bpoint_calc_plane(nu, bp, r_plane);
1235 }
1236 }
1237 else {
1238 const bool use_handle = v3d ? (v3d->overlay.handle_display != CURVE_HANDLE_NONE) : true;
1239
1240 for (nu = static_cast<Nurb *>(nurbs->first); nu; nu = nu->next) {
1241 /* Only bezier has a normal. */
1242 if (nu->type == CU_BEZIER) {
1243 BezTriple *bezt = nu->bezt;
1244 a = nu->pntsu;
1245 while (a--) {
1246 short flag = 0;
1247
1248#define SEL_F1 (1 << 0)
1249#define SEL_F2 (1 << 1)
1250#define SEL_F3 (1 << 2)
1251
1252 if (use_handle) {
1253 if (bezt->f1 & SELECT) {
1254 flag |= SEL_F1;
1255 }
1256 if (bezt->f2 & SELECT) {
1257 flag |= SEL_F2;
1258 }
1259 if (bezt->f3 & SELECT) {
1260 flag |= SEL_F3;
1261 }
1262 }
1263 else {
1264 flag = (bezt->f2 & SELECT) ? (SEL_F1 | SEL_F2 | SEL_F3) : 0;
1265 }
1266
1267 /* Exception. */
1268 if (flag) {
1269 float tvec[3];
1270 if ((around == V3D_AROUND_LOCAL_ORIGINS) ||
1272 {
1273 BKE_nurb_bezt_calc_normal(nu, bezt, tvec);
1274 add_v3_v3(r_normal, tvec);
1275 }
1276 else {
1277 /* Ignore `bezt->f2` in this case. */
1278 if (flag & SEL_F1) {
1279 sub_v3_v3v3(tvec, bezt->vec[0], bezt->vec[1]);
1280 normalize_v3(tvec);
1281 add_v3_v3(r_normal, tvec);
1282 }
1283 if (flag & SEL_F3) {
1284 sub_v3_v3v3(tvec, bezt->vec[1], bezt->vec[2]);
1285 normalize_v3(tvec);
1286 add_v3_v3(r_normal, tvec);
1287 }
1288 }
1289
1290 BKE_nurb_bezt_calc_plane(nu, bezt, tvec);
1291 add_v3_v3(r_plane, tvec);
1292 }
1293
1294#undef SEL_F1
1295#undef SEL_F2
1296#undef SEL_F3
1297
1298 bezt++;
1299 }
1300 }
1301 else if (nu->bp && (nu->pntsv == 1)) {
1302 BPoint *bp = nu->bp;
1303 a = nu->pntsu;
1304 while (a--) {
1305 if (bp->f1 & SELECT) {
1306 float tvec[3];
1307
1308 BPoint *bp_prev = BKE_nurb_bpoint_get_prev(nu, bp);
1309 BPoint *bp_next = BKE_nurb_bpoint_get_next(nu, bp);
1310
1311 const bool is_prev_sel = bp_prev && (bp_prev->f1 & SELECT);
1312 const bool is_next_sel = bp_next && (bp_next->f1 & SELECT);
1313 if (is_prev_sel == false && is_next_sel == false) {
1314 /* Isolated, add based on surrounding. */
1315 BKE_nurb_bpoint_calc_normal(nu, bp, tvec);
1316 add_v3_v3(r_normal, tvec);
1317 }
1318 else if (is_next_sel) {
1319 /* A segment, add the edge normal. */
1320 sub_v3_v3v3(tvec, bp->vec, bp_next->vec);
1321 normalize_v3(tvec);
1322 add_v3_v3(r_normal, tvec);
1323 }
1324
1325 BKE_nurb_bpoint_calc_plane(nu, bp, tvec);
1326 add_v3_v3(r_plane, tvec);
1327 }
1328 bp++;
1329 }
1330 }
1331 }
1332 }
1333
1334 if (!is_zero_v3(r_normal)) {
1336 }
1337 }
1338 else if (obedit->type == OB_MBALL) {
1339 MetaBall *mb = static_cast<MetaBall *>(obedit->data);
1340 MetaElem *ml;
1341 bool ok = false;
1342 float tmat[3][3];
1343
1344 if (activeOnly && (ml = mb->lastelem)) {
1345 quat_to_mat3(tmat, ml->quat);
1346 add_v3_v3(r_normal, tmat[2]);
1347 add_v3_v3(r_plane, tmat[1]);
1348 ok = true;
1349 }
1350 else {
1351 LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) {
1352 if (ml->flag & SELECT) {
1353 quat_to_mat3(tmat, ml->quat);
1354 add_v3_v3(r_normal, tmat[2]);
1355 add_v3_v3(r_plane, tmat[1]);
1356 ok = true;
1357 }
1358 }
1359 }
1360
1361 if (ok) {
1362 if (!is_zero_v3(r_plane)) {
1364 }
1365 }
1366 }
1367 else if (obedit->type == OB_ARMATURE) {
1368 bArmature *arm = static_cast<bArmature *>(obedit->data);
1369 EditBone *ebone;
1370 bool ok = false;
1371 float tmat[3][3];
1372
1373 if (activeOnly && (ebone = arm->act_edbone)) {
1374 ED_armature_ebone_to_mat3(ebone, tmat);
1375 add_v3_v3(r_normal, tmat[2]);
1376 add_v3_v3(r_plane, tmat[1]);
1377 ok = true;
1378 }
1379 else {
1380 /* When we only have the root/tip are selected. */
1381 bool fallback_ok = false;
1382 float fallback_normal[3];
1383 float fallback_plane[3];
1384
1385 zero_v3(fallback_normal);
1386 zero_v3(fallback_plane);
1387
1388 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1389 if (blender::animrig::bone_is_visible(arm, ebone)) {
1390 if (ebone->flag & BONE_SELECTED) {
1391 ED_armature_ebone_to_mat3(ebone, tmat);
1392 add_v3_v3(r_normal, tmat[2]);
1393 add_v3_v3(r_plane, tmat[1]);
1394 ok = true;
1395 }
1396 else if ((ok == false) && ((ebone->flag & BONE_TIPSEL) ||
1397 ((ebone->flag & BONE_ROOTSEL) &&
1398 (ebone->parent && ebone->flag & BONE_CONNECTED) == false)))
1399 {
1400 ED_armature_ebone_to_mat3(ebone, tmat);
1401 add_v3_v3(fallback_normal, tmat[2]);
1402 add_v3_v3(fallback_plane, tmat[1]);
1403 fallback_ok = true;
1404 }
1405 }
1406 }
1407 if ((ok == false) && fallback_ok) {
1408 ok = true;
1409 copy_v3_v3(r_normal, fallback_normal);
1410 copy_v3_v3(r_plane, fallback_plane);
1411 }
1412 }
1413
1414 if (ok) {
1415 if (!is_zero_v3(r_plane)) {
1417 }
1418 }
1419 }
1420
1421 /* Vectors from edges don't need the special transpose inverse multiplication. */
1422 if (result == ORIENTATION_EDGE) {
1423 float tvec[3];
1424
1425 mul_mat3_m4_v3(ob->object_to_world().ptr(), r_normal);
1426 mul_mat3_m4_v3(ob->object_to_world().ptr(), r_plane);
1427
1428 /* Align normal to edge direction (so normal is perpendicular to the plane).
1429 * 'ORIENTATION_EDGE' will do the other way around.
1430 * This has to be done **after** applying obmat, see #45775! */
1431 project_v3_v3v3(tvec, r_normal, r_plane);
1432 sub_v3_v3(r_normal, tvec);
1433 }
1434 else {
1435 mul_m3_v3(mat, r_normal);
1436 mul_m3_v3(mat, r_plane);
1437 }
1438 }
1439 else if (ob && (ob->mode & OB_MODE_POSE)) {
1440 bArmature *arm = static_cast<bArmature *>(ob->data);
1441 bPoseChannel *pchan;
1442 float imat[3][3], mat[3][3];
1443 bool ok = false;
1444
1445 if (activeOnly && (pchan = BKE_pose_channel_active_if_bonecoll_visible(ob))) {
1446 float pose_mat[3][3];
1447 BKE_pose_channel_transform_orientation(arm, pchan, pose_mat);
1448
1449 add_v3_v3(r_normal, pose_mat[2]);
1450 add_v3_v3(r_plane, pose_mat[1]);
1451 ok = true;
1452 }
1453 else {
1454 const int transformed_len = armature_bone_transflags_update(*ob, arm, &ob->pose->chanbase);
1455 if (transformed_len) {
1456 /* Use channels to get stats. */
1457 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
1458 if (pchan->runtime.flag & POSE_RUNTIME_TRANSFORM) {
1459 float pose_mat[3][3];
1460 BKE_pose_channel_transform_orientation(arm, pchan, pose_mat);
1461
1462 add_v3_v3(r_normal, pose_mat[2]);
1463 add_v3_v3(r_plane, pose_mat[1]);
1464 }
1465 }
1466 ok = true;
1467 }
1468 }
1469
1470 /* Use for both active & all. */
1471 if (ok) {
1472 /* We need the transpose of the inverse for a normal. */
1473 copy_m3_m4(imat, ob->object_to_world().ptr());
1474
1475 invert_m3_m3(mat, imat);
1476 transpose_m3(mat);
1477 mul_m3_v3(mat, r_normal);
1478 mul_m3_v3(mat, r_plane);
1479
1481 }
1482 }
1483 else {
1484 /* We need the one selected object, if its not active. */
1485 if (ob != nullptr) {
1486 bool ok = false;
1487 if (activeOnly || (ob->mode & (OB_MODE_ALL_PAINT | OB_MODE_PARTICLE_EDIT))) {
1488 /* Ignore selection state. */
1489 ok = true;
1490 }
1491 else {
1492 BKE_view_layer_synced_ensure(scene, view_layer);
1493 Base *base = BKE_view_layer_base_find(view_layer, ob);
1494 if (UNLIKELY(base == nullptr)) {
1495 /* This is very unlikely, if it happens allow the value to be set since the caller
1496 * may have taken the object from outside this view-layer. */
1497 ok = true;
1498 }
1499 else if (BASE_SELECTED(v3d, base)) {
1500 ok = true;
1501 }
1502 }
1503
1504 if (ok) {
1505 copy_v3_v3(r_normal, ob->object_to_world().ptr()[2]);
1506 copy_v3_v3(r_plane, ob->object_to_world().ptr()[1]);
1507 }
1508 }
1510 }
1511
1512 normalize_v3(r_normal);
1513 normalize_v3(r_plane);
1514
1515 return result;
1516}
1517
1518int getTransformOrientation(const bContext *C, float r_normal[3], float r_plane[3])
1519{
1521 Object *obedit = CTX_data_edit_object(C);
1522
1523 /* Dummy value, not #V3D_AROUND_ACTIVE and not #V3D_AROUND_LOCAL_ORIGINS. */
1524 short around = V3D_AROUND_CENTER_BOUNDS;
1525
1526 const Scene *scene = CTX_data_scene(C);
1527 ViewLayer *view_layer = CTX_data_view_layer(C);
1528 View3D *v3d = CTX_wm_view3d(C);
1529
1531 scene, view_layer, v3d, obact, obedit, around, r_normal, r_plane);
1532}
1533
1535 ViewLayer *view_layer,
1536 const View3D *v3d,
1537 Object *ob,
1538 Object *obedit,
1539 const short around,
1540 float r_orientation_mat[3][3])
1541{
1542 float normal[3] = {0.0, 0.0, 0.0};
1543 float plane[3] = {0.0, 0.0, 0.0};
1544
1545 int type;
1546
1547 type = getTransformOrientation_ex(scene, view_layer, v3d, ob, obedit, around, normal, plane);
1548
1549 /* Fallback, when the plane can't be calculated. */
1550 if (ORIENTATION_USE_PLANE(type) && is_zero_v3(plane)) {
1551 type = ORIENTATION_VERT;
1552 }
1553
1554 switch (type) {
1555 case ORIENTATION_NORMAL:
1556 if (createSpaceNormalTangent(r_orientation_mat, normal, plane) == 0) {
1557 type = ORIENTATION_NONE;
1558 }
1559 break;
1560 case ORIENTATION_VERT:
1561 if (createSpaceNormal(r_orientation_mat, normal) == 0) {
1562 type = ORIENTATION_NONE;
1563 }
1564 break;
1565 case ORIENTATION_EDGE:
1566 if (createSpaceNormalTangent(r_orientation_mat, normal, plane) == 0) {
1567 type = ORIENTATION_NONE;
1568 }
1569 break;
1570 case ORIENTATION_FACE:
1571 if (createSpaceNormalTangent(r_orientation_mat, normal, plane) == 0) {
1572 type = ORIENTATION_NONE;
1573 }
1574 break;
1575 default:
1577 break;
1578 }
1579
1580 if (type == ORIENTATION_NONE) {
1581 unit_m3(r_orientation_mat);
1582 }
1583}
1584
1585} // namespace blender::ed::transform
Functions to deal with Armatures.
C++ functions to deal with Armature collections (i.e. the successor of bone layers).
bool ANIM_bone_in_visible_collection(const bArmature *armature, const Bone *bone)
Blender kernel action and pose functionality.
void BKE_pose_channel_transform_orientation(const bArmature *arm, const bPoseChannel *pose_bone, float r_pose_orientation[3][3])
bPoseChannel * BKE_pose_channel_active(Object *ob, bool check_bonecoll)
bPoseChannel * BKE_pose_channel_active_if_bonecoll_visible(Object *ob) ATTR_WARN_UNUSED_RESULT
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
Base * CTX_data_active_base(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
void BKE_nurb_bpoint_calc_plane(Nurb *nu, BPoint *bp, float r_plane[3])
Definition curve.cc:1082
ListBase * BKE_curve_editNurbs_get(Curve *cu)
Definition curve.cc:419
void BKE_nurb_bezt_calc_normal(Nurb *nu, BezTriple *bezt, float r_normal[3])
Definition curve.cc:1007
bool BKE_curve_nurb_vert_active_get(Curve *cu, Nurb **r_nu, void **r_vert)
Definition curve.cc:5031
BPoint * BKE_nurb_bpoint_get_prev(Nurb *nu, BPoint *bp)
Definition curve.cc:985
BPoint * BKE_nurb_bpoint_get_next(Nurb *nu, BPoint *bp)
Definition curve.cc:942
void BKE_nurb_bpoint_calc_normal(Nurb *nu, BPoint *bp, float r_normal[3])
Definition curve.cc:1059
void BKE_nurb_bezt_calc_plane(Nurb *nu, BezTriple *bezt, float r_plane[3])
Definition curve.cc:1022
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
void void BKE_reports_prepend(ReportList *reports, const char *prepend)
Definition report.cc:233
int BKE_scene_transform_orientation_get_index(const Scene *scene, const TransformOrientation *orientation)
Definition scene.cc:3530
void BKE_scene_transform_orientation_remove(Scene *scene, TransformOrientation *orientation)
Definition scene.cc:3505
TransformOrientation * BKE_scene_transform_orientation_find(const Scene *scene, int index)
Definition scene.cc:3525
int BKE_scene_orientation_get_index(Scene *scene, int slot_index)
Definition scene.cc:2439
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ATTR_FALLTHROUGH
#define LISTBASE_FOREACH(type, var, list)
void * BLI_findstring(const ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:608
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
MINLINE int min_ii(int a, int b)
#define BLI_ASSERT_UNIT_V3(v)
#define M_PI_2
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
Normal to x,y matrix.
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:41
void mul_m3_v3(const float M[3][3], float r[3])
void mul_m3_m3_pre(float R[3][3], const float A[3][3])
void copy_m3_m3(float m1[3][3], const float m2[3][3])
void unit_m3(float m[3][3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
bool invert_m3_m3(float inverse[3][3], const float mat[3][3])
void zero_m3(float m[3][3])
void normalize_m3(float R[3][3]) ATTR_NONNULL()
void invert_m3_m3_safe_ortho(float inverse[3][3], const float mat[3][3])
void transpose_m3(float R[3][3])
void mul_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3])
bool invert_m3(float mat[3][3])
void mul_mat3_m4_v3(const float mat[4][4], float r[3])
void mul_m3_m4m3(float R[3][3], const float A[4][4], const float B[3][3])
void eulO_to_gimbal_axis(float gmat[3][3], const float eul[3], short order)
void axis_angle_to_mat3_single(float R[3][3], char axis, float angle)
void axis_angle_to_quat(float r[4], const float axis[3], float angle)
void quat_to_mat3(float m[3][3], const float q[4])
void mul_qt_v3(const float q[4], float r[3])
MINLINE float len_squared_v3(const float v[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 copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_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])
void project_plane_v3_v3v3(float out[3], const float p[3], const float v_plane[3])
void project_plane_normalized_v3_v3v3(float out[3], const float p[3], const float v_plane[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])
void ortho_v3_v3(float out[3], const float v[3])
MINLINE void negate_v3(float r[3])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
size_t void BLI_uniquename_cb(blender::FunctionRef< bool(blender::StringRefNull)> unique_check, const char *defname, char delim, char *name, size_t name_maxncpy) ATTR_NONNULL(2
unsigned int uint
#define ARRAY_SIZE(arr)
#define UNPACK3(a)
#define UNLIKELY(x)
#define ELEM(...)
#define LIKELY(x)
#define RPT_(msgid)
#define CTX_DATA_(context, msgid)
#define BLT_I18NCONTEXT_ID_SCENE
#define DATA_(msgid)
@ POSE_RUNTIME_TRANSFORM
@ ROT_MODE_QUAT
@ ROT_MODE_AXISANGLE
@ POSE_SELECTED
@ BONE_ROOTSEL
@ BONE_SELECTED
@ BONE_TIPSEL
@ BONE_CONNECTED
@ BONE_HINGE
@ CU_BEZIER
#define MAX_NAME
Definition DNA_defs.h:50
#define OB_MODE_ALL_PAINT
#define OB_MODE_ALL_WEIGHT_PAINT
@ OB_MODE_PARTICLE_EDIT
@ OB_MODE_POSE
Object is a sort of wrapper for general info.
@ OB_MBALL
@ OB_SURF
@ OB_ARMATURE
@ OB_MESH
@ OB_CURVES_LEGACY
#define BASE_SELECTED(v3d, base)
@ SCE_ORIENT_DEFAULT
@ RGN_TYPE_WINDOW
@ SPACE_SEQ
@ SPACE_VIEW3D
@ V3D_AROUND_ACTIVE
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_LOCAL_ORIGINS
@ V3D_ORIENT_NORMAL
@ V3D_ORIENT_CUSTOM
@ V3D_ORIENT_GLOBAL
@ V3D_ORIENT_PARENT
@ V3D_ORIENT_CUSTOM_MATRIX
@ V3D_ORIENT_LOCAL
@ V3D_ORIENT_VIEW
@ V3D_ORIENT_CURSOR
@ V3D_ORIENT_GIMBAL
@ RV3D_CAMOB
@ CURVE_HANDLE_NONE
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
void ED_armature_ebone_to_mat3(EditBone *ebone, float r_mat[3][3])
@ BM_ELEM_SELECT
#define BM_elem_flag_test(ele, hflag)
#define BM_ITER_MESH(ele, iter, bm, itype)
BMIterType
BMesh Iterators.
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
BMesh * bm
BMesh const char itype
BMVert * BM_mesh_active_vert_get(BMesh *bm)
void BM_editselection_plane(BMEditSelection *ese, float r_plane[3])
void BM_editselection_normal(BMEditSelection *ese, float r_normal[3])
bool BM_select_history_active_get(BMesh *bm, BMEditSelection *ese)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
void BM_vert_tri_calc_tangent_from_edge(BMVert *verts[3], float r_tangent[3])
void BM_face_calc_tangent_pair_auto(const BMFace *f, float r_tangent_a[3], float r_tangent_b[3])
void BM_edge_ordered_verts(const BMEdge *edge, BMVert **r_v1, BMVert **r_v2)
float BM_edge_calc_length_squared(const BMEdge *e)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
bool BM_vert_edge_pair(const BMVert *v, BMEdge **r_e_a, BMEdge **r_e_b)
BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
void activate(bool forceActivation=false) const
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
constexpr const char * c_str() const
#define SEL_F1
Definition curve.cc:4011
#define SEL_F2
Definition curve.cc:4012
#define SEL_F3
Definition curve.cc:4013
#define SELECT
#define offsetof(t, d)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
ccl_device_inline bool is_zero(const float2 a)
void pose_bone_descendent_iterator(bPose &pose, bPoseChannel &pose_bone, FunctionRef< void(bPoseChannel &child_bone)> callback)
bool bone_is_visible(const bArmature *armature, const Bone *bone)
void createSpaceNormalTangent_or_fallback(float mat[3][3], const float normal[3], const float tangent[3])
static bool test_rotmode_euler(short rotmode)
TransformOrientation * addMatrixSpace(bContext *C, float mat[3][3], const char *name, const bool overwrite)
static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle)
int getTransformOrientation(const bContext *C, float r_normal[3], float r_plane[3])
static void handle_object_parent_orientation(Object *ob, float r_mat[3][3])
bool gimbal_axis_pose(Object *ob, const bPoseChannel *pchan, float gmat[3][3])
bool BIF_createTransformOrientation(bContext *C, ReportList *reports, const char *name, bool use_view, bool activate, bool overwrite)
static TransformOrientation * createViewSpace(bContext *C, ReportList *, const char *name, const bool overwrite)
static TransformOrientation * createMeshSpace(bContext *C, ReportList *reports, const char *name, const bool overwrite)
int getTransformOrientation_ex(const Scene *scene, ViewLayer *view_layer, const View3D *v3d, Object *ob, Object *obedit, const short around, float r_normal[3], float r_plane[3])
void BIF_clearTransformOrientation(bContext *C)
static TransformOrientation * createObjectSpace(bContext *C, ReportList *, const char *name, const bool overwrite)
void BIF_removeTransformOrientationIndex(bContext *C, int index)
Object * transform_object_deform_pose_armature_get(const TransInfo *t, Object *ob)
bool createSpaceNormal(float mat[3][3], const float normal[3])
static int bone_children_clear_transflag(bPose &pose, bPoseChannel &pose_bone)
static int armature_bone_transflags_update(Object &ob, bArmature *arm, ListBase *lb)
bool createSpaceNormalTangent(float mat[3][3], const float normal[3], const float tangent[3])
void applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char r_name[64])
static void handle_armature_parent_orientation(Object *ob, float r_mat[3][3])
static uint bm_mesh_elems_select_get_n__internal(BMesh *bm, BMElem **elems, const uint n, const BMIterType itype, const char htype)
void calc_orientation_from_type(const bContext *C, float r_mat[3][3])
int BIF_countTransformOrientation(const bContext *C)
void BIF_removeTransformOrientation(bContext *C, TransformOrientation *target)
bool transform_orientations_create_from_axis(float mat[3][3], const float x[3], const float y[3], const float z[3])
short transform_orientation_matrix_get(bContext *C, TransInfo *t, short orient_index, const float custom[3][3], float r_spacemtx[3][3])
static uint bm_mesh_edges_select_get_n(BMesh *bm, BMEdge **elems, const uint n)
static TransformOrientation * createCurveSpace(bContext *C, ReportList *reports, const char *name, const bool overwrite)
const char * transform_orientations_spacename_get(TransInfo *t, const short orient_type)
static TransformOrientation * createBoneSpace(bContext *C, ReportList *reports, const char *name, const bool overwrite)
bool gimbal_axis_object(Object *ob, float gmat[3][3])
short calc_orientation_from_type_ex(const Scene *scene, ViewLayer *view_layer, const View3D *v3d, const RegionView3D *rv3d, Object *ob, Object *obedit, short orientation_index, int pivot_point, float r_mat[3][3])
static uint bm_mesh_verts_select_get_n(BMesh *bm, BMVert **elems, const uint n)
static TransformOrientation * findOrientationName(ListBase *lb, const char *name)
void ED_getTransformOrientationMatrix(const Scene *scene, ViewLayer *view_layer, const View3D *v3d, Object *ob, Object *obedit, short around, float r_orientation_mat[3][3])
static void uniqueOrientationName(ListBase *lb, char *name)
void BIF_selectTransformOrientation(bContext *C, TransformOrientation *target)
void transform_orientations_current_set(TransInfo *t, const short orient_index)
float2 image_transform_mirror_factor_get(const Strip *strip)
Strip * select_active_get(const Scene *scene)
VecBase< float, 2 > float2
MatBase< float, 3, 3 > float3x3
const char * name
void * regiondata
BMVert * v1
BMVert * v2
struct BMLoop * l
BMHeader head
float no[3]
struct BMVert * v
float co[3]
float no[3]
int totfacesel
int totvertsel
int totedgesel
uint8_t f1
float vec[4]
struct Object * object
float vec[3][3]
float arm_mat[4][4]
float bone_mat[3][3]
EditBone * parent
char name[258]
Definition DNA_ID.h:432
void * first
MetaElem * lastelem
ListBase * editelems
float quat[4]
struct Nurb * next
short type
BezTriple * bezt
BPoint * bp
struct bPose * pose
float rot[3]
float rotAxis[3]
struct Object * parent
float viewmat[4][4]
float viewinv[4][4]
struct ToolSettings * toolsettings
View3DCursor cursor
TransformOrientationSlot orientation_slots[4]
ListBase transform_spaces
StripTransform * transform
StripData * data
View3DOverlay overlay
struct Object * camera
struct EditBone * act_edbone
ListBase * edbo
struct Bone * bone
struct bPoseChannel * parent
struct bPoseChannel_Runtime runtime
float pose_mat[4][4]
ListBase chanbase
struct blender::ed::transform::TransInfo::@342160341045112220003361360177273117120157367076 orient[3]
i
Definition text_draw.cc:230
#define ORIENTATION_USE_PLANE(ty)
uint8_t flag
Definition wm_window.cc:145