Blender V5.0
transform_convert.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 "DNA_anim_types.h"
11#include "DNA_mesh_types.h"
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_array_utils.hh"
16#include "BLI_function_ref.hh"
17#include "BLI_kdtree.h"
18#include "BLI_linklist_stack.h"
19#include "BLI_listbase.h"
20#include "BLI_math_matrix.h"
21#include "BLI_math_vector.h"
22
23#include "BKE_action.hh"
24#include "BKE_anim_data.hh"
25#include "BKE_context.hh"
26#include "BKE_global.hh"
27#include "BKE_layer.hh"
28#include "BKE_lib_id.hh"
29#include "BKE_modifier.hh"
30#include "BKE_nla.hh"
31#include "BKE_scene.hh"
32
33#include "ED_particle.hh"
34#include "ED_screen.hh"
35#include "ED_screen_types.hh"
36#include "ED_sequencer.hh"
37
38#include "ANIM_keyframing.hh"
39#include "ANIM_nla.hh"
40
41#include "UI_view2d.hh"
42
43#include "WM_types.hh"
44
46
47#include "transform.hh"
48
49/* Own include. */
50#include "transform_convert.hh"
51
52namespace blender::ed::transform {
53
58
60{
61 if (data_len_all != 1) {
62 return;
63 }
65 return;
66 }
68 return;
69 }
70 if (t->flag & T_OVERRIDE_CENTER) {
71 return;
72 }
73
75}
76
81
82/* -------------------------------------------------------------------- */
85
90static void make_sorted_index_map(TransDataContainer *tc, FunctionRef<bool(int, int)> compare)
91{
92 BLI_assert(tc->sorted_index_map == nullptr);
94
95 const MutableSpan sorted_index_span(tc->sorted_index_map, tc->data_len);
96 array_utils::fill_index_range(sorted_index_span);
97 std::sort(sorted_index_span.begin(), sorted_index_span.end(), compare);
98}
99
106{
107 const bool use_dist = (t->flag & T_PROP_CONNECTED);
108 const auto compare = [&](const int a, const int b) {
109 /* If both selected, then they are equivalent. To keep memory access sequential (and thus more
110 * predictable for pre-caching) when iterating the arrays, keep them sorted by array index. */
111 const bool is_selected_a = tc->data[a].flag & TD_SELECTED;
112 const bool is_selected_b = tc->data[b].flag & TD_SELECTED;
113 if (is_selected_a && is_selected_b) {
114 return a < b;
115 }
116
117 /* Selected comes before unselected. */
118 if (is_selected_a) {
119 return true;
120 }
121 if (is_selected_b) {
122 return false;
123 }
124
125 /* If both are unselected, only then the distance matters. */
126 if (use_dist) {
127 return tc->data[a].dist < tc->data[b].dist;
128 }
129 return tc->data[a].rdist < tc->data[b].rdist;
130 };
131
132 /* The "sort by distance" is often preceded by "calculate distance", which is
133 * often preceded by "sort selected first". */
135
136 make_sorted_index_map(tc, compare);
137}
144
150{
151 BLI_assert_msg(tc->sorted_index_map == nullptr,
152 "Expected sorting by selection state to only happen once");
153
154 const auto compare = [&](const int a, const int b) {
155 /* If the selection state is the same, they are equivalent. To keep memory
156 * access sequential (and thus more predictable for pre-caching) when
157 * iterating the arrays, keep them sorted by array index. */
158 const bool is_selected_a = tc->data[a].flag & TD_SELECTED;
159 const bool is_selected_b = tc->data[b].flag & TD_SELECTED;
160 if (is_selected_a == is_selected_b) {
161 return a < b;
162 }
163
164 /* If A is selected, a comes before b, so return true.
165 * If B is selected, a comes after b, so return false. */
166 return is_selected_a;
167 };
168 make_sorted_index_map(tc, compare);
169}
176
178 const TransData *td,
179 const bool use_island,
180 const float proj_vec[3])
181{
182 float3 vec;
183
184 if (use_island) {
185 if (tc->use_local_mat) {
186 mul_v3_m4v3(vec, tc->mat, td->iloc);
187 }
188 else {
189 mul_v3_m3v3(vec, td->mtx, td->iloc);
190 }
191 }
192 else {
193 if (tc->use_local_mat) {
194 mul_v3_m4v3(vec, tc->mat, td->center);
195 }
196 else {
197 mul_v3_m3v3(vec, td->mtx, td->center);
198 }
199 }
200
201 if (proj_vec) {
202 float vec_p[3];
203 project_v3_v3v3(vec_p, vec, proj_vec);
204 sub_v3_v3(vec, vec_p);
205 }
206
207 return vec;
208}
209
216static void set_prop_dist(TransInfo *t, const bool with_dist)
217{
218 float _proj_vec[3];
219 const float *proj_vec = nullptr;
220
221 /* Support for face-islands. */
222 const bool use_island = transdata_check_local_islands(t, t->around);
223
224 if (t->flag & T_PROP_PROJECTED) {
225 if (t->spacetype == SPACE_VIEW3D && t->region && t->region->regiontype == RGN_TYPE_WINDOW) {
226 RegionView3D *rv3d = static_cast<RegionView3D *>(t->region->regiondata);
227 normalize_v3_v3(_proj_vec, rv3d->viewinv[2]);
228 proj_vec = _proj_vec;
229 }
230 }
231
232 /* Count number of selected. */
233 int td_table_len = 0;
235 tc->foreach_index_selected([&](const int /*i*/) { td_table_len++; });
236 }
237
238 /* Pointers to selected's #TransData.
239 * Used to find #TransData from the index returned by #BLI_kdtree_find_nearest. */
240 TransData **td_table = static_cast<TransData **>(
241 MEM_mallocN(sizeof(*td_table) * td_table_len, __func__));
242
243 /* Create and fill KD-tree of selected's positions - in global or proj_vec space. */
244 KDTree_3d *td_tree = BLI_kdtree_3d_new(td_table_len);
245
246 int td_table_index = 0;
248 tc->foreach_index_selected([&](const int i) {
249 TransData *td = &tc->data[i];
250 /* Initialize, it was malloced. */
251 td->rdist = 0.0f;
252
253 const float3 vec = prop_dist_loc_get(tc, td, use_island, proj_vec);
254
255 BLI_kdtree_3d_insert(td_tree, td_table_index, vec);
256 td_table[td_table_index++] = td;
257 });
258 }
259 BLI_assert(td_table_index == td_table_len);
260
261 BLI_kdtree_3d_balance(td_tree);
262
263 /* For each non-selected vertex, find distance to the nearest selected vertex. */
265 tc->foreach_index([&](const int i) {
266 TransData *td = &tc->data[i];
267 if (td->flag & TD_SELECTED) {
268 return true;
269 }
270
271 const float3 vec = prop_dist_loc_get(tc, td, use_island, proj_vec);
272
273 KDTreeNearest_3d nearest;
274 const int td_index = BLI_kdtree_3d_find_nearest(td_tree, vec, &nearest);
275
276 td->rdist = -1.0f;
277 if (td_index != -1) {
278 td->rdist = nearest.dist;
279 if (use_island) {
280 /* Use center and axismtx of closest point found. */
281 copy_v3_v3(td->center, td_table[td_index]->center);
282 copy_m3_m3(td->axismtx, td_table[td_index]->axismtx);
283 }
284 }
285
286 if (with_dist) {
287 td->dist = td->rdist;
288 }
289 return true;
290 });
291 }
292
293 BLI_kdtree_3d_free(td_tree);
294 MEM_freeN(td_table);
295}
296
298
299/* -------------------------------------------------------------------- */
302
304static bool pchan_autoik_adjust(bPoseChannel *pchan, short chainlen)
305{
306 bool changed = false;
307
308 /* Don't bother to search if no valid constraints. */
309 if ((pchan->constflag & (PCHAN_HAS_IK | PCHAN_HAS_NO_TARGET)) == 0) {
310 return changed;
311 }
312
313 /* Check if pchan has ik-constraint. */
314 LISTBASE_FOREACH (bConstraint *, con, &pchan->constraints) {
315 if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) {
316 continue;
317 }
318 if (con->type == CONSTRAINT_TYPE_KINEMATIC && (con->enforce != 0.0f)) {
319 bKinematicConstraint *data = static_cast<bKinematicConstraint *>(con->data);
320
321 /* Only accept if a temporary one (for auto-IK). */
322 if (data->flag & CONSTRAINT_IK_TEMP) {
323 /* `chainlen` is new `chainlen`, but is limited by maximum `chainlen`. */
324 const int old_rootbone = data->rootbone;
325 if ((chainlen == 0) || (chainlen > data->max_rootbone)) {
326 data->rootbone = data->max_rootbone;
327 }
328 else {
329 data->rootbone = chainlen;
330 }
331 changed |= (data->rootbone != old_rootbone);
332 }
333 }
334 }
335
336 return changed;
337}
338
340{
341 Main *bmain = CTX_data_main(t->context);
342
343 short *chainlen = &t->settings->autoik_chainlen;
344
345 /* `mode` determines what change to apply to `chainlen`. */
346 if (mode == 1) {
347 /* `mode==1` is from WHEELMOUSEDOWN: increases len. */
348 (*chainlen)++;
349 }
350 else if (mode == -1) {
351 /* `mode==-1` is from WHEELMOUSEUP: decreases len. */
352 if (*chainlen > 0) {
353 (*chainlen)--;
354 }
355 else {
356 /* IK length did not change, skip updates. */
357 return;
358 }
359 }
360
361 /* Apply to all pose-channels. */
362 bool changed = false;
363
365
366 /* Sanity checks (don't assume `t->poseobj` is set, or that it is an armature). */
367 if (ELEM(nullptr, tc->poseobj, tc->poseobj->pose)) {
368 continue;
369 }
370
372 changed |= pchan_autoik_adjust(pchan, *chainlen);
373 }
374 }
375
376 if (changed) {
377 /* TODO(sergey): Consider doing partial update only. */
379 }
380}
381
383
384/* -------------------------------------------------------------------- */
387
388void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic)
389{
390 TransData *td;
392 BLI_LINKSTACK_INIT(queue);
393 for (td = head; td <= tail; td++) {
394 if (td->flag & TD_SELECTED) {
395 td->dist = 0.0f;
396 BLI_LINKSTACK_PUSH(queue, td);
397 }
398 else {
399 td->dist = FLT_MAX;
400 }
401 }
402
403 while ((td = BLI_LINKSTACK_POP(queue))) {
404 float dist;
405 float vec[3];
406
407 TransData *next_td = nullptr;
408
409 if (td + 1 <= tail) {
410 next_td = td + 1;
411 }
412 else if (cyclic) {
413 next_td = head;
414 }
415
416 if (next_td != nullptr) {
417 sub_v3_v3v3(vec, next_td->center, td->center);
418 mul_m3_v3(head->mtx, vec);
419 dist = len_v3(vec) + td->dist;
420
421 if (dist < next_td->dist) {
422 next_td->dist = dist;
423 BLI_LINKSTACK_PUSH(queue, next_td);
424 }
425 }
426
427 next_td = nullptr;
428
429 if (td - 1 >= head) {
430 next_td = td - 1;
431 }
432 else if (cyclic) {
433 next_td = tail;
434 }
435
436 if (next_td != nullptr) {
437 sub_v3_v3v3(vec, next_td->center, td->center);
438 mul_m3_v3(head->mtx, vec);
439 dist = len_v3(vec) + td->dist;
440
441 if (dist < next_td->dist) {
442 next_td->dist = dist;
443 BLI_LINKSTACK_PUSH(queue, next_td);
444 }
445 }
446 }
447 BLI_LINKSTACK_FREE(queue);
448}
449
451{
453 td->flag |= TD_BEZTRIPLE;
454 hdata = td->hdata = MEM_mallocN<TransDataCurveHandleFlags>("CuHandle Data");
455 hdata->ih1 = bezt->h1;
456 hdata->h1 = &bezt->h1;
457 hdata->ih2 = bezt->h2; /* In case the second is not selected. */
458 hdata->h2 = &bezt->h2;
459 return hdata;
460}
461
463
464/* -------------------------------------------------------------------- */
467
469{
470 /* NOTE(@ideasman42): Often used to clip UV's after proportional editing:
471 * In this case the radius of the proportional region can end outside the clipping area,
472 * while not ideal an elegant solution here would likely be computationally expensive
473 * as it would need to calculate the transform value that would meet the UV bounds.
474 * While it would be technically correct to handle this properly,
475 * there isn't a strong use case for it. */
476
478 TransData *td = tc->data;
479 for (int a = 0; a < tc->data_len; a++, td++) {
480 if ((td->flag & TD_SKIP) || (!td->loc)) {
481 continue;
482 }
483
484 td->loc[0] = min_ff(max_ff(0.0f, td->loc[0]), t->aspect[0]);
485 td->loc[1] = min_ff(max_ff(0.0f, td->loc[1]), t->aspect[1]);
486 }
487 }
488}
489
491
492/* -------------------------------------------------------------------- */
495
497{
498 char dir;
499 float center[2];
500 if (t->flag & T_MODAL) {
502 (View2D *)t->view, t->mouse.imval[0], t->mouse.imval[1], &center[0], &center[1]);
503 dir = (center[0] > cframe) ? 'R' : 'L';
504 {
505 /* XXX: This saves the direction in the "mirror" property to be used for redo! */
506 if (dir == 'R') {
507 t->flag |= T_NO_MIRROR;
508 }
509 }
510 }
511 else {
512 dir = (t->flag & T_NO_MIRROR) ? 'R' : 'L';
513 }
514
515 return dir;
516}
517
518bool FrameOnMouseSide(char side, float frame, float cframe)
519{
520 /* Both sides, so it doesn't matter. */
521 if (side == 'B') {
522 return true;
523 }
524
525 /* Only on the named side. */
526 if (side == 'R') {
527 return (frame >= cframe);
528 }
529 return (frame <= cframe);
530}
531
533
534/* -------------------------------------------------------------------- */
537
539{
540 /* Loop through constraints, checking if there's one of the mentioned
541 * constraints needing special crazy-space corrections. */
542 if (list) {
543 LISTBASE_FOREACH (bConstraint *, con, list) {
544 /* Only consider constraint if it is enabled, and has influence on result. */
545 if ((con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) == 0 && (con->enforce != 0.0f)) {
546 /* Affirmative: returns for specific constraints here. */
547 /* Constraints that require this regardless. */
548 if (ELEM(con->type,
554 {
555 return true;
556 }
557
558 /* Constraints that require this only under special conditions. */
559 if (con->type == CONSTRAINT_TYPE_CHILDOF) {
560 /* ChildOf constraint only works when using all location components, see #42256. */
562
563 if ((data->flag & CHILDOF_LOCX) && (data->flag & CHILDOF_LOCY) &&
564 (data->flag & CHILDOF_LOCZ))
565 {
566 return true;
567 }
568 }
569 else if (con->type == CONSTRAINT_TYPE_ROTLIKE) {
570 /* CopyRot constraint only does this when rotating, and offset is on. */
572
575 {
576 return true;
577 }
578 }
579 else if (con->type == CONSTRAINT_TYPE_TRANSLIKE) {
580 /* Copy Transforms constraint only does this in the Before mode. */
582
585 {
586 return true;
587 }
588 if (ELEM(data->mix_mode, TRANSLIKE_MIX_BEFORE_SPLIT) && ELEM(t->mode, TFM_ROTATION)) {
589 return true;
590 }
591 }
592 else if (con->type == CONSTRAINT_TYPE_ACTION) {
593 /* The Action constraint only does this in the Before mode. */
595
598 {
599 return true;
600 }
601 if (ELEM(data->mix_mode, ACTCON_MIX_BEFORE_SPLIT) && ELEM(t->mode, TFM_ROTATION)) {
602 return true;
603 }
604 }
605 else if (con->type == CONSTRAINT_TYPE_TRANSFORM) {
606 /* Transform constraint needs it for rotation at least (r.57309),
607 * but doing so when translating may also mess things up, see: #36203. */
609
610 if (data->to == TRANS_ROTATION) {
611 if (t->mode == TFM_ROTATION && data->mix_mode_rot == TRANS_MIXROT_BEFORE) {
612 return true;
613 }
614 }
615 }
616 }
617 }
618 }
619
620 /* No appropriate candidates found. */
621 return false;
622}
623
625
626/* -------------------------------------------------------------------- */
629
631{
632 /* NOTE: Sequencer freeing has its own function now because of a conflict
633 * with transform's order of freeing (campbell).
634 * Order changed, the sequencer stuff should go back in here. */
635
636 /* Early out when nothing happened. */
637 if (t->data_len_all == 0 || t->mode == TFM_DUMMY) {
638 return;
639 }
640
642 return;
643 }
644
647}
648
650{
651 if (t->options & CTX_CURSOR) {
652 return G_TRANSFORM_CURSOR;
653 }
654 if (t->spacetype == SPACE_SEQ) {
655 return G_TRANSFORM_SEQ;
656 }
657 if (t->spacetype == SPACE_GRAPH) {
658 return G_TRANSFORM_FCURVES;
659 }
660 if ((t->flag & T_EDIT) || (t->options & CTX_POSE_BONE)) {
661 return G_TRANSFORM_EDIT;
662 }
663 if (t->options & (CTX_OBJECT | CTX_TEXTURE_SPACE)) {
664 return G_TRANSFORM_OBJ;
665 }
666
667 return 0;
668}
669
671
672/* -------------------------------------------------------------------- */
675
677{
678 BLI_assert(ELEM(t->data_len_all, 0, -1));
679 t->data_len_all = 0;
680 int data_container_len_orig = t->data_container_len;
681 for (TransDataContainer *th_end = t->data_container - 1,
682 *tc = &t->data_container[t->data_container_len - 1];
683 tc != th_end;
684 tc--)
685 {
686 if (tc->data_len == 0) {
687 uint index = tc - t->data_container;
688 if (index + 1 != t->data_container_len) {
689 std::swap(t->data_container[index], t->data_container[t->data_container_len - 1]);
690 }
691 t->data_container_len -= 1;
692 }
693 else {
694 t->data_len_all += tc->data_len;
695 }
696 }
697 if (data_container_len_orig != t->data_container_len) {
698 t->data_container = static_cast<TransDataContainer *>(
700 }
701 return t->data_len_all;
702}
703
705{
706 /* NOTE: Proportional editing is not usable in pose mode yet #32444. */
707 /* NOTE: This `ELEM` uses more than 16 elements and so has been split. */
708 if (!(ELEM(t->data_type,
726 {
727 /* Disable proportional editing. */
728 t->options |= CTX_NO_PET;
730 return;
731 }
732
733 if (t->data_len_all && (t->flag & T_PROP_EDIT)) {
735 /* Selected objects are already first, no need to presort. */
736 }
737 else {
739 }
740
742 /* Distance has already been set. */
743 }
744 else if (ELEM(t->data_type,
748 {
749 if (t->flag & T_PROP_CONNECTED) {
750 /* Already calculated by #transform_convert_mesh_connectivity_distance. */
751 }
752 else {
753 set_prop_dist(t, false);
754 }
755 }
756 else if (t->data_type == &TransConvertType_MeshUV && t->flag & T_PROP_CONNECTED) {
757 /* Already calculated by #uv_set_connectivity_distance. */
758 }
759 else if (t->data_type == &TransConvertType_Curve) {
761 if (t->flag & T_PROP_CONNECTED) {
762 /* Already calculated by #calc_distanceCurveVerts. */
763 }
764 else {
765 set_prop_dist(t, false);
766 }
767 }
768 else if (ELEM(t->data_type,
771 {
773 if (t->flag & T_PROP_CONNECTED) {
774 /* Already calculated by #calculate_curve_point_distances_for_proportional_editing. */
775 }
776 else {
777 set_prop_dist(t, false);
778 }
779 }
780 else {
781 set_prop_dist(t, true);
782 }
783
785 }
786 else if (ELEM(t->obedit_type, OB_CURVES_LEGACY)) {
787 /* Needed because bezier handles can be partially selected
788 * and are still added into transform data. */
790 }
791}
792
793/* For multi object editing. */
795{
796 if (!ELEM(t->data_type,
810 {
811 /* Does not support Multi object editing. */
812 return;
813 }
814
815 const eObjectMode object_mode = eObjectMode(obact ? obact->mode : OB_MODE_OBJECT);
816 const short object_type = obact ? obact->type : -1;
817
818 if ((object_mode & OB_MODE_EDIT) ||
820 ((object_mode & OB_MODE_POSE) && (object_type == OB_ARMATURE)))
821 {
822 if (t->data_container) {
824 }
825
826 Vector<Object *> local_objects;
827 if (objects.is_empty()) {
829 params.object_mode = object_mode;
830 /* Pose transform operates on `ob->pose` so don't skip duplicate object-data. */
831 params.no_dup_data = (object_mode & OB_MODE_POSE) == 0;
833 t->scene,
834 t->view_layer,
835 static_cast<const View3D *>((t->spacetype == SPACE_VIEW3D) ? t->view : nullptr),
836 &params);
837 objects = local_objects;
838 }
839
841 t->data_container_len = objects.size();
842
843 for (int i = 0; i < objects.size(); i++) {
845 if (!(t->flag & T_NO_MIRROR) && (objects[i]->type == OB_MESH)) {
846 tc->use_mirror_axis_x = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_X) != 0;
847 tc->use_mirror_axis_y = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_Y) != 0;
848 tc->use_mirror_axis_z = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_Z) != 0;
849 }
850
851 if (object_mode & OB_MODE_EDIT) {
852 tc->obedit = objects[i];
853 /* Check needed for UVs. */
854 if ((t->flag & T_2D_EDIT) == 0) {
855 tc->use_local_mat = true;
856 }
857 }
858 else if (object_mode & OB_MODE_POSE) {
859 tc->poseobj = objects[i];
860 tc->use_local_mat = true;
861 }
863 tc->use_local_mat = true;
864 }
865
866 if (tc->use_local_mat) {
867 BLI_assert((t->flag & T_2D_EDIT) == 0);
868 copy_m4_m4(tc->mat, objects[i]->object_to_world().ptr());
869 copy_m3_m4(tc->mat3, tc->mat);
870 /* For non-invertible scale matrices, #invert_m4_m4_fallback()
871 * can still provide a valid pivot. */
873 invert_m3_m3(tc->imat3, tc->mat3);
875 }
876 /* Otherwise leave as zero. */
877 }
878 }
879}
880
881static TransConvertTypeInfo *convert_type_get(const TransInfo *t, Object **r_obj_armature)
882{
883 ViewLayer *view_layer = t->view_layer;
886
887 /* If tests must match recalc_data for correct updates. */
888 if (t->options & CTX_CURSOR) {
889 if (t->spacetype == SPACE_IMAGE) {
891 }
892
893 if (t->spacetype == SPACE_SEQ) {
895 }
896
898 }
899 if (!(t->options & CTX_PAINT_CURVE) && (t->spacetype == SPACE_VIEW3D) && ob &&
900 (ob->mode == OB_MODE_SCULPT) && ob->sculpt)
901 {
903 }
904 if (t->options & CTX_TEXTURE_SPACE) {
906 }
907 if (t->options & CTX_EDGE_DATA) {
909 }
910 if (t->options & CTX_GPENCIL_STROKES) {
911 if (t->obedit_type == OB_GREASE_PENCIL) {
913 }
914 return nullptr;
915 }
916 if (t->spacetype == SPACE_IMAGE) {
917 if (t->options & CTX_MASK) {
918 return &TransConvertType_Mask;
919 }
920 if (t->options & CTX_PAINT_CURVE) {
921 if (!ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) {
923 }
924 }
925 else if (t->obedit_type == OB_MESH) {
927 }
928 return nullptr;
929 }
930 if (t->spacetype == SPACE_ACTION) {
932 }
933 if (t->spacetype == SPACE_NLA) {
934 return &TransConvertType_NLA;
935 }
936 if (t->spacetype == SPACE_SEQ) {
937 if (t->options & CTX_SEQUENCER_IMAGE) {
939 }
942 }
944 }
945 if (t->spacetype == SPACE_GRAPH) {
947 }
948 if (t->spacetype == SPACE_NODE) {
949 return &TransConvertType_Node;
950 }
951 if (t->spacetype == SPACE_CLIP) {
952 if (t->options & CTX_MOVIECLIP) {
955 }
957 }
958 if (t->options & CTX_MASK) {
959 return &TransConvertType_Mask;
960 }
961 return nullptr;
962 }
963 if (t->obedit_type != -1) {
964 if (t->obedit_type == OB_MESH) {
965 if (t->mode == TFM_SKIN_RESIZE) {
967 }
970 }
971 return &TransConvertType_Mesh;
972 }
975 }
976 if (t->obedit_type == OB_LATTICE) {
978 }
979 if (t->obedit_type == OB_MBALL) {
981 }
982 if (t->obedit_type == OB_ARMATURE) {
984 }
985 if (t->obedit_type == OB_CURVES) {
987 }
988 if (t->obedit_type == OB_POINTCLOUD) {
990 }
991 return nullptr;
992 }
993 if (ob && (ob->mode & OB_MODE_POSE)) {
994 return &TransConvertType_Pose;
995 }
996 if (ob && (ob->mode & OB_MODE_ALL_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) {
998 if (ob_armature) {
999 *r_obj_armature = ob_armature;
1000 return &TransConvertType_Pose;
1001 }
1002 return nullptr;
1003 }
1004 if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT) &&
1006 {
1008 }
1009 if (ob && ((ob->mode & OB_MODE_ALL_PAINT) || (ob->mode & OB_MODE_SCULPT_CURVES))) {
1012 }
1013 return nullptr;
1014 }
1015 if (ob && (ob->mode & OB_MODE_ALL_PAINT_GPENCIL)) {
1016 /* In grease pencil all transformations must be canceled if not Object or Edit. */
1017 return nullptr;
1018 }
1020}
1021
1023{
1024 t->data_len_all = -1;
1025
1026 Object *ob_armature = nullptr;
1027 t->data_type = convert_type_get(t, &ob_armature);
1028 if (t->data_type == nullptr) {
1029 printf("edit type not implemented!\n");
1030 BLI_assert(t->data_len_all == -1);
1031 t->data_len_all = 0;
1032 return;
1033 }
1034
1035 t->flag |= eTFlag(t->data_type->flags);
1036
1037 if (ob_armature) {
1038 init_TransDataContainers(t, ob_armature, {ob_armature});
1039 }
1040 else {
1043 init_TransDataContainers(t, ob, {});
1044 }
1045
1047 t->options |= CTX_OBJECT;
1048
1049 /* Needed for correct Object.obmat after duplication, see: #62135. */
1051
1054 }
1057 }
1058 TransConvertType_Object.create_trans_data(C, t);
1059 /* Check if we're transforming the camera from the camera. */
1060 if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) {
1061 View3D *v3d = static_cast<View3D *>(t->view);
1062 RegionView3D *rv3d = static_cast<RegionView3D *>(t->region->regiondata);
1063 if ((rv3d->persp == RV3D_CAMOB) && v3d->camera) {
1064 /* We could have a flag to easily check an object is being transformed. */
1065 if (v3d->camera->id.tag & ID_TAG_DOIT) {
1066 t->options |= CTX_CAMERA;
1067 }
1068 }
1069 else if (v3d->ob_center && v3d->ob_center->id.tag & ID_TAG_DOIT) {
1070 t->options |= CTX_CAMERA;
1071 }
1072 }
1073 }
1074 else {
1075 if (t->data_type == &TransConvertType_Pose) {
1076 t->options |= CTX_POSE_BONE;
1077 }
1078 else if (t->data_type == &TransConvertType_Sequencer) {
1079 /* Sequencer has no use for floating point transform. */
1080 t->num.flag |= NUM_NO_FRACTION;
1081 }
1083 t->obedit_type = -1;
1084 }
1086 }
1087
1089
1091}
1092
1094
1095/* -------------------------------------------------------------------- */
1098
1100{
1101 Object *ob = tc->obedit;
1102 ModifierData *md = static_cast<ModifierData *>(ob->modifiers.first);
1103
1104 for (; md; md = md->next) {
1105 if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
1107
1108 if ((mmd->flag & MOD_MIR_CLIPPING) == 0) {
1109 continue;
1110 }
1111
1112 if ((mmd->flag & (MOD_MIR_AXIS_X | MOD_MIR_AXIS_Y | MOD_MIR_AXIS_Z)) == 0) {
1113 continue;
1114 }
1115
1116 float mtx[4][4], imtx[4][4];
1117
1118 if (mmd->mirror_ob) {
1119 float obinv[4][4];
1120
1121 invert_m4_m4(obinv, mmd->mirror_ob->object_to_world().ptr());
1122 mul_m4_m4m4(mtx, obinv, ob->object_to_world().ptr());
1123 invert_m4_m4(imtx, mtx);
1124 }
1125
1126 TransData *td = tc->data;
1127 for (int i = 0; i < tc->data_len; i++, td++) {
1128 float loc[3], iloc[3];
1129
1130 if (td->loc == nullptr) {
1131 break;
1132 }
1133
1134 if (td->flag & TD_SKIP) {
1135 continue;
1136 }
1137
1138 copy_v3_v3(loc, td->loc);
1139 copy_v3_v3(iloc, td->iloc);
1140
1141 if (mmd->mirror_ob) {
1142 mul_m4_v3(mtx, loc);
1143 mul_m4_v3(mtx, iloc);
1144 }
1145
1146 bool is_clipping = false;
1147 if (mmd->flag & MOD_MIR_AXIS_X) {
1148 if (fabsf(iloc[0]) <= mmd->tolerance || loc[0] * iloc[0] < 0.0f) {
1149 loc[0] = 0.0f;
1150 is_clipping = true;
1151 }
1152 }
1153
1154 if (mmd->flag & MOD_MIR_AXIS_Y) {
1155 if (fabsf(iloc[1]) <= mmd->tolerance || loc[1] * iloc[1] < 0.0f) {
1156 loc[1] = 0.0f;
1157 is_clipping = true;
1158 }
1159 }
1160 if (mmd->flag & MOD_MIR_AXIS_Z) {
1161 if (fabsf(iloc[2]) <= mmd->tolerance || loc[2] * iloc[2] < 0.0f) {
1162 loc[2] = 0.0f;
1163 is_clipping = true;
1164 }
1165 }
1166
1167 if (is_clipping) {
1168 if (mmd->mirror_ob) {
1169 mul_m4_v3(imtx, loc);
1170 }
1171 copy_v3_v3(td->loc, loc);
1172 }
1173 }
1174 }
1175 }
1176}
1177
1179{
1180 Scene *scene = t->scene;
1181 wmTimer *animtimer = t->animtimer;
1182 ScreenAnimData *sad = static_cast<ScreenAnimData *>((animtimer) ? animtimer->customdata :
1183 nullptr);
1184
1185 /* Sanity checks. */
1186 if (ELEM(nullptr, scene, id, sad)) {
1187 return;
1188 }
1189
1190 /* Check if we need a new strip if:
1191 * - If `animtimer` is running.
1192 * - We're not only keying for available channels.
1193 * - The option to add new actions for each round is not enabled.
1194 */
1197 {
1198 /* If playback has just looped around,
1199 * we need to add a new NLA track+strip to allow a clean pass to occur. */
1200 if ((sad) && (sad->flag & ANIMPLAY_FLAG_JUMPED)) {
1201 AnimData *adt = BKE_animdata_from_id(id);
1202 const bool is_first = (adt) && (adt->nla_tracks.first == nullptr);
1203
1204 /* Perform push-down manually with some differences
1205 * NOTE: #BKE_nla_action_pushdown() sync warning. */
1206 if ((adt->action) && !(adt->flag & ADT_NLA_EDIT_ON)) {
1207 /* Only push down if action is more than 1-2 frames long. */
1208 const float2 frame_range = adt->action->wrap().get_frame_range_of_keys(true);
1209 if (frame_range[1] > frame_range[0] + 2.0f) {
1210 /* TODO: call #BKE_nla_action_pushdown() instead? */
1211
1212 /* Add a new NLA strip to the track, which references the active action + slot. */
1213 NlaStrip *strip = BKE_nlastack_add_strip({*id, *adt}, ID_IS_OVERRIDE_LIBRARY(id));
1214 BLI_assert(strip);
1216
1217 /* Clear reference to action now that we've pushed it onto the stack. */
1218 const bool unassign_ok = animrig::unassign_action(*id);
1220 unassign_ok,
1221 "Expecting un-assigning an action to always work when pushing down an NLA strip");
1222 UNUSED_VARS_NDEBUG(unassign_ok);
1223
1224 /* Adjust blending + extend so that they will behave correctly. */
1228
1229 /* Copy current "action blending" settings from adt to the strip,
1230 * as it was keyframed with these settings, so omitting them will
1231 * change the effect, see: #54766. */
1232 if (is_first == false) {
1233 strip->blendmode = adt->act_blendmode;
1234 strip->influence = adt->act_influence;
1235
1236 if (adt->act_influence < 1.0f) {
1237 /* Enable "user-controlled" influence (which will insert a default keyframe)
1238 * so that the influence doesn't get lost on the new update.
1239 *
1240 * NOTE: An alternative way would have been to instead hack the influence
1241 * to not get always get reset to full strength if NLASTRIP_FLAG_USR_INFLUENCE
1242 * is disabled but auto-blending isn't being used. However, that approach
1243 * is a bit hacky/hard to discover, and may cause backwards compatibility issues,
1244 * so it's better to just do it this way. */
1247 }
1248 }
1249
1250 /* Also, adjust the AnimData's action extend mode to be on
1251 * 'nothing' so that previous result still play. */
1253 }
1254 }
1255 }
1256 }
1257}
1258
1259void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, const float y_fac)
1260{
1261 float delta_x = td->loc[0] - td->iloc[0];
1262 float delta_y = (td->loc[1] - td->iloc[1]) * y_fac;
1263
1264 /* If the handles are to be moved too
1265 * (as side-effect of keyframes moving, to keep the general effect)
1266 * offset them by the same amount so that the general angles are maintained
1267 * (i.e. won't change while handles are free-to-roam and keyframes are snap-locked).
1268 */
1269 if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) {
1270 td2d->h1[0] = td2d->ih1[0] + delta_x;
1271 td2d->h1[1] = td2d->ih1[1] + delta_y;
1272 }
1273 if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) {
1274 td2d->h2[0] = td2d->ih2[0] + delta_x;
1275 td2d->h2[1] = td2d->ih2[1] + delta_y;
1276 }
1277}
1278
1280{
1281 if (!t->data_type || !t->data_type->recalc_data) {
1282 return;
1283 }
1284 t->data_type->recalc_data(t);
1285}
1286
1288
1289} // namespace blender::ed::transform
Functions to insert, delete or modify keyframes.
Blender kernel action and pose functionality.
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:83
Main * CTX_data_main(const bContext *C)
@ G_TRANSFORM_OBJ
@ G_TRANSFORM_CURSOR
@ G_TRANSFORM_SEQ
@ G_TRANSFORM_EDIT
@ G_TRANSFORM_FCURVES
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_mode_params(const Scene *scene, ViewLayer *view_layer, const View3D *v3d, const ObjectsInModeParams *params)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
NlaStrip * BKE_nlastack_add_strip(OwnedAnimData owned_adt, const bool is_liboverride)
void BKE_nlastrip_validate_fcurves(NlaStrip *strip)
void BKE_scene_graph_evaluated_ensure(Depsgraph *depsgraph, Main *bmain)
Definition scene.cc:2626
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
A KD-tree for nearest neighbor search.
#define LISTBASE_FOREACH(type, var, list)
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
void mul_m3_v3(const float M[3][3], float r[3])
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void normalize_m3_m3(float R[3][3], const float M[3][3]) ATTR_NONNULL()
void copy_m3_m3(float m1[3][3], const float m2[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 mul_m4_v3(const float M[4][4], float r[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])
bool invert_m4_m4_fallback(float inverse[4][4], const float mat[4][4])
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])
void project_v3_v3v3(float out[3], const float p[3], const float v_proj[3])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
unsigned int uint
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
void DEG_relations_tag_update(Main *bmain)
@ ID_TAG_DOIT
Definition DNA_ID.h:1036
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:730
@ PCHAN_HAS_NO_TARGET
@ PCHAN_HAS_IK
@ NLASTRIP_FLAG_ACTIVE
@ NLASTRIP_FLAG_USR_INFLUENCE
@ NLASTRIP_FLAG_AUTO_BLENDS
@ NLASTRIP_FLAG_SELECT
@ ADT_NLA_EDIT_ON
@ NLASTRIP_EXTEND_NOTHING
@ CONSTRAINT_OFF
@ CONSTRAINT_DISABLE
@ CONSTRAINT_IK_TEMP
@ ROTLIKE_MIX_OFFSET
@ ROTLIKE_MIX_BEFORE
@ CONSTRAINT_TYPE_CHILDOF
@ CONSTRAINT_TYPE_TRANSFORM
@ CONSTRAINT_TYPE_FOLLOWTRACK
@ CONSTRAINT_TYPE_OBJECTSOLVER
@ CONSTRAINT_TYPE_ARMATURE
@ CONSTRAINT_TYPE_ROTLIKE
@ CONSTRAINT_TYPE_KINEMATIC
@ CONSTRAINT_TYPE_TRANSLIKE
@ CONSTRAINT_TYPE_CLAMPTO
@ CONSTRAINT_TYPE_ACTION
@ CONSTRAINT_TYPE_FOLLOWPATH
@ ACTCON_MIX_BEFORE
@ ACTCON_MIX_BEFORE_SPLIT
@ ACTCON_MIX_BEFORE_FULL
@ TRANSLIKE_MIX_BEFORE
@ TRANSLIKE_MIX_BEFORE_SPLIT
@ TRANSLIKE_MIX_BEFORE_FULL
@ TRANS_ROTATION
@ TRANS_MIXROT_BEFORE
@ ME_SYMMETRY_X
@ ME_SYMMETRY_Y
@ ME_SYMMETRY_Z
@ eModifierMode_Realtime
@ eModifierType_Mirror
@ MOD_MIR_AXIS_Z
@ MOD_MIR_CLIPPING
@ MOD_MIR_AXIS_X
@ MOD_MIR_AXIS_Y
#define OB_MODE_ALL_PAINT
#define OB_MODE_ALL_WEIGHT_PAINT
#define OB_MODE_ALL_PAINT_GPENCIL
eObjectMode
@ OB_MODE_PARTICLE_EDIT
@ OB_MODE_EDIT
@ OB_MODE_SCULPT
@ OB_MODE_SCULPT_CURVES
@ OB_MODE_POSE
@ OB_MODE_OBJECT
@ OB_LATTICE
@ OB_MBALL
@ OB_SURF
@ OB_GREASE_PENCIL
@ OB_ARMATURE
@ OB_MESH
@ OB_POINTCLOUD
@ OB_CURVES_LEGACY
@ OB_CURVES
@ SCE_XFORM_SKIP_CHILDREN
@ SCE_XFORM_DATA_ORIGIN
@ RGN_TYPE_WINDOW
@ RGN_TYPE_PREVIEW
@ SPACE_CLIP
@ SPACE_ACTION
@ SPACE_NODE
@ SPACE_NLA
@ SPACE_SEQ
@ SPACE_IMAGE
@ SPACE_GRAPH
@ SPACE_VIEW3D
@ AUTOKEY_FLAG_LAYERED_RECORD
@ AUTOKEY_FLAG_INSERTAVAILABLE
@ V3D_AROUND_ACTIVE
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_CENTER_MEDIAN
@ V3D_AROUND_LOCAL_ORIGINS
@ RV3D_CAMOB
@ NUM_NO_FRACTION
PTCacheEdit * PE_get_current(Depsgraph *depsgraph, Scene *scene, Object *ob)
int PE_start_edit(PTCacheEdit *edit)
@ ANIMPLAY_FLAG_JUMPED
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
#define C
Definition RandGen.cpp:29
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1668
BMesh const char void * data
constexpr T * end() const
Definition BLI_span.hh:548
constexpr T * begin() const
Definition BLI_span.hh:544
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
Definition BLI_span.hh:260
#define printf(...)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ActionSlotAssignmentResult assign_action_slot_handle(NlaStrip &strip, slot_handle_t slot_handle, ID &animated_id)
bool unassign_action(ID &animated_id)
bool is_keying_flag(const Scene *scene, eKeying_Flag flag)
void fill_index_range(MutableSpan< T > span, const T start=0)
TransConvertTypeInfo TransConvertType_Pose
TransConvertTypeInfo TransConvertType_Mesh
void animrecord_check_state(TransInfo *t, ID *id)
TransConvertTypeInfo TransConvertType_Mask
TransConvertTypeInfo TransConvertType_Sequencer
void special_aftertrans_update(bContext *C, TransInfo *t)
void recalc_data(TransInfo *t)
static void sort_trans_data_selected_first(TransInfo *t)
void clipUVData(TransInfo *t)
static void sort_trans_data_dist_container(const TransInfo *t, TransDataContainer *tc)
TransConvertTypeInfo TransConvertType_Sculpt
TransConvertTypeInfo TransConvertType_Node
bool transform_mode_use_local_origins(const TransInfo *t)
static void set_prop_dist(TransInfo *t, const bool with_dist)
TransConvertTypeInfo TransConvertType_Graph
static bool pchan_autoik_adjust(bPoseChannel *pchan, short chainlen)
char transform_convert_frame_side_dir_get(TransInfo *t, float cframe)
void transform_around_single_fallback(TransInfo *t)
TransConvertTypeInfo TransConvertType_Curve
TransConvertTypeInfo TransConvertType_CursorImage
TransConvertTypeInfo TransConvertType_MeshEdge
static void init_TransDataContainers(TransInfo *t, Object *obact, Span< Object * > objects)
static void sort_trans_data_selected_first_container(TransDataContainer *tc)
TransConvertTypeInfo TransConvertType_Lattice
static void make_sorted_index_map(TransDataContainer *tc, FunctionRef< bool(int, int)> compare)
Object * transform_object_deform_pose_armature_get(const TransInfo *t, Object *ob)
bool constraints_list_needinv(TransInfo *t, ListBase *list)
TransConvertTypeInfo TransConvertType_Particle
TransDataCurveHandleFlags * initTransDataCurveHandles(TransData *td, BezTriple *bezt)
TransConvertTypeInfo TransConvertType_MBall
void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc)
static float3 prop_dist_loc_get(const TransDataContainer *tc, const TransData *td, const bool use_island, const float proj_vec[3])
TransConvertTypeInfo TransConvertType_Action
bool FrameOnMouseSide(char side, float frame, float cframe)
void transform_autoik_update(TransInfo *t, short mode)
TransConvertTypeInfo TransConvertType_NLA
TransConvertTypeInfo TransConvertType_MeshSkin
void sort_trans_data_dist(TransInfo *t)
static void init_proportional_edit(TransInfo *t)
static TransConvertTypeInfo * convert_type_get(const TransInfo *t, Object **r_obj_armature)
void transform_around_single_fallback_ex(TransInfo *t, int data_len_all)
void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, const float y_fac)
TransConvertTypeInfo TransConvertType_MeshUV
TransConvertTypeInfo TransConvertType_Tracking
TransConvertTypeInfo TransConvertType_Object
TransConvertTypeInfo TransConvertType_CursorSequencer
static int countAndCleanTransDataContainer(TransInfo *t)
TransConvertTypeInfo TransConvertType_PaintCurve
int special_transform_moving(TransInfo *t)
void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic)
TransConvertTypeInfo TransConvertType_EditArmature
void create_trans_data(bContext *C, TransInfo *t)
bool transdata_check_local_islands(TransInfo *t, short around)
TransConvertTypeInfo TransConvertType_Cursor3D
bool sequencer_retiming_mode_is_active(const bContext *C)
VecBase< float, 2 > float2
VecBase< float, 3 > float3
#define fabsf
#define FLT_MAX
Definition stdcycles.h:14
void * regiondata
bAction * action
short act_blendmode
float act_influence
int32_t slot_handle
short act_extendmode
ListBase nla_tracks
Definition DNA_ID.h:414
int tag
Definition DNA_ID.h:442
void * first
struct Object * mirror_ob
struct ModifierData * next
short blendmode
float influence
short extendmode
short flag
struct bPose * pose
ListBase modifiers
struct SculptSession * sculpt
float viewinv[4][4]
struct ToolSettings * toolsettings
struct Object * camera
struct Object * ob_center
ListBase chanbase
void(* special_aftertrans_update)(bContext *C, TransInfo *t)
void(* create_trans_data)(bContext *C, TransInfo *t)
bool foreach_index(FunctionRef< bool(int)> fn) const
Definition transform.hh:758
void foreach_index_selected(FunctionRef< void(int)> fn) const
Definition transform.hh:785
TransDataCurveHandleFlags * hdata
Definition transform.hh:520
TransConvertTypeInfo * data_type
Definition transform.hh:810
TransDataContainer * data_container
Definition transform.hh:802
void * customdata
Definition WM_types.hh:965
i
Definition text_draw.cc:230
#define T_PROP_EDIT_ALL
Definition transform.hh:28
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
Definition transform.hh:42
conversion and adaptation of different datablocks to a common struct.