2019-09-05 14:34:54 -03:00
|
|
|
/*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** \file
|
|
|
|
* \ingroup edtransform
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "DNA_anim_types.h"
|
|
|
|
#include "DNA_armature_types.h"
|
|
|
|
#include "DNA_space_types.h"
|
|
|
|
#include "DNA_constraint_types.h"
|
|
|
|
#include "DNA_gpencil_types.h"
|
|
|
|
#include "DNA_mask_types.h"
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
#include "BLI_math.h"
|
|
|
|
#include "BLI_listbase.h"
|
|
|
|
#include "BLI_string.h"
|
|
|
|
#include "BLI_kdtree.h"
|
|
|
|
|
|
|
|
#include "BKE_animsys.h"
|
|
|
|
#include "BKE_armature.h"
|
|
|
|
#include "BKE_context.h"
|
|
|
|
#include "BKE_fcurve.h"
|
|
|
|
#include "BKE_global.h"
|
|
|
|
#include "BKE_gpencil.h"
|
|
|
|
#include "BKE_layer.h"
|
|
|
|
#include "BKE_key.h"
|
|
|
|
#include "BKE_main.h"
|
|
|
|
#include "BKE_modifier.h"
|
|
|
|
#include "BKE_nla.h"
|
|
|
|
#include "BKE_node.h"
|
|
|
|
#include "BKE_pointcache.h"
|
|
|
|
#include "BKE_rigidbody.h"
|
|
|
|
#include "BKE_scene.h"
|
|
|
|
#include "BKE_editmesh.h"
|
|
|
|
#include "BKE_tracking.h"
|
|
|
|
#include "BKE_mask.h"
|
|
|
|
|
|
|
|
#include "BIK_api.h"
|
|
|
|
|
|
|
|
#include "ED_anim_api.h"
|
|
|
|
#include "ED_armature.h"
|
|
|
|
#include "ED_particle.h"
|
|
|
|
#include "ED_image.h"
|
|
|
|
#include "ED_keyframing.h"
|
|
|
|
#include "ED_keyframes_edit.h"
|
|
|
|
#include "ED_object.h"
|
|
|
|
#include "ED_markers.h"
|
|
|
|
#include "ED_mesh.h"
|
|
|
|
#include "ED_node.h"
|
|
|
|
#include "ED_clip.h"
|
|
|
|
#include "ED_mask.h"
|
|
|
|
|
|
|
|
#include "WM_api.h" /* for WM_event_add_notifier to deal with stabilization nodes */
|
|
|
|
#include "WM_types.h"
|
|
|
|
|
|
|
|
#include "RNA_access.h"
|
|
|
|
|
|
|
|
#include "DEG_depsgraph.h"
|
|
|
|
#include "DEG_depsgraph_build.h"
|
|
|
|
|
|
|
|
#include "transform.h"
|
|
|
|
#include "transform_convert.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Transforming around ourselves is no use, fallback to individual origins,
|
|
|
|
* useful for curve/armatures.
|
|
|
|
*/
|
|
|
|
void transform_around_single_fallback(TransInfo *t)
|
|
|
|
{
|
|
|
|
if ((t->data_len_all == 1) &&
|
|
|
|
(ELEM(t->around, V3D_AROUND_CENTER_BOUNDS, V3D_AROUND_CENTER_MEDIAN, V3D_AROUND_ACTIVE)) &&
|
|
|
|
(ELEM(t->mode, TFM_RESIZE, TFM_ROTATION, TFM_TRACKBALL))) {
|
|
|
|
t->around = V3D_AROUND_LOCAL_ORIGINS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ************************** Functions *************************** */
|
|
|
|
|
|
|
|
static int trans_data_compare_dist(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
const TransData *td_a = (const TransData *)a;
|
|
|
|
const TransData *td_b = (const TransData *)b;
|
|
|
|
|
|
|
|
if (td_a->dist < td_b->dist) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (td_a->dist > td_b->dist) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int trans_data_compare_rdist(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
const TransData *td_a = (const TransData *)a;
|
|
|
|
const TransData *td_b = (const TransData *)b;
|
|
|
|
|
|
|
|
if (td_a->rdist < td_b->rdist) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (td_a->rdist > td_b->rdist) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sort_trans_data_dist_container(const TransInfo *t, TransDataContainer *tc)
|
|
|
|
{
|
|
|
|
TransData *start = tc->data;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < tc->data_len && start->flag & TD_SELECTED; i++) {
|
|
|
|
start++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i < tc->data_len) {
|
|
|
|
if (t->flag & T_PROP_CONNECTED) {
|
|
|
|
qsort(start, tc->data_len - i, sizeof(TransData), trans_data_compare_dist);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
qsort(start, tc->data_len - i, sizeof(TransData), trans_data_compare_rdist);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void sort_trans_data_dist(TransInfo *t)
|
|
|
|
{
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
sort_trans_data_dist_container(t, tc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Make #TD_SELECTED first in the array.
|
|
|
|
*/
|
|
|
|
static void sort_trans_data_selected_first_container(TransDataContainer *tc)
|
|
|
|
{
|
|
|
|
TransData *sel, *unsel;
|
|
|
|
TransData temp;
|
|
|
|
unsel = tc->data;
|
|
|
|
sel = tc->data;
|
|
|
|
sel += tc->data_len - 1;
|
|
|
|
while (sel > unsel) {
|
|
|
|
while (unsel->flag & TD_SELECTED) {
|
|
|
|
unsel++;
|
|
|
|
if (unsel == sel) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (!(sel->flag & TD_SELECTED)) {
|
|
|
|
sel--;
|
|
|
|
if (unsel == sel) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
temp = *unsel;
|
|
|
|
*unsel = *sel;
|
|
|
|
*sel = temp;
|
|
|
|
sel--;
|
|
|
|
unsel++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void sort_trans_data_selected_first(TransInfo *t)
|
|
|
|
{
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
sort_trans_data_selected_first_container(tc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Distance calculated from not-selected vertex to nearest selected vertex.
|
|
|
|
*/
|
|
|
|
static void set_prop_dist(TransInfo *t, const bool with_dist)
|
|
|
|
{
|
|
|
|
int a;
|
|
|
|
|
|
|
|
float _proj_vec[3];
|
|
|
|
const float *proj_vec = NULL;
|
|
|
|
|
|
|
|
/* support for face-islands */
|
|
|
|
const bool use_island = transdata_check_local_islands(t, t->around);
|
|
|
|
|
|
|
|
if (t->flag & T_PROP_PROJECTED) {
|
|
|
|
if (t->spacetype == SPACE_VIEW3D && t->ar && t->ar->regiontype == RGN_TYPE_WINDOW) {
|
|
|
|
RegionView3D *rv3d = t->ar->regiondata;
|
|
|
|
normalize_v3_v3(_proj_vec, rv3d->viewinv[2]);
|
|
|
|
proj_vec = _proj_vec;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Count number of selected. */
|
|
|
|
int td_table_len = 0;
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
TransData *td = tc->data;
|
|
|
|
for (a = 0; a < tc->data_len; a++, td++) {
|
|
|
|
if (td->flag & TD_SELECTED) {
|
|
|
|
td_table_len++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* By definition transform-data has selected items in beginning. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Pointers to selected's #TransData.
|
|
|
|
* Used to find #TransData from the index returned by #BLI_kdtree_find_nearest. */
|
|
|
|
TransData **td_table = MEM_mallocN(sizeof(*td_table) * td_table_len, __func__);
|
|
|
|
|
|
|
|
/* Create and fill kd-tree of selected's positions - in global or proj_vec space. */
|
|
|
|
KDTree_3d *td_tree = BLI_kdtree_3d_new(td_table_len);
|
|
|
|
|
|
|
|
int td_table_index = 0;
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
TransData *td = tc->data;
|
|
|
|
for (a = 0; a < tc->data_len; a++, td++) {
|
|
|
|
if (td->flag & TD_SELECTED) {
|
|
|
|
/* Initialize, it was mallocced. */
|
|
|
|
float vec[3];
|
|
|
|
td->rdist = 0.0f;
|
|
|
|
|
|
|
|
if (use_island) {
|
|
|
|
if (tc->use_local_mat) {
|
|
|
|
mul_v3_m4v3(vec, tc->mat, td->iloc);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mul_v3_m3v3(vec, td->mtx, td->iloc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (tc->use_local_mat) {
|
|
|
|
mul_v3_m4v3(vec, tc->mat, td->center);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mul_v3_m3v3(vec, td->mtx, td->center);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (proj_vec) {
|
|
|
|
float vec_p[3];
|
|
|
|
project_v3_v3v3(vec_p, vec, proj_vec);
|
|
|
|
sub_v3_v3(vec, vec_p);
|
|
|
|
}
|
|
|
|
|
|
|
|
BLI_kdtree_3d_insert(td_tree, td_table_index, vec);
|
|
|
|
td_table[td_table_index++] = td;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* By definition transform-data has selected items in beginning. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BLI_assert(td_table_index == td_table_len);
|
|
|
|
|
|
|
|
BLI_kdtree_3d_balance(td_tree);
|
|
|
|
|
|
|
|
/* For each non-selected vertex, find distance to the nearest selected vertex. */
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
TransData *td = tc->data;
|
|
|
|
for (a = 0; a < tc->data_len; a++, td++) {
|
|
|
|
if ((td->flag & TD_SELECTED) == 0) {
|
|
|
|
float vec[3];
|
|
|
|
|
|
|
|
if (use_island) {
|
|
|
|
if (tc->use_local_mat) {
|
|
|
|
mul_v3_m4v3(vec, tc->mat, td->iloc);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mul_v3_m3v3(vec, td->mtx, td->iloc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (tc->use_local_mat) {
|
|
|
|
mul_v3_m4v3(vec, tc->mat, td->center);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mul_v3_m3v3(vec, td->mtx, td->center);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (proj_vec) {
|
|
|
|
float vec_p[3];
|
|
|
|
project_v3_v3v3(vec_p, vec, proj_vec);
|
|
|
|
sub_v3_v3(vec, vec_p);
|
|
|
|
}
|
|
|
|
|
|
|
|
KDTreeNearest_3d nearest;
|
|
|
|
const int td_index = BLI_kdtree_3d_find_nearest(td_tree, vec, &nearest);
|
|
|
|
|
|
|
|
td->rdist = -1.0f;
|
|
|
|
if (td_index != -1) {
|
|
|
|
td->rdist = nearest.dist;
|
|
|
|
if (use_island) {
|
|
|
|
copy_v3_v3(td->center, td_table[td_index]->center);
|
|
|
|
copy_m3_m3(td->axismtx, td_table[td_index]->axismtx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (with_dist) {
|
|
|
|
td->dist = td->rdist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BLI_kdtree_3d_free(td_tree);
|
|
|
|
MEM_freeN(td_table);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ********************* pose mode ************* */
|
|
|
|
|
|
|
|
static short apply_targetless_ik(Object *ob)
|
|
|
|
{
|
|
|
|
bPoseChannel *pchan, *parchan, *chanlist[256];
|
|
|
|
bKinematicConstraint *data;
|
|
|
|
int segcount, apply = 0;
|
|
|
|
|
|
|
|
/* now we got a difficult situation... we have to find the
|
|
|
|
* target-less IK pchans, and apply transformation to the all
|
|
|
|
* pchans that were in the chain */
|
|
|
|
|
|
|
|
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
|
|
|
data = has_targetless_ik(pchan);
|
|
|
|
if (data && (data->flag & CONSTRAINT_IK_AUTO)) {
|
|
|
|
|
|
|
|
/* fill the array with the bones of the chain (armature.c does same, keep it synced) */
|
|
|
|
segcount = 0;
|
|
|
|
|
|
|
|
/* exclude tip from chain? */
|
|
|
|
if (!(data->flag & CONSTRAINT_IK_TIP)) {
|
|
|
|
parchan = pchan->parent;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
parchan = pchan;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the chain's root & count the segments needed */
|
|
|
|
for (; parchan; parchan = parchan->parent) {
|
|
|
|
chanlist[segcount] = parchan;
|
|
|
|
segcount++;
|
|
|
|
|
|
|
|
if (segcount == data->rootbone || segcount > 255) {
|
|
|
|
break; // 255 is weak
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (; segcount; segcount--) {
|
|
|
|
Bone *bone;
|
|
|
|
float rmat[4][4] /*, tmat[4][4], imat[4][4]*/;
|
|
|
|
|
|
|
|
/* pose_mat(b) = pose_mat(b-1) * offs_bone * channel * constraint * IK */
|
|
|
|
/* we put in channel the entire result of rmat = (channel * constraint * IK) */
|
|
|
|
/* pose_mat(b) = pose_mat(b-1) * offs_bone * rmat */
|
|
|
|
/* rmat = pose_mat(b) * inv(pose_mat(b-1) * offs_bone ) */
|
|
|
|
|
|
|
|
parchan = chanlist[segcount - 1];
|
|
|
|
bone = parchan->bone;
|
|
|
|
bone->flag |= BONE_TRANSFORM; /* ensures it gets an auto key inserted */
|
|
|
|
|
|
|
|
BKE_armature_mat_pose_to_bone(parchan, parchan->pose_mat, rmat);
|
|
|
|
|
|
|
|
/* apply and decompose, doesn't work for constraints or non-uniform scale well */
|
|
|
|
{
|
|
|
|
float rmat3[3][3], qrmat[3][3], imat3[3][3], smat[3][3];
|
|
|
|
copy_m3_m4(rmat3, rmat);
|
|
|
|
|
|
|
|
/* rotation */
|
|
|
|
/* [#22409] is partially caused by this, as slight numeric error introduced during
|
|
|
|
* the solving process leads to locked-axis values changing. However, we cannot modify
|
|
|
|
* the values here, or else there are huge discrepancies between IK-solver (interactive)
|
|
|
|
* and applied poses. */
|
|
|
|
BKE_pchan_mat3_to_rot(parchan, rmat3, false);
|
|
|
|
|
|
|
|
/* for size, remove rotation */
|
|
|
|
/* causes problems with some constraints (so apply only if needed) */
|
|
|
|
if (data->flag & CONSTRAINT_IK_STRETCH) {
|
|
|
|
BKE_pchan_rot_to_mat3(parchan, qrmat);
|
|
|
|
invert_m3_m3(imat3, qrmat);
|
|
|
|
mul_m3_m3m3(smat, rmat3, imat3);
|
|
|
|
mat3_to_size(parchan->size, smat);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* causes problems with some constraints (e.g. childof), so disable this */
|
|
|
|
/* as it is IK shouldn't affect location directly */
|
|
|
|
/* copy_v3_v3(parchan->loc, rmat[3]); */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
apply = 1;
|
|
|
|
data->flag &= ~CONSTRAINT_IK_AUTO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return apply;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bone_children_clear_transflag(int mode, short around, ListBase *lb)
|
|
|
|
{
|
|
|
|
Bone *bone = lb->first;
|
|
|
|
|
|
|
|
for (; bone; bone = bone->next) {
|
|
|
|
if ((bone->flag & BONE_HINGE) && (bone->flag & BONE_CONNECTED)) {
|
|
|
|
bone->flag |= BONE_HINGE_CHILD_TRANSFORM;
|
|
|
|
}
|
|
|
|
else if ((bone->flag & BONE_TRANSFORM) && (mode == TFM_ROTATION || mode == TFM_TRACKBALL) &&
|
|
|
|
(around == V3D_AROUND_LOCAL_ORIGINS)) {
|
|
|
|
bone->flag |= BONE_TRANSFORM_CHILD;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
bone->flag &= ~(BONE_TRANSFORM | BONE_TRANSFORM_MIRROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
bone_children_clear_transflag(mode, around, &bone->childbase);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sets transform flags in the bones
|
|
|
|
* returns total number of bones with BONE_TRANSFORM */
|
|
|
|
int count_set_pose_transflags(Object *ob,
|
|
|
|
const int mode,
|
|
|
|
const short around,
|
|
|
|
bool has_translate_rotate[2])
|
|
|
|
{
|
|
|
|
bArmature *arm = ob->data;
|
|
|
|
bPoseChannel *pchan;
|
|
|
|
Bone *bone;
|
|
|
|
int total = 0;
|
|
|
|
|
|
|
|
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
|
|
|
bone = pchan->bone;
|
|
|
|
if (PBONE_VISIBLE(arm, bone)) {
|
|
|
|
if ((bone->flag & BONE_SELECTED)) {
|
|
|
|
bone->flag |= BONE_TRANSFORM;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
bone->flag &= ~(BONE_TRANSFORM | BONE_TRANSFORM_MIRROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
bone->flag &= ~BONE_HINGE_CHILD_TRANSFORM;
|
|
|
|
bone->flag &= ~BONE_TRANSFORM_CHILD;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
bone->flag &= ~(BONE_TRANSFORM | BONE_TRANSFORM_MIRROR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* make sure no bone can be transformed when a parent is transformed */
|
|
|
|
/* since pchans are depsgraph sorted, the parents are in beginning of list */
|
|
|
|
if (!ELEM(mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) {
|
|
|
|
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
|
|
|
bone = pchan->bone;
|
|
|
|
if (bone->flag & BONE_TRANSFORM) {
|
|
|
|
bone_children_clear_transflag(mode, around, &bone->childbase);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* now count, and check if we have autoIK or have to switch from translate to rotate */
|
|
|
|
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
|
|
|
bone = pchan->bone;
|
|
|
|
if (bone->flag & BONE_TRANSFORM) {
|
|
|
|
total++;
|
|
|
|
|
|
|
|
if (has_translate_rotate != NULL) {
|
|
|
|
if (has_targetless_ik(pchan) == NULL) {
|
|
|
|
if (pchan->parent && (pchan->bone->flag & BONE_CONNECTED)) {
|
|
|
|
if (pchan->bone->flag & BONE_HINGE_CHILD_TRANSFORM) {
|
|
|
|
has_translate_rotate[0] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC) {
|
|
|
|
has_translate_rotate[0] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((pchan->protectflag & OB_LOCK_ROT) != OB_LOCK_ROT) {
|
|
|
|
has_translate_rotate[1] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
has_translate_rotate[0] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------- Auto-IK ---------- */
|
|
|
|
|
|
|
|
/* adjust pose-channel's auto-ik chainlen */
|
|
|
|
static bool pchan_autoik_adjust(bPoseChannel *pchan, short chainlen)
|
|
|
|
{
|
|
|
|
bConstraint *con;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
/* don't bother to search if no valid constraints */
|
|
|
|
if ((pchan->constflag & (PCHAN_HAS_IK | PCHAN_HAS_TARGET)) == 0) {
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if pchan has ik-constraint */
|
|
|
|
for (con = pchan->constraints.first; con; con = con->next) {
|
|
|
|
if (con->type == CONSTRAINT_TYPE_KINEMATIC && (con->enforce != 0.0f)) {
|
|
|
|
bKinematicConstraint *data = con->data;
|
|
|
|
|
|
|
|
/* only accept if a temporary one (for auto-ik) */
|
|
|
|
if (data->flag & CONSTRAINT_IK_TEMP) {
|
|
|
|
/* chainlen is new chainlen, but is limited by maximum chainlen */
|
|
|
|
const int old_rootbone = data->rootbone;
|
|
|
|
if ((chainlen == 0) || (chainlen > data->max_rootbone)) {
|
|
|
|
data->rootbone = data->max_rootbone;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
data->rootbone = chainlen;
|
|
|
|
}
|
|
|
|
changed |= (data->rootbone != old_rootbone);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* change the chain-length of auto-ik */
|
|
|
|
void transform_autoik_update(TransInfo *t, short mode)
|
|
|
|
{
|
|
|
|
Main *bmain = CTX_data_main(t->context);
|
|
|
|
|
|
|
|
short *chainlen = &t->settings->autoik_chainlen;
|
|
|
|
bPoseChannel *pchan;
|
|
|
|
|
|
|
|
/* mode determines what change to apply to chainlen */
|
|
|
|
if (mode == 1) {
|
|
|
|
/* mode=1 is from WHEELMOUSEDOWN... increases len */
|
|
|
|
(*chainlen)++;
|
|
|
|
}
|
|
|
|
else if (mode == -1) {
|
|
|
|
/* mode==-1 is from WHEELMOUSEUP... decreases len */
|
|
|
|
if (*chainlen > 0) {
|
|
|
|
(*chainlen)--;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* IK length did not change, skip updates. */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* apply to all pose-channels */
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
|
|
|
|
/* sanity checks (don't assume t->poseobj is set, or that it is an armature) */
|
|
|
|
if (ELEM(NULL, tc->poseobj, tc->poseobj->pose)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (pchan = tc->poseobj->pose->chanbase.first; pchan; pchan = pchan->next) {
|
|
|
|
changed |= pchan_autoik_adjust(pchan, *chainlen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (changed) {
|
|
|
|
/* TODO(sergey): Consider doing partial update only. */
|
|
|
|
DEG_relations_tag_update(bmain);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* frees temporal IKs */
|
|
|
|
static void pose_grab_with_ik_clear(Main *bmain, Object *ob)
|
|
|
|
{
|
|
|
|
bKinematicConstraint *data;
|
|
|
|
bPoseChannel *pchan;
|
|
|
|
bConstraint *con, *next;
|
|
|
|
bool relations_changed = false;
|
|
|
|
|
|
|
|
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
|
|
|
/* clear all temporary lock flags */
|
|
|
|
pchan->ikflag &= ~(BONE_IK_NO_XDOF_TEMP | BONE_IK_NO_YDOF_TEMP | BONE_IK_NO_ZDOF_TEMP);
|
|
|
|
|
|
|
|
pchan->constflag &= ~(PCHAN_HAS_IK | PCHAN_HAS_TARGET);
|
|
|
|
|
|
|
|
/* remove all temporary IK-constraints added */
|
|
|
|
for (con = pchan->constraints.first; con; con = next) {
|
|
|
|
next = con->next;
|
|
|
|
if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
|
|
|
|
data = con->data;
|
|
|
|
if (data->flag & CONSTRAINT_IK_TEMP) {
|
|
|
|
relations_changed = true;
|
|
|
|
|
|
|
|
/* iTaSC needs clear for removed constraints */
|
|
|
|
BIK_clear_data(ob->pose);
|
|
|
|
|
|
|
|
BLI_remlink(&pchan->constraints, con);
|
|
|
|
MEM_freeN(con->data);
|
|
|
|
MEM_freeN(con);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
pchan->constflag |= PCHAN_HAS_IK;
|
|
|
|
if (data->tar == NULL || (data->tar->type == OB_ARMATURE && data->subtarget[0] == 0)) {
|
|
|
|
pchan->constflag |= PCHAN_HAS_TARGET;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (relations_changed) {
|
|
|
|
/* TODO(sergey): Consider doing partial update only. */
|
|
|
|
DEG_relations_tag_update(bmain);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ********************* curve/surface ********* */
|
|
|
|
|
|
|
|
void calc_distanceCurveVerts(TransData *head, TransData *tail)
|
|
|
|
{
|
|
|
|
TransData *td, *td_near = NULL;
|
|
|
|
for (td = head; td <= tail; td++) {
|
|
|
|
if (td->flag & TD_SELECTED) {
|
|
|
|
td_near = td;
|
|
|
|
td->dist = 0.0f;
|
|
|
|
}
|
|
|
|
else if (td_near) {
|
|
|
|
float dist;
|
|
|
|
float vec[3];
|
|
|
|
|
|
|
|
sub_v3_v3v3(vec, td_near->center, td->center);
|
|
|
|
mul_m3_v3(head->mtx, vec);
|
|
|
|
dist = len_v3(vec);
|
|
|
|
|
|
|
|
if (dist < (td - 1)->dist) {
|
|
|
|
td->dist = (td - 1)->dist;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
td->dist = dist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
td->dist = FLT_MAX;
|
|
|
|
td->flag |= TD_NOTCONNECTED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
td_near = NULL;
|
|
|
|
for (td = tail; td >= head; td--) {
|
|
|
|
if (td->flag & TD_SELECTED) {
|
|
|
|
td_near = td;
|
|
|
|
td->dist = 0.0f;
|
|
|
|
}
|
|
|
|
else if (td_near) {
|
|
|
|
float dist;
|
|
|
|
float vec[3];
|
|
|
|
|
|
|
|
sub_v3_v3v3(vec, td_near->center, td->center);
|
|
|
|
mul_m3_v3(head->mtx, vec);
|
|
|
|
dist = len_v3(vec);
|
|
|
|
|
|
|
|
if (td->flag & TD_NOTCONNECTED || dist < td->dist || (td + 1)->dist < td->dist) {
|
|
|
|
td->flag &= ~TD_NOTCONNECTED;
|
|
|
|
if (dist < (td + 1)->dist) {
|
|
|
|
td->dist = (td + 1)->dist;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
td->dist = dist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Utility function for getting the handle data from bezier's */
|
|
|
|
TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTriple *bezt)
|
|
|
|
{
|
|
|
|
TransDataCurveHandleFlags *hdata;
|
|
|
|
td->flag |= TD_BEZTRIPLE;
|
|
|
|
hdata = td->hdata = MEM_mallocN(sizeof(TransDataCurveHandleFlags), "CuHandle Data");
|
|
|
|
hdata->ih1 = bezt->h1;
|
|
|
|
hdata->h1 = &bezt->h1;
|
|
|
|
hdata->ih2 = bezt->h2; /* in case the second is not selected */
|
|
|
|
hdata->h2 = &bezt->h2;
|
|
|
|
return hdata;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ********************* UV ****************** */
|
|
|
|
|
|
|
|
bool clipUVTransform(TransInfo *t, float vec[2], const bool resize)
|
|
|
|
{
|
|
|
|
bool clipx = true, clipy = true;
|
|
|
|
float min[2], max[2];
|
|
|
|
|
|
|
|
min[0] = min[1] = 0.0f;
|
|
|
|
max[0] = t->aspect[0];
|
|
|
|
max[1] = t->aspect[1];
|
|
|
|
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
|
|
|
|
TransData *td;
|
|
|
|
int a;
|
|
|
|
|
|
|
|
for (a = 0, td = tc->data; a < tc->data_len; a++, td++) {
|
|
|
|
minmax_v2v2_v2(min, max, td->loc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (resize) {
|
|
|
|
if (min[0] < 0.0f && t->center_global[0] > 0.0f && t->center_global[0] < t->aspect[0] * 0.5f) {
|
|
|
|
vec[0] *= t->center_global[0] / (t->center_global[0] - min[0]);
|
|
|
|
}
|
|
|
|
else if (max[0] > t->aspect[0] && t->center_global[0] < t->aspect[0]) {
|
|
|
|
vec[0] *= (t->center_global[0] - t->aspect[0]) / (t->center_global[0] - max[0]);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
clipx = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (min[1] < 0.0f && t->center_global[1] > 0.0f && t->center_global[1] < t->aspect[1] * 0.5f) {
|
|
|
|
vec[1] *= t->center_global[1] / (t->center_global[1] - min[1]);
|
|
|
|
}
|
|
|
|
else if (max[1] > t->aspect[1] && t->center_global[1] < t->aspect[1]) {
|
|
|
|
vec[1] *= (t->center_global[1] - t->aspect[1]) / (t->center_global[1] - max[1]);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
clipy = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (min[0] < 0.0f) {
|
|
|
|
vec[0] -= min[0];
|
|
|
|
}
|
|
|
|
else if (max[0] > t->aspect[0]) {
|
|
|
|
vec[0] -= max[0] - t->aspect[0];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
clipx = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (min[1] < 0.0f) {
|
|
|
|
vec[1] -= min[1];
|
|
|
|
}
|
|
|
|
else if (max[1] > t->aspect[1]) {
|
|
|
|
vec[1] -= max[1] - t->aspect[1];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
clipy = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (clipx || clipy);
|
|
|
|
}
|
|
|
|
|
|
|
|
void clipUVData(TransInfo *t)
|
|
|
|
{
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
TransData *td = tc->data;
|
|
|
|
for (int a = 0; a < tc->data_len; a++, td++) {
|
|
|
|
if (td->flag & TD_NOACTION) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((td->flag & TD_SKIP) || (!td->loc)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
td->loc[0] = min_ff(max_ff(0.0f, td->loc[0]), t->aspect[0]);
|
|
|
|
td->loc[1] = min_ff(max_ff(0.0f, td->loc[1]), t->aspect[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ********************* ANIMATION EDITORS (GENERAL) ************************* */
|
|
|
|
|
|
|
|
/* This function tests if a point is on the "mouse" side of the cursor/frame-marking */
|
|
|
|
bool FrameOnMouseSide(char side, float frame, float cframe)
|
|
|
|
{
|
|
|
|
/* both sides, so it doesn't matter */
|
|
|
|
if (side == 'B') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* only on the named side */
|
|
|
|
if (side == 'R') {
|
|
|
|
return (frame >= cframe);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return (frame <= cframe);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ********************* ACTION EDITOR ****************** */
|
|
|
|
|
|
|
|
static int gpf_cmp_frame(void *thunk, const void *a, const void *b)
|
|
|
|
{
|
|
|
|
const bGPDframe *frame_a = a;
|
|
|
|
const bGPDframe *frame_b = b;
|
|
|
|
|
|
|
|
if (frame_a->framenum < frame_b->framenum) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (frame_a->framenum > frame_b->framenum) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
*((bool *)thunk) = true;
|
|
|
|
/* selected last */
|
|
|
|
if ((frame_a->flag & GP_FRAME_SELECT) && ((frame_b->flag & GP_FRAME_SELECT) == 0)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int masklay_shape_cmp_frame(void *thunk, const void *a, const void *b)
|
|
|
|
{
|
|
|
|
const MaskLayerShape *frame_a = a;
|
|
|
|
const MaskLayerShape *frame_b = b;
|
|
|
|
|
|
|
|
if (frame_a->frame < frame_b->frame) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (frame_a->frame > frame_b->frame) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
*((bool *)thunk) = true;
|
|
|
|
/* selected last */
|
|
|
|
if ((frame_a->flag & MASK_SHAPE_SELECT) && ((frame_b->flag & MASK_SHAPE_SELECT) == 0)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Called by special_aftertrans_update to make sure selected gp-frames replace
|
|
|
|
* any other gp-frames which may reside on that frame (that are not selected).
|
|
|
|
* It also makes sure gp-frames are still stored in chronological order after
|
|
|
|
* transform.
|
|
|
|
*/
|
|
|
|
static void posttrans_gpd_clean(bGPdata *gpd)
|
|
|
|
{
|
|
|
|
bGPDlayer *gpl;
|
|
|
|
|
|
|
|
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
|
|
|
bGPDframe *gpf, *gpfn;
|
|
|
|
bool is_double = false;
|
|
|
|
|
|
|
|
BLI_listbase_sort_r(&gpl->frames, gpf_cmp_frame, &is_double);
|
|
|
|
|
|
|
|
if (is_double) {
|
|
|
|
for (gpf = gpl->frames.first; gpf; gpf = gpfn) {
|
|
|
|
gpfn = gpf->next;
|
|
|
|
if (gpfn && gpf->framenum == gpfn->framenum) {
|
|
|
|
BKE_gpencil_layer_delframe(gpl, gpf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
|
|
|
|
BLI_assert(!gpf->next || gpf->framenum < gpf->next->framenum);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
/* set cache flag to dirty */
|
|
|
|
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void posttrans_mask_clean(Mask *mask)
|
|
|
|
{
|
|
|
|
MaskLayer *masklay;
|
|
|
|
|
|
|
|
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
|
|
|
|
MaskLayerShape *masklay_shape, *masklay_shape_next;
|
|
|
|
bool is_double = false;
|
|
|
|
|
|
|
|
BLI_listbase_sort_r(&masklay->splines_shapes, masklay_shape_cmp_frame, &is_double);
|
|
|
|
|
|
|
|
if (is_double) {
|
|
|
|
for (masklay_shape = masklay->splines_shapes.first; masklay_shape;
|
|
|
|
masklay_shape = masklay_shape_next) {
|
|
|
|
masklay_shape_next = masklay_shape->next;
|
|
|
|
if (masklay_shape_next && masklay_shape->frame == masklay_shape_next->frame) {
|
|
|
|
BKE_mask_layer_shape_unlink(masklay, masklay_shape);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
for (masklay_shape = masklay->splines_shapes.first; masklay_shape;
|
|
|
|
masklay_shape = masklay_shape->next) {
|
|
|
|
BLI_assert(!masklay_shape->next || masklay_shape->frame < masklay_shape->next->frame);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Time + Average value */
|
|
|
|
typedef struct tRetainedKeyframe {
|
|
|
|
struct tRetainedKeyframe *next, *prev;
|
|
|
|
float frame; /* frame to cluster around */
|
|
|
|
float val; /* average value */
|
|
|
|
|
|
|
|
size_t tot_count; /* number of keyframes that have been averaged */
|
|
|
|
size_t del_count; /* number of keyframes of this sort that have been deleted so far */
|
|
|
|
} tRetainedKeyframe;
|
|
|
|
|
|
|
|
/* Called during special_aftertrans_update to make sure selected keyframes replace
|
|
|
|
* any other keyframes which may reside on that frame (that is not selected).
|
|
|
|
*/
|
|
|
|
static void posttrans_fcurve_clean(FCurve *fcu, const bool use_handle)
|
|
|
|
{
|
|
|
|
/* NOTE: We assume that all keys are sorted */
|
|
|
|
ListBase retained_keys = {NULL, NULL};
|
|
|
|
const bool can_average_points = ((fcu->flag & (FCURVE_INT_VALUES | FCURVE_DISCRETE_VALUES)) ==
|
|
|
|
0);
|
|
|
|
|
|
|
|
/* sanity checks */
|
|
|
|
if ((fcu->totvert == 0) || (fcu->bezt == NULL)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 1) Identify selected keyframes, and average the values on those
|
|
|
|
* in case there are collisions due to multiple keys getting scaled
|
|
|
|
* to all end up on the same frame
|
|
|
|
*/
|
|
|
|
for (int i = 0; i < fcu->totvert; i++) {
|
|
|
|
BezTriple *bezt = &fcu->bezt[i];
|
|
|
|
|
|
|
|
if (BEZT_ISSEL_ANY(bezt)) {
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
/* If there's another selected frame here, merge it */
|
|
|
|
for (tRetainedKeyframe *rk = retained_keys.last; rk; rk = rk->prev) {
|
|
|
|
if (IS_EQT(rk->frame, bezt->vec[1][0], BEZT_BINARYSEARCH_THRESH)) {
|
|
|
|
rk->val += bezt->vec[1][1];
|
|
|
|
rk->tot_count++;
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (rk->frame < bezt->vec[1][0]) {
|
|
|
|
/* Terminate early if have passed the supposed insertion point? */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If nothing found yet, create a new one */
|
|
|
|
if (found == false) {
|
|
|
|
tRetainedKeyframe *rk = MEM_callocN(sizeof(tRetainedKeyframe), "tRetainedKeyframe");
|
|
|
|
|
|
|
|
rk->frame = bezt->vec[1][0];
|
|
|
|
rk->val = bezt->vec[1][1];
|
|
|
|
rk->tot_count = 1;
|
|
|
|
|
|
|
|
BLI_addtail(&retained_keys, rk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (BLI_listbase_is_empty(&retained_keys)) {
|
|
|
|
/* This may happen if none of the points were selected... */
|
|
|
|
if (G.debug & G_DEBUG) {
|
|
|
|
printf("%s: nothing to do for FCurve %p (rna_path = '%s')\n", __func__, fcu, fcu->rna_path);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Compute the average values for each retained keyframe */
|
|
|
|
for (tRetainedKeyframe *rk = retained_keys.first; rk; rk = rk->next) {
|
|
|
|
rk->val = rk->val / (float)rk->tot_count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 2) Delete all keyframes duplicating the "retained keys" found above
|
|
|
|
* - Most of these will be unselected keyframes
|
|
|
|
* - Some will be selected keyframes though. For those, we only keep the last one
|
|
|
|
* (or else everything is gone), and replace its value with the averaged value.
|
|
|
|
*/
|
|
|
|
for (int i = fcu->totvert - 1; i >= 0; i--) {
|
|
|
|
BezTriple *bezt = &fcu->bezt[i];
|
|
|
|
|
|
|
|
/* Is this keyframe a candidate for deletion? */
|
|
|
|
/* TODO: Replace loop with an O(1) lookup instead */
|
|
|
|
for (tRetainedKeyframe *rk = retained_keys.last; rk; rk = rk->prev) {
|
|
|
|
if (IS_EQT(bezt->vec[1][0], rk->frame, BEZT_BINARYSEARCH_THRESH)) {
|
|
|
|
/* Selected keys are treated with greater care than unselected ones... */
|
|
|
|
if (BEZT_ISSEL_ANY(bezt)) {
|
|
|
|
/* - If this is the last selected key left (based on rk->del_count) ==> UPDATE IT
|
|
|
|
* (or else we wouldn't have any keyframe left here)
|
|
|
|
* - Otherwise, there are still other selected keyframes on this frame
|
|
|
|
* to be merged down still ==> DELETE IT
|
|
|
|
*/
|
|
|
|
if (rk->del_count == rk->tot_count - 1) {
|
|
|
|
/* Update keyframe... */
|
|
|
|
if (can_average_points) {
|
|
|
|
/* TODO: update handles too? */
|
|
|
|
bezt->vec[1][1] = rk->val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Delete Keyframe */
|
|
|
|
delete_fcurve_key(fcu, i, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update count of how many we've deleted
|
|
|
|
* - It should only matter that we're doing this for all but the last one
|
|
|
|
*/
|
|
|
|
rk->del_count++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Always delete - Unselected keys don't matter */
|
|
|
|
delete_fcurve_key(fcu, i, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop the RK search... we've found our match now */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 3) Recalculate handles */
|
|
|
|
testhandles_fcurve(fcu, use_handle);
|
|
|
|
|
|
|
|
/* cleanup */
|
|
|
|
BLI_freelistN(&retained_keys);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Called by special_aftertrans_update to make sure selected keyframes replace
|
|
|
|
* any other keyframes which may reside on that frame (that is not selected).
|
|
|
|
* remake_action_ipos should have already been called
|
|
|
|
*/
|
|
|
|
static void posttrans_action_clean(bAnimContext *ac, bAction *act)
|
|
|
|
{
|
|
|
|
ListBase anim_data = {NULL, NULL};
|
|
|
|
bAnimListElem *ale;
|
|
|
|
int filter;
|
|
|
|
|
|
|
|
/* filter data */
|
|
|
|
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/);
|
|
|
|
ANIM_animdata_filter(ac, &anim_data, filter, act, ANIMCONT_ACTION);
|
|
|
|
|
|
|
|
/* loop through relevant data, removing keyframes as appropriate
|
|
|
|
* - all keyframes are converted in/out of global time
|
|
|
|
*/
|
|
|
|
for (ale = anim_data.first; ale; ale = ale->next) {
|
|
|
|
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
|
|
|
|
|
|
|
|
if (adt) {
|
|
|
|
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
|
|
|
|
posttrans_fcurve_clean(ale->key_data, false); /* only use handles in graph editor */
|
|
|
|
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
posttrans_fcurve_clean(ale->key_data, false); /* only use handles in graph editor */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free temp data */
|
|
|
|
ANIM_animdata_freelist(&anim_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ********************* GRAPH EDITOR ************************* */
|
|
|
|
|
|
|
|
/* struct for use in re-sorting BezTriples during Graph Editor transform */
|
|
|
|
typedef struct BeztMap {
|
|
|
|
BezTriple *bezt;
|
|
|
|
unsigned int oldIndex; /* index of bezt in fcu->bezt array before sorting */
|
|
|
|
unsigned int newIndex; /* index of bezt in fcu->bezt array after sorting */
|
|
|
|
short swapHs; /* swap order of handles (-1=clear; 0=not checked, 1=swap) */
|
|
|
|
char pipo, cipo; /* interpolation of current and next segments */
|
|
|
|
} BeztMap;
|
|
|
|
|
|
|
|
/* This function converts an FCurve's BezTriple array to a BeztMap array
|
|
|
|
* NOTE: this allocates memory that will need to get freed later
|
|
|
|
*/
|
|
|
|
static BeztMap *bezt_to_beztmaps(BezTriple *bezts, int totvert, const short UNUSED(use_handle))
|
|
|
|
{
|
|
|
|
BezTriple *bezt = bezts;
|
|
|
|
BezTriple *prevbezt = NULL;
|
|
|
|
BeztMap *bezm, *bezms;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* allocate memory for this array */
|
|
|
|
if (totvert == 0 || bezts == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
bezm = bezms = MEM_callocN(sizeof(BeztMap) * totvert, "BeztMaps");
|
|
|
|
|
|
|
|
/* assign beztriples to beztmaps */
|
|
|
|
for (i = 0; i < totvert; i++, bezm++, prevbezt = bezt, bezt++) {
|
|
|
|
bezm->bezt = bezt;
|
|
|
|
|
|
|
|
bezm->oldIndex = i;
|
|
|
|
bezm->newIndex = i;
|
|
|
|
|
|
|
|
bezm->pipo = (prevbezt) ? prevbezt->ipo : bezt->ipo;
|
|
|
|
bezm->cipo = bezt->ipo;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bezms;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This function copies the code of sort_time_ipocurve, but acts on BeztMap structs instead */
|
|
|
|
static void sort_time_beztmaps(BeztMap *bezms, int totvert, const short UNUSED(use_handle))
|
|
|
|
{
|
|
|
|
BeztMap *bezm;
|
|
|
|
int i, ok = 1;
|
|
|
|
|
|
|
|
/* keep repeating the process until nothing is out of place anymore */
|
|
|
|
while (ok) {
|
|
|
|
ok = 0;
|
|
|
|
|
|
|
|
bezm = bezms;
|
|
|
|
i = totvert;
|
|
|
|
while (i--) {
|
|
|
|
/* is current bezm out of order (i.e. occurs later than next)? */
|
|
|
|
if (i > 0) {
|
|
|
|
if (bezm->bezt->vec[1][0] > (bezm + 1)->bezt->vec[1][0]) {
|
|
|
|
bezm->newIndex++;
|
|
|
|
(bezm + 1)->newIndex--;
|
|
|
|
|
|
|
|
SWAP(BeztMap, *bezm, *(bezm + 1));
|
|
|
|
|
|
|
|
ok = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* do we need to check if the handles need to be swapped?
|
|
|
|
* optimization: this only needs to be performed in the first loop
|
|
|
|
*/
|
|
|
|
if (bezm->swapHs == 0) {
|
|
|
|
if ((bezm->bezt->vec[0][0] > bezm->bezt->vec[1][0]) &&
|
|
|
|
(bezm->bezt->vec[2][0] < bezm->bezt->vec[1][0])) {
|
|
|
|
/* handles need to be swapped */
|
|
|
|
bezm->swapHs = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* handles need to be cleared */
|
|
|
|
bezm->swapHs = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bezm++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This function firstly adjusts the pointers that the transdata has to each BezTriple */
|
|
|
|
static void beztmap_to_data(
|
|
|
|
TransInfo *t, FCurve *fcu, BeztMap *bezms, int totvert, const short UNUSED(use_handle))
|
|
|
|
{
|
|
|
|
BezTriple *bezts = fcu->bezt;
|
|
|
|
BeztMap *bezm;
|
|
|
|
TransData2D *td2d;
|
|
|
|
TransData *td;
|
|
|
|
int i, j;
|
|
|
|
char *adjusted;
|
|
|
|
|
|
|
|
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
|
|
|
|
|
|
|
/* dynamically allocate an array of chars to mark whether an TransData's
|
|
|
|
* pointers have been fixed already, so that we don't override ones that are
|
|
|
|
* already done
|
|
|
|
*/
|
|
|
|
adjusted = MEM_callocN(tc->data_len, "beztmap_adjusted_map");
|
|
|
|
|
|
|
|
/* for each beztmap item, find if it is used anywhere */
|
|
|
|
bezm = bezms;
|
|
|
|
for (i = 0; i < totvert; i++, bezm++) {
|
|
|
|
/* loop through transdata, testing if we have a hit
|
|
|
|
* for the handles (vec[0]/vec[2]), we must also check if they need to be swapped...
|
|
|
|
*/
|
|
|
|
td2d = tc->data_2d;
|
|
|
|
td = tc->data;
|
|
|
|
for (j = 0; j < tc->data_len; j++, td2d++, td++) {
|
|
|
|
/* skip item if already marked */
|
|
|
|
if (adjusted[j] != 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update all transdata pointers, no need to check for selections etc,
|
|
|
|
* since only points that are really needed were created as transdata
|
|
|
|
*/
|
|
|
|
if (td2d->loc2d == bezm->bezt->vec[0]) {
|
|
|
|
if (bezm->swapHs == 1) {
|
|
|
|
td2d->loc2d = (bezts + bezm->newIndex)->vec[2];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
td2d->loc2d = (bezts + bezm->newIndex)->vec[0];
|
|
|
|
}
|
|
|
|
adjusted[j] = 1;
|
|
|
|
}
|
|
|
|
else if (td2d->loc2d == bezm->bezt->vec[2]) {
|
|
|
|
if (bezm->swapHs == 1) {
|
|
|
|
td2d->loc2d = (bezts + bezm->newIndex)->vec[0];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
td2d->loc2d = (bezts + bezm->newIndex)->vec[2];
|
|
|
|
}
|
|
|
|
adjusted[j] = 1;
|
|
|
|
}
|
|
|
|
else if (td2d->loc2d == bezm->bezt->vec[1]) {
|
|
|
|
td2d->loc2d = (bezts + bezm->newIndex)->vec[1];
|
|
|
|
|
|
|
|
/* if only control point is selected, the handle pointers need to be updated as well */
|
|
|
|
if (td2d->h1) {
|
|
|
|
td2d->h1 = (bezts + bezm->newIndex)->vec[0];
|
|
|
|
}
|
|
|
|
if (td2d->h2) {
|
|
|
|
td2d->h2 = (bezts + bezm->newIndex)->vec[2];
|
|
|
|
}
|
|
|
|
|
|
|
|
adjusted[j] = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the handle type pointer has to be updated too */
|
|
|
|
if (adjusted[j] && td->flag & TD_BEZTRIPLE && td->hdata) {
|
|
|
|
if (bezm->swapHs == 1) {
|
|
|
|
td->hdata->h1 = &(bezts + bezm->newIndex)->h2;
|
|
|
|
td->hdata->h2 = &(bezts + bezm->newIndex)->h1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
td->hdata->h1 = &(bezts + bezm->newIndex)->h1;
|
|
|
|
td->hdata->h2 = &(bezts + bezm->newIndex)->h2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free temp memory used for 'adjusted' array */
|
|
|
|
MEM_freeN(adjusted);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This function is called by recalcData during the Transform loop to recalculate
|
|
|
|
* the handles of curves and sort the keyframes so that the curves draw correctly.
|
|
|
|
* It is only called if some keyframes have moved out of order.
|
|
|
|
*
|
|
|
|
* anim_data is the list of channels (F-Curves) retrieved already containing the
|
|
|
|
* channels to work on. It should not be freed here as it may still need to be used.
|
|
|
|
*/
|
|
|
|
void remake_graph_transdata(TransInfo *t, ListBase *anim_data)
|
|
|
|
{
|
|
|
|
SpaceGraph *sipo = (SpaceGraph *)t->sa->spacedata.first;
|
|
|
|
bAnimListElem *ale;
|
|
|
|
const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0;
|
|
|
|
|
|
|
|
/* sort and reassign verts */
|
|
|
|
for (ale = anim_data->first; ale; ale = ale->next) {
|
|
|
|
FCurve *fcu = (FCurve *)ale->key_data;
|
|
|
|
|
|
|
|
if (fcu->bezt) {
|
|
|
|
BeztMap *bezm;
|
|
|
|
|
|
|
|
/* adjust transform-data pointers */
|
|
|
|
/* note, none of these functions use 'use_handle', it could be removed */
|
|
|
|
bezm = bezt_to_beztmaps(fcu->bezt, fcu->totvert, use_handle);
|
|
|
|
sort_time_beztmaps(bezm, fcu->totvert, use_handle);
|
|
|
|
beztmap_to_data(t, fcu, bezm, fcu->totvert, use_handle);
|
|
|
|
|
|
|
|
/* free mapping stuff */
|
|
|
|
MEM_freeN(bezm);
|
|
|
|
|
|
|
|
/* re-sort actual beztriples (perhaps this could be done using the beztmaps to save time?) */
|
|
|
|
sort_time_fcurve(fcu);
|
|
|
|
|
|
|
|
/* make sure handles are all set correctly */
|
|
|
|
testhandles_fcurve(fcu, use_handle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* *********************** Transform data ******************* */
|
|
|
|
|
|
|
|
/* Little helper function for ObjectToTransData used to give certain
|
|
|
|
* constraints (ChildOf, FollowPath, and others that may be added)
|
|
|
|
* inverse corrections for transform, so that they aren't in CrazySpace.
|
|
|
|
* These particular constraints benefit from this, but others don't, hence
|
|
|
|
* this semi-hack ;-) - Aligorith
|
|
|
|
*/
|
|
|
|
bool constraints_list_needinv(TransInfo *t, ListBase *list)
|
|
|
|
{
|
|
|
|
bConstraint *con;
|
|
|
|
|
|
|
|
/* loop through constraints, checking if there's one of the mentioned
|
|
|
|
* constraints needing special crazyspace corrections
|
|
|
|
*/
|
|
|
|
if (list) {
|
|
|
|
for (con = list->first; con; con = con->next) {
|
|
|
|
/* only consider constraint if it is enabled, and has influence on result */
|
|
|
|
if ((con->flag & CONSTRAINT_DISABLE) == 0 && (con->enforce != 0.0f)) {
|
|
|
|
/* (affirmative) returns for specific constraints here... */
|
|
|
|
/* constraints that require this regardless */
|
|
|
|
if (ELEM(con->type,
|
|
|
|
CONSTRAINT_TYPE_FOLLOWPATH,
|
|
|
|
CONSTRAINT_TYPE_CLAMPTO,
|
|
|
|
CONSTRAINT_TYPE_ARMATURE,
|
|
|
|
CONSTRAINT_TYPE_OBJECTSOLVER,
|
|
|
|
CONSTRAINT_TYPE_FOLLOWTRACK)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* constraints that require this only under special conditions */
|
|
|
|
if (con->type == CONSTRAINT_TYPE_CHILDOF) {
|
|
|
|
/* ChildOf constraint only works when using all location components, see T42256. */
|
|
|
|
bChildOfConstraint *data = (bChildOfConstraint *)con->data;
|
|
|
|
|
|
|
|
if ((data->flag & CHILDOF_LOCX) && (data->flag & CHILDOF_LOCY) &&
|
|
|
|
(data->flag & CHILDOF_LOCZ)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (con->type == CONSTRAINT_TYPE_ROTLIKE) {
|
|
|
|
/* CopyRot constraint only does this when rotating, and offset is on */
|
|
|
|
bRotateLikeConstraint *data = (bRotateLikeConstraint *)con->data;
|
|
|
|
|
2019-09-04 12:06:59 +03:00
|
|
|
if (ELEM(data->mix_mode, ROTLIKE_MIX_OFFSET, ROTLIKE_MIX_BEFORE) &&
|
|
|
|
ELEM(t->mode, TFM_ROTATION)) {
|
2019-09-05 14:34:54 -03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (con->type == CONSTRAINT_TYPE_TRANSFORM) {
|
|
|
|
/* Transform constraint needs it for rotation at least (r.57309),
|
|
|
|
* but doing so when translating may also mess things up [#36203]
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (t->mode == TFM_ROTATION) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/* ??? (t->mode == TFM_SCALE) ? */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no appropriate candidates found */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Auto-keyframing feature - for objects
|
|
|
|
*
|
|
|
|
* \param tmode: A transform mode.
|
|
|
|
*
|
|
|
|
* \note Context may not always be available,
|
|
|
|
* so must check before using it as it's a luxury for a few cases.
|
|
|
|
*/
|
|
|
|
void autokeyframe_object(bContext *C, Scene *scene, ViewLayer *view_layer, Object *ob, int tmode)
|
|
|
|
{
|
|
|
|
Main *bmain = CTX_data_main(C);
|
|
|
|
ID *id = &ob->id;
|
|
|
|
FCurve *fcu;
|
|
|
|
|
|
|
|
// TODO: this should probably be done per channel instead...
|
|
|
|
if (autokeyframe_cfra_can_key(scene, id)) {
|
|
|
|
ReportList *reports = CTX_wm_reports(C);
|
|
|
|
ToolSettings *ts = scene->toolsettings;
|
|
|
|
KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene);
|
|
|
|
ListBase dsources = {NULL, NULL};
|
|
|
|
float cfra = (float)CFRA; // xxx this will do for now
|
|
|
|
short flag = 0;
|
|
|
|
|
|
|
|
/* get flags used for inserting keyframes */
|
|
|
|
flag = ANIM_get_keyframing_flags(scene, 1);
|
|
|
|
|
|
|
|
/* add datasource override for the object */
|
|
|
|
ANIM_relative_keyingset_add_source(&dsources, id, NULL, NULL);
|
|
|
|
|
|
|
|
if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) {
|
|
|
|
/* Only insert into active keyingset
|
|
|
|
* NOTE: we assume here that the active Keying Set
|
|
|
|
* does not need to have its iterator overridden.
|
|
|
|
*/
|
|
|
|
ANIM_apply_keyingset(C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, cfra);
|
|
|
|
}
|
|
|
|
else if (IS_AUTOKEY_FLAG(scene, INSERTAVAIL)) {
|
|
|
|
AnimData *adt = ob->adt;
|
|
|
|
|
|
|
|
/* only key on available channels */
|
|
|
|
if (adt && adt->action) {
|
|
|
|
ListBase nla_cache = {NULL, NULL};
|
|
|
|
for (fcu = adt->action->curves.first; fcu; fcu = fcu->next) {
|
|
|
|
fcu->flag &= ~FCURVE_SELECTED;
|
|
|
|
insert_keyframe(bmain,
|
|
|
|
reports,
|
|
|
|
id,
|
|
|
|
adt->action,
|
|
|
|
(fcu->grp ? fcu->grp->name : NULL),
|
|
|
|
fcu->rna_path,
|
|
|
|
fcu->array_index,
|
|
|
|
cfra,
|
|
|
|
ts->keyframe_type,
|
|
|
|
&nla_cache,
|
|
|
|
flag);
|
|
|
|
}
|
|
|
|
|
|
|
|
BKE_animsys_free_nla_keyframing_context_cache(&nla_cache);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (IS_AUTOKEY_FLAG(scene, INSERTNEEDED)) {
|
|
|
|
bool do_loc = false, do_rot = false, do_scale = false;
|
|
|
|
|
|
|
|
/* filter the conditions when this happens (assume that curarea->spacetype==SPACE_VIE3D) */
|
|
|
|
if (tmode == TFM_TRANSLATION) {
|
|
|
|
do_loc = true;
|
|
|
|
}
|
|
|
|
else if (ELEM(tmode, TFM_ROTATION, TFM_TRACKBALL)) {
|
|
|
|
if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) {
|
|
|
|
if (ob != OBACT(view_layer)) {
|
|
|
|
do_loc = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) {
|
|
|
|
do_loc = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) {
|
|
|
|
do_rot = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tmode == TFM_RESIZE) {
|
|
|
|
if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) {
|
|
|
|
if (ob != OBACT(view_layer)) {
|
|
|
|
do_loc = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) {
|
|
|
|
do_loc = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) {
|
|
|
|
do_scale = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* insert keyframes for the affected sets of channels using the builtin KeyingSets found */
|
|
|
|
if (do_loc) {
|
|
|
|
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOCATION_ID);
|
|
|
|
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
|
|
|
|
}
|
|
|
|
if (do_rot) {
|
|
|
|
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_ROTATION_ID);
|
|
|
|
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
|
|
|
|
}
|
|
|
|
if (do_scale) {
|
|
|
|
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_SCALING_ID);
|
|
|
|
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* insert keyframe in all (transform) channels */
|
|
|
|
else {
|
|
|
|
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID);
|
|
|
|
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free temp info */
|
|
|
|
BLI_freelistN(&dsources);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return if we need to update motion paths, only if they already exist,
|
|
|
|
* and we will insert a keyframe at the end of transform. */
|
|
|
|
bool motionpath_need_update_object(Scene *scene, Object *ob)
|
|
|
|
{
|
|
|
|
/* XXX: there's potential here for problems with unkeyed rotations/scale,
|
|
|
|
* but for now (until proper data-locality for baking operations),
|
|
|
|
* this should be a better fix for T24451 and T37755
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (autokeyframe_cfra_can_key(scene, &ob->id)) {
|
|
|
|
return (ob->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Auto-keyframing feature - for poses/pose-channels
|
|
|
|
*
|
|
|
|
* \param tmode: A transform mode.
|
|
|
|
*
|
|
|
|
* targetless_ik: has targetless ik been done on any channels?
|
|
|
|
*
|
|
|
|
* \note Context may not always be available,
|
|
|
|
* so must check before using it as it's a luxury for a few cases.
|
|
|
|
*/
|
|
|
|
void autokeyframe_pose(bContext *C, Scene *scene, Object *ob, int tmode, short targetless_ik)
|
|
|
|
{
|
|
|
|
Main *bmain = CTX_data_main(C);
|
|
|
|
ID *id = &ob->id;
|
|
|
|
AnimData *adt = ob->adt;
|
|
|
|
bAction *act = (adt) ? adt->action : NULL;
|
|
|
|
bPose *pose = ob->pose;
|
|
|
|
bPoseChannel *pchan;
|
|
|
|
FCurve *fcu;
|
|
|
|
|
|
|
|
// TODO: this should probably be done per channel instead...
|
|
|
|
if (autokeyframe_cfra_can_key(scene, id)) {
|
|
|
|
ReportList *reports = CTX_wm_reports(C);
|
|
|
|
ToolSettings *ts = scene->toolsettings;
|
|
|
|
KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene);
|
|
|
|
ListBase nla_cache = {NULL, NULL};
|
|
|
|
float cfra = (float)CFRA;
|
|
|
|
short flag = 0;
|
|
|
|
|
|
|
|
/* flag is initialized from UserPref keyframing settings
|
|
|
|
* - special exception for targetless IK - INSERTKEY_MATRIX keyframes should get
|
|
|
|
* visual keyframes even if flag not set, as it's not that useful otherwise
|
|
|
|
* (for quick animation recording)
|
|
|
|
*/
|
|
|
|
flag = ANIM_get_keyframing_flags(scene, 1);
|
|
|
|
|
|
|
|
if (targetless_ik) {
|
|
|
|
flag |= INSERTKEY_MATRIX;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
|
|
|
|
if (pchan->bone->flag & (BONE_TRANSFORM | BONE_TRANSFORM_MIRROR)) {
|
|
|
|
|
|
|
|
ListBase dsources = {NULL, NULL};
|
|
|
|
|
|
|
|
/* clear any 'unkeyed' flag it may have */
|
|
|
|
pchan->bone->flag &= ~BONE_UNKEYED;
|
|
|
|
|
|
|
|
/* add datasource override for the camera object */
|
|
|
|
ANIM_relative_keyingset_add_source(&dsources, id, &RNA_PoseBone, pchan);
|
|
|
|
|
|
|
|
/* only insert into active keyingset? */
|
|
|
|
if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) {
|
|
|
|
/* run the active Keying Set on the current datasource */
|
|
|
|
ANIM_apply_keyingset(C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, cfra);
|
|
|
|
}
|
|
|
|
/* only insert into available channels? */
|
|
|
|
else if (IS_AUTOKEY_FLAG(scene, INSERTAVAIL)) {
|
|
|
|
if (act) {
|
|
|
|
for (fcu = act->curves.first; fcu; fcu = fcu->next) {
|
|
|
|
/* only insert keyframes for this F-Curve if it affects the current bone */
|
|
|
|
if (strstr(fcu->rna_path, "bones")) {
|
|
|
|
char *pchanName = BLI_str_quoted_substrN(fcu->rna_path, "bones[");
|
|
|
|
|
|
|
|
/* only if bone name matches too...
|
|
|
|
* NOTE: this will do constraints too, but those are ok to do here too?
|
|
|
|
*/
|
|
|
|
if (pchanName && STREQ(pchanName, pchan->name)) {
|
|
|
|
insert_keyframe(bmain,
|
|
|
|
reports,
|
|
|
|
id,
|
|
|
|
act,
|
|
|
|
((fcu->grp) ? (fcu->grp->name) : (NULL)),
|
|
|
|
fcu->rna_path,
|
|
|
|
fcu->array_index,
|
|
|
|
cfra,
|
|
|
|
ts->keyframe_type,
|
|
|
|
&nla_cache,
|
|
|
|
flag);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pchanName) {
|
|
|
|
MEM_freeN(pchanName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* only insert keyframe if needed? */
|
|
|
|
else if (IS_AUTOKEY_FLAG(scene, INSERTNEEDED)) {
|
|
|
|
bool do_loc = false, do_rot = false, do_scale = false;
|
|
|
|
|
|
|
|
/* Filter the conditions when this happens
|
|
|
|
* (assume that 'curarea->spacetype == SPACE_VIEW3D'). */
|
|
|
|
if (tmode == TFM_TRANSLATION) {
|
|
|
|
if (targetless_ik) {
|
|
|
|
do_rot = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
do_loc = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ELEM(tmode, TFM_ROTATION, TFM_TRACKBALL)) {
|
|
|
|
if (ELEM(scene->toolsettings->transform_pivot_point,
|
|
|
|
V3D_AROUND_CURSOR,
|
|
|
|
V3D_AROUND_ACTIVE)) {
|
|
|
|
do_loc = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) {
|
|
|
|
do_rot = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tmode == TFM_RESIZE) {
|
|
|
|
if (ELEM(scene->toolsettings->transform_pivot_point,
|
|
|
|
V3D_AROUND_CURSOR,
|
|
|
|
V3D_AROUND_ACTIVE)) {
|
|
|
|
do_loc = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) {
|
|
|
|
do_scale = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (do_loc) {
|
|
|
|
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOCATION_ID);
|
|
|
|
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
|
|
|
|
}
|
|
|
|
if (do_rot) {
|
|
|
|
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_ROTATION_ID);
|
|
|
|
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
|
|
|
|
}
|
|
|
|
if (do_scale) {
|
|
|
|
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_SCALING_ID);
|
|
|
|
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* insert keyframe in all (transform) channels */
|
|
|
|
else {
|
|
|
|
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID);
|
|
|
|
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free temp info */
|
|
|
|
BLI_freelistN(&dsources);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BKE_animsys_free_nla_keyframing_context_cache(&nla_cache);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* tag channels that should have unkeyed data */
|
|
|
|
for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
|
|
|
|
if (pchan->bone->flag & BONE_TRANSFORM) {
|
|
|
|
/* tag this channel */
|
|
|
|
pchan->bone->flag |= BONE_UNKEYED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return if we need to update motion paths, only if they already exist,
|
|
|
|
* and we will insert a keyframe at the end of transform. */
|
|
|
|
bool motionpath_need_update_pose(Scene *scene, Object *ob)
|
|
|
|
{
|
|
|
|
if (autokeyframe_cfra_can_key(scene, &ob->id)) {
|
|
|
|
return (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void special_aftertrans_update__movieclip(bContext *C, TransInfo *t)
|
|
|
|
{
|
|
|
|
SpaceClip *sc = t->sa->spacedata.first;
|
|
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
|
|
ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(&clip->tracking);
|
|
|
|
const int framenr = ED_space_clip_get_clip_frame_number(sc);
|
|
|
|
/* Update coordinates of modified plane tracks. */
|
|
|
|
for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; plane_track;
|
|
|
|
plane_track = plane_track->next) {
|
|
|
|
bool do_update = false;
|
|
|
|
if (plane_track->flag & PLANE_TRACK_HIDDEN) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
do_update |= PLANE_TRACK_VIEW_SELECTED(plane_track) != 0;
|
|
|
|
if (do_update == false) {
|
|
|
|
if ((plane_track->flag & PLANE_TRACK_AUTOKEY) == 0) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < plane_track->point_tracksnr; i++) {
|
|
|
|
MovieTrackingTrack *track = plane_track->point_tracks[i];
|
|
|
|
if (TRACK_VIEW_SELECTED(sc, track)) {
|
|
|
|
do_update = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (do_update) {
|
|
|
|
BKE_tracking_track_plane_from_existing_motion(plane_track, framenr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (t->scene->nodetree != NULL) {
|
|
|
|
/* Tracks can be used for stabilization nodes,
|
|
|
|
* flush update for such nodes.
|
|
|
|
*/
|
|
|
|
nodeUpdateID(t->scene->nodetree, &clip->id);
|
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void special_aftertrans_update__mask(bContext *C, TransInfo *t)
|
|
|
|
{
|
|
|
|
Mask *mask = NULL;
|
|
|
|
|
|
|
|
if (t->spacetype == SPACE_CLIP) {
|
|
|
|
SpaceClip *sc = t->sa->spacedata.first;
|
|
|
|
mask = ED_space_clip_get_mask(sc);
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_IMAGE) {
|
|
|
|
SpaceImage *sima = t->sa->spacedata.first;
|
|
|
|
mask = ED_space_image_get_mask(sima);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
BLI_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t->scene->nodetree) {
|
|
|
|
/* tracks can be used for stabilization nodes,
|
|
|
|
* flush update for such nodes */
|
|
|
|
// if (nodeUpdateID(t->scene->nodetree, &mask->id))
|
|
|
|
{
|
|
|
|
WM_event_add_notifier(C, NC_MASK | ND_DATA, &mask->id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO - dont key all masks... */
|
|
|
|
if (IS_AUTOKEY_ON(t->scene)) {
|
|
|
|
Scene *scene = t->scene;
|
|
|
|
|
|
|
|
ED_mask_layer_shape_auto_key_select(mask, CFRA);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void special_aftertrans_update__node(bContext *C, TransInfo *t)
|
|
|
|
{
|
|
|
|
Main *bmain = CTX_data_main(C);
|
|
|
|
const bool canceled = (t->state == TRANS_CANCEL);
|
|
|
|
|
|
|
|
if (canceled && t->remove_on_cancel) {
|
|
|
|
/* remove selected nodes on cancel */
|
|
|
|
SpaceNode *snode = (SpaceNode *)t->sa->spacedata.first;
|
|
|
|
bNodeTree *ntree = snode->edittree;
|
|
|
|
if (ntree) {
|
|
|
|
bNode *node, *node_next;
|
|
|
|
for (node = ntree->nodes.first; node; node = node_next) {
|
|
|
|
node_next = node->next;
|
|
|
|
if (node->flag & NODE_SELECT) {
|
|
|
|
nodeRemoveNode(bmain, ntree, node, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t)
|
|
|
|
{
|
|
|
|
/* so automerge supports mirror */
|
|
|
|
if ((t->scene->toolsettings->automerge) && ((t->flag & T_EDIT) && t->obedit_type == OB_MESH)) {
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
|
|
|
|
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
|
|
|
|
BMesh *bm = em->bm;
|
|
|
|
char hflag;
|
|
|
|
bool has_face_sel = (bm->totfacesel != 0);
|
|
|
|
|
|
|
|
if (tc->mirror.axis_flag) {
|
|
|
|
TransData *td;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Rather then adjusting the selection (which the user would notice)
|
|
|
|
* tag all mirrored verts, then auto-merge those. */
|
|
|
|
BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false);
|
|
|
|
|
|
|
|
for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
|
|
|
|
if (td->extra) {
|
|
|
|
BM_elem_flag_enable((BMVert *)td->extra, BM_ELEM_TAG);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hflag = BM_ELEM_SELECT | BM_ELEM_TAG;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
hflag = BM_ELEM_SELECT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t->scene->toolsettings->automerge & AUTO_MERGE) {
|
|
|
|
if (t->scene->toolsettings->automerge & AUTO_MERGE_AND_SPLIT) {
|
|
|
|
EDBM_automerge_and_split(
|
|
|
|
tc->obedit, true, true, true, hflag, t->scene->toolsettings->doublimit);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
EDBM_automerge(tc->obedit, true, hflag, t->scene->toolsettings->doublimit);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Special case, this is needed or faces won't re-select.
|
|
|
|
* Flush selected edges to faces. */
|
|
|
|
if (has_face_sel && (em->selectmode == SCE_SELECT_FACE)) {
|
|
|
|
EDBM_selectmode_flush_ex(em, SCE_SELECT_EDGE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* inserting keys, pointcache, redraw events... */
|
|
|
|
/**
|
|
|
|
* \note Sequencer freeing has its own function now because of a conflict
|
|
|
|
* with transform's order of freeing (campbell).
|
|
|
|
* Order changed, the sequencer stuff should go back in here
|
|
|
|
*/
|
|
|
|
void special_aftertrans_update(bContext *C, TransInfo *t)
|
|
|
|
{
|
|
|
|
Main *bmain = CTX_data_main(t->context);
|
|
|
|
BLI_assert(bmain == CTX_data_main(C));
|
|
|
|
|
|
|
|
Object *ob;
|
|
|
|
// short redrawipo=0, resetslowpar=1;
|
|
|
|
const bool canceled = (t->state == TRANS_CANCEL);
|
|
|
|
const bool duplicate = (t->mode == TFM_TIME_DUPLICATE);
|
|
|
|
|
|
|
|
/* early out when nothing happened */
|
|
|
|
if (t->data_len_all == 0 || t->mode == TFM_DUMMY) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t->spacetype == SPACE_VIEW3D) {
|
|
|
|
if (t->flag & T_EDIT) {
|
|
|
|
/* Special Exception:
|
|
|
|
* We don't normally access 't->custom.mode' here, but its needed in this case. */
|
|
|
|
|
|
|
|
if (canceled == 0) {
|
|
|
|
/* we need to delete the temporary faces before automerging */
|
|
|
|
if (t->mode == TFM_EDGE_SLIDE) {
|
|
|
|
/* handle multires re-projection, done
|
|
|
|
* on transform completion since it's
|
|
|
|
* really slow -joeedh */
|
|
|
|
projectEdgeSlideData(t, true);
|
|
|
|
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
EdgeSlideData *sld = tc->custom.mode.data;
|
|
|
|
|
|
|
|
if (sld == NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free temporary faces to avoid auto-merging and deleting
|
|
|
|
* during cleanup - psy-fi. */
|
|
|
|
freeEdgeSlideTempFaces(sld);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->mode == TFM_VERT_SLIDE) {
|
|
|
|
/* as above */
|
|
|
|
projectVertSlideData(t, true);
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
VertSlideData *sld = tc->custom.mode.data;
|
|
|
|
freeVertSlideTempFaces(sld);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t->obedit_type == OB_MESH) {
|
|
|
|
special_aftertrans_update__mesh(C, t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (t->mode == TFM_EDGE_SLIDE) {
|
|
|
|
EdgeSlideParams *slp = t->custom.mode.data;
|
|
|
|
slp->perc = 0.0;
|
|
|
|
projectEdgeSlideData(t, false);
|
|
|
|
}
|
|
|
|
else if (t->mode == TFM_VERT_SLIDE) {
|
|
|
|
EdgeSlideParams *slp = t->custom.mode.data;
|
|
|
|
slp->perc = 0.0;
|
|
|
|
projectVertSlideData(t, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t->options & CTX_GPENCIL_STROKES) {
|
|
|
|
/* pass */
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_SEQ) {
|
|
|
|
/* freeSeqData in transform_conversions.c does this
|
|
|
|
* keep here so the else at the end wont run... */
|
|
|
|
|
|
|
|
SpaceSeq *sseq = (SpaceSeq *)t->sa->spacedata.first;
|
|
|
|
|
|
|
|
/* marker transform, not especially nice but we may want to move markers
|
|
|
|
* at the same time as keyframes in the dope sheet. */
|
|
|
|
if ((sseq->flag & SEQ_MARKER_TRANS) && (canceled == 0)) {
|
|
|
|
/* cant use TFM_TIME_EXTEND
|
|
|
|
* for some reason EXTEND is changed into TRANSLATE, so use frame_side instead */
|
|
|
|
|
|
|
|
if (t->mode == TFM_SEQ_SLIDE) {
|
|
|
|
if (t->frame_side == 'B') {
|
|
|
|
ED_markers_post_apply_transform(
|
|
|
|
&t->scene->markers, t->scene, TFM_TIME_TRANSLATE, t->values[0], t->frame_side);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ELEM(t->frame_side, 'L', 'R')) {
|
|
|
|
ED_markers_post_apply_transform(
|
|
|
|
&t->scene->markers, t->scene, TFM_TIME_EXTEND, t->values[0], t->frame_side);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_IMAGE) {
|
|
|
|
if (t->options & CTX_MASK) {
|
|
|
|
special_aftertrans_update__mask(C, t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_NODE) {
|
|
|
|
SpaceNode *snode = (SpaceNode *)t->sa->spacedata.first;
|
|
|
|
special_aftertrans_update__node(C, t);
|
|
|
|
if (canceled == 0) {
|
|
|
|
ED_node_post_apply_transform(C, snode->edittree);
|
|
|
|
|
|
|
|
ED_node_link_insert(bmain, t->sa);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* clear link line */
|
|
|
|
ED_node_link_intersect_test(t->sa, 0);
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_CLIP) {
|
|
|
|
if (t->options & CTX_MOVIECLIP) {
|
|
|
|
special_aftertrans_update__movieclip(C, t);
|
|
|
|
}
|
|
|
|
else if (t->options & CTX_MASK) {
|
|
|
|
special_aftertrans_update__mask(C, t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_ACTION) {
|
|
|
|
SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
|
|
|
|
bAnimContext ac;
|
|
|
|
|
|
|
|
/* initialize relevant anim-context 'context' data */
|
|
|
|
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ob = ac.obact;
|
|
|
|
|
|
|
|
if (ELEM(ac.datatype, ANIMCONT_DOPESHEET, ANIMCONT_SHAPEKEY, ANIMCONT_TIMELINE)) {
|
|
|
|
ListBase anim_data = {NULL, NULL};
|
|
|
|
bAnimListElem *ale;
|
|
|
|
short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/);
|
|
|
|
|
|
|
|
/* get channels to work on */
|
|
|
|
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
|
|
|
|
|
|
|
|
/* these should all be F-Curves */
|
|
|
|
for (ale = anim_data.first; ale; ale = ale->next) {
|
|
|
|
AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
|
|
|
|
FCurve *fcu = (FCurve *)ale->key_data;
|
|
|
|
|
|
|
|
/* 3 cases here for curve cleanups:
|
|
|
|
* 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done
|
|
|
|
* 2) canceled == 0 -> user confirmed the transform,
|
|
|
|
* so duplicates should be removed
|
|
|
|
* 3) canceled + duplicate -> user canceled the transform,
|
|
|
|
* but we made duplicates, so get rid of these
|
|
|
|
*/
|
|
|
|
if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) {
|
|
|
|
if (adt) {
|
|
|
|
ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0);
|
|
|
|
posttrans_fcurve_clean(fcu, false); /* only use handles in graph editor */
|
|
|
|
ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
posttrans_fcurve_clean(fcu, false); /* only use handles in graph editor */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free temp memory */
|
|
|
|
ANIM_animdata_freelist(&anim_data);
|
|
|
|
}
|
|
|
|
else if (ac.datatype == ANIMCONT_ACTION) { // TODO: just integrate into the above...
|
|
|
|
/* Depending on the lock status, draw necessary views */
|
|
|
|
// fixme... some of this stuff is not good
|
|
|
|
if (ob) {
|
|
|
|
if (ob->pose || BKE_key_from_object(ob)) {
|
|
|
|
DEG_id_tag_update(&ob->id,
|
|
|
|
ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 3 cases here for curve cleanups:
|
|
|
|
* 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done
|
|
|
|
* 2) canceled == 0 -> user confirmed the transform,
|
|
|
|
* so duplicates should be removed.
|
|
|
|
* 3) canceled + duplicate -> user canceled the transform,
|
|
|
|
* but we made duplicates, so get rid of these.
|
|
|
|
*/
|
|
|
|
if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) {
|
|
|
|
posttrans_action_clean(&ac, (bAction *)ac.data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ac.datatype == ANIMCONT_GPENCIL) {
|
|
|
|
/* remove duplicate frames and also make sure points are in order! */
|
|
|
|
/* 3 cases here for curve cleanups:
|
|
|
|
* 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done
|
|
|
|
* 2) canceled == 0 -> user confirmed the transform,
|
|
|
|
* so duplicates should be removed
|
|
|
|
* 3) canceled + duplicate -> user canceled the transform,
|
|
|
|
* but we made duplicates, so get rid of these
|
|
|
|
*/
|
|
|
|
if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) {
|
|
|
|
bGPdata *gpd;
|
|
|
|
|
|
|
|
// XXX: BAD! this get gpencil datablocks directly from main db...
|
|
|
|
// but that's how this currently works :/
|
|
|
|
for (gpd = bmain->gpencils.first; gpd; gpd = gpd->id.next) {
|
|
|
|
if (ID_REAL_USERS(gpd)) {
|
|
|
|
posttrans_gpd_clean(gpd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ac.datatype == ANIMCONT_MASK) {
|
|
|
|
/* remove duplicate frames and also make sure points are in order! */
|
|
|
|
/* 3 cases here for curve cleanups:
|
|
|
|
* 1) NOTRANSKEYCULL on:
|
|
|
|
* Cleanup of duplicates shouldn't be done.
|
|
|
|
* 2) canceled == 0:
|
|
|
|
* User confirmed the transform, so duplicates should be removed.
|
|
|
|
* 3) Canceled + duplicate:
|
|
|
|
* User canceled the transform, but we made duplicates, so get rid of these.
|
|
|
|
*/
|
|
|
|
if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) {
|
|
|
|
Mask *mask;
|
|
|
|
|
|
|
|
// XXX: BAD! this get gpencil datablocks directly from main db...
|
|
|
|
// but that's how this currently works :/
|
|
|
|
for (mask = bmain->masks.first; mask; mask = mask->id.next) {
|
|
|
|
if (ID_REAL_USERS(mask)) {
|
|
|
|
posttrans_mask_clean(mask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* marker transform, not especially nice but we may want to move markers
|
|
|
|
* at the same time as keyframes in the dope sheet.
|
|
|
|
*/
|
|
|
|
if ((saction->flag & SACTION_MARKERS_MOVE) && (canceled == 0)) {
|
|
|
|
if (t->mode == TFM_TIME_TRANSLATE) {
|
|
|
|
#if 0
|
|
|
|
if (ELEM(t->frame_side, 'L', 'R')) { /* TFM_TIME_EXTEND */
|
|
|
|
/* same as below */
|
|
|
|
ED_markers_post_apply_transform(
|
|
|
|
ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side);
|
|
|
|
}
|
|
|
|
else /* TFM_TIME_TRANSLATE */
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
ED_markers_post_apply_transform(
|
|
|
|
ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->mode == TFM_TIME_SCALE) {
|
|
|
|
ED_markers_post_apply_transform(
|
|
|
|
ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* make sure all F-Curves are set correctly */
|
|
|
|
if (!ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
|
|
|
|
ANIM_editkeyframes_refresh(&ac);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* clear flag that was set for time-slide drawing */
|
|
|
|
saction->flag &= ~SACTION_MOVING;
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_GRAPH) {
|
|
|
|
SpaceGraph *sipo = (SpaceGraph *)t->sa->spacedata.first;
|
|
|
|
bAnimContext ac;
|
|
|
|
const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0;
|
|
|
|
|
|
|
|
/* initialize relevant anim-context 'context' data */
|
|
|
|
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ac.datatype) {
|
|
|
|
ListBase anim_data = {NULL, NULL};
|
|
|
|
bAnimListElem *ale;
|
|
|
|
short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE);
|
|
|
|
|
|
|
|
/* get channels to work on */
|
|
|
|
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
|
|
|
|
|
|
|
|
for (ale = anim_data.first; ale; ale = ale->next) {
|
|
|
|
AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
|
|
|
|
FCurve *fcu = (FCurve *)ale->key_data;
|
|
|
|
|
|
|
|
/* 3 cases here for curve cleanups:
|
|
|
|
* 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done
|
|
|
|
* 2) canceled == 0 -> user confirmed the transform,
|
|
|
|
* so duplicates should be removed
|
|
|
|
* 3) canceled + duplicate -> user canceled the transform,
|
|
|
|
* but we made duplicates, so get rid of these
|
|
|
|
*/
|
|
|
|
if ((sipo->flag & SIPO_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) {
|
|
|
|
if (adt) {
|
|
|
|
ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0);
|
|
|
|
posttrans_fcurve_clean(fcu, use_handle);
|
|
|
|
ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
posttrans_fcurve_clean(fcu, use_handle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free temp memory */
|
|
|
|
ANIM_animdata_freelist(&anim_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure all F-Curves are set correctly, but not if transform was
|
|
|
|
* canceled, since then curves were already restored to initial state.
|
|
|
|
* Note: if the refresh is really needed after cancel then some way
|
|
|
|
* has to be added to not update handle types (see bug 22289).
|
|
|
|
*/
|
|
|
|
if (!canceled) {
|
|
|
|
ANIM_editkeyframes_refresh(&ac);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_NLA) {
|
|
|
|
bAnimContext ac;
|
|
|
|
|
|
|
|
/* initialize relevant anim-context 'context' data */
|
|
|
|
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ac.datatype) {
|
|
|
|
ListBase anim_data = {NULL, NULL};
|
|
|
|
bAnimListElem *ale;
|
|
|
|
short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT);
|
|
|
|
|
|
|
|
/* get channels to work on */
|
|
|
|
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
|
|
|
|
|
|
|
|
for (ale = anim_data.first; ale; ale = ale->next) {
|
|
|
|
NlaTrack *nlt = (NlaTrack *)ale->data;
|
|
|
|
|
|
|
|
/* make sure strips are in order again */
|
|
|
|
BKE_nlatrack_sort_strips(nlt);
|
|
|
|
|
|
|
|
/* remove the temp metas */
|
|
|
|
BKE_nlastrips_clear_metas(&nlt->strips, 0, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free temp memory */
|
|
|
|
ANIM_animdata_freelist(&anim_data);
|
|
|
|
|
|
|
|
/* perform after-transfrom validation */
|
|
|
|
ED_nla_postop_refresh(&ac);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->flag & T_EDIT) {
|
|
|
|
if (t->obedit_type == OB_MESH) {
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
|
|
|
|
/* table needs to be created for each edit command, since vertices can move etc */
|
|
|
|
ED_mesh_mirror_spatial_table(tc->obedit, em, NULL, NULL, 'e');
|
|
|
|
/* TODO(campbell): xform: We need support for many mirror objects at once! */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->flag & T_POSE && (t->mode == TFM_BONESIZE)) {
|
|
|
|
/* Handle the exception where for TFM_BONESIZE in edit mode we pretend to be
|
|
|
|
* in pose mode (to use bone orientation matrix),
|
|
|
|
* in that case we don't do operations like auto-keyframing. */
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
ob = tc->poseobj;
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->flag & T_POSE) {
|
|
|
|
GSet *motionpath_updates = BLI_gset_ptr_new("motionpath updates");
|
|
|
|
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
|
|
|
|
bPoseChannel *pchan;
|
|
|
|
short targetless_ik = 0;
|
|
|
|
|
|
|
|
ob = tc->poseobj;
|
|
|
|
|
|
|
|
if ((t->flag & T_AUTOIK) && (t->options & CTX_AUTOCONFIRM)) {
|
|
|
|
/* when running transform non-interactively (operator exec),
|
|
|
|
* we need to update the pose otherwise no updates get called during
|
|
|
|
* transform and the auto-ik is not applied. see [#26164] */
|
|
|
|
struct Object *pose_ob = tc->poseobj;
|
|
|
|
BKE_pose_where_is(t->depsgraph, t->scene, pose_ob);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set BONE_TRANSFORM flags for autokey, gizmo draw might have changed them */
|
|
|
|
if (!canceled && (t->mode != TFM_DUMMY)) {
|
|
|
|
count_set_pose_transflags(ob, t->mode, t->around, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if target-less IK grabbing, we calculate the pchan transforms and clear flag */
|
|
|
|
if (!canceled && t->mode == TFM_TRANSLATION) {
|
|
|
|
targetless_ik = apply_targetless_ik(ob);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* not forget to clear the auto flag */
|
|
|
|
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
|
|
|
bKinematicConstraint *data = has_targetless_ik(pchan);
|
|
|
|
if (data) {
|
|
|
|
data->flag &= ~CONSTRAINT_IK_AUTO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t->mode == TFM_TRANSLATION) {
|
|
|
|
pose_grab_with_ik_clear(bmain, ob);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* automatic inserting of keys and unkeyed tagging -
|
|
|
|
* only if transform wasn't canceled (or TFM_DUMMY) */
|
|
|
|
if (!canceled && (t->mode != TFM_DUMMY)) {
|
|
|
|
autokeyframe_pose(C, t->scene, ob, t->mode, targetless_ik);
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t->mode != TFM_DUMMY && motionpath_need_update_pose(t->scene, ob)) {
|
|
|
|
BLI_gset_insert(motionpath_updates, ob);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update motion paths once for all transformed bones in an object. */
|
|
|
|
GSetIterator gs_iter;
|
|
|
|
GSET_ITER (gs_iter, motionpath_updates) {
|
|
|
|
bool current_frame_only = canceled;
|
|
|
|
ob = BLI_gsetIterator_getKey(&gs_iter);
|
|
|
|
ED_pose_recalculate_paths(C, t->scene, ob, current_frame_only);
|
|
|
|
}
|
|
|
|
BLI_gset_free(motionpath_updates, NULL);
|
|
|
|
}
|
|
|
|
else if (t->options & CTX_PAINT_CURVE) {
|
|
|
|
/* pass */
|
|
|
|
}
|
|
|
|
else if ((t->view_layer->basact) && (ob = t->view_layer->basact->object) &&
|
|
|
|
(ob->mode & OB_MODE_PARTICLE_EDIT) && PE_get_current(t->scene, ob)) {
|
|
|
|
/* do nothing */
|
|
|
|
}
|
|
|
|
else if (t->flag & T_CURSOR) {
|
|
|
|
/* do nothing */
|
|
|
|
}
|
|
|
|
else { /* Objects */
|
|
|
|
BLI_assert(t->flag & (T_OBJECT | T_TEXTURE));
|
|
|
|
|
|
|
|
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
|
|
|
bool motionpath_update = false;
|
|
|
|
|
|
|
|
for (int i = 0; i < tc->data_len; i++) {
|
|
|
|
TransData *td = tc->data + i;
|
|
|
|
ListBase pidlist;
|
|
|
|
PTCacheID *pid;
|
|
|
|
ob = td->ob;
|
|
|
|
|
|
|
|
if (td->flag & TD_NOACTION) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (td->flag & TD_SKIP) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* flag object caches as outdated */
|
|
|
|
BKE_ptcache_ids_from_object(&pidlist, ob, t->scene, MAX_DUPLI_RECUR);
|
|
|
|
for (pid = pidlist.first; pid; pid = pid->next) {
|
|
|
|
if (pid->type != PTCACHE_TYPE_PARTICLES) {
|
|
|
|
/* particles don't need reset on geometry change */
|
|
|
|
pid->cache->flag |= PTCACHE_OUTDATED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BLI_freelistN(&pidlist);
|
|
|
|
|
|
|
|
/* pointcache refresh */
|
|
|
|
if (BKE_ptcache_object_reset(t->scene, ob, PTCACHE_RESET_OUTDATED)) {
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Needed for proper updating of "quick cached" dynamics. */
|
|
|
|
/* Creates troubles for moving animated objects without */
|
|
|
|
/* autokey though, probably needed is an anim sys override? */
|
|
|
|
/* Please remove if some other solution is found. -jahka */
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
|
|
|
|
|
|
|
|
/* Set autokey if necessary */
|
|
|
|
if (!canceled) {
|
|
|
|
autokeyframe_object(C, t->scene, t->view_layer, ob, t->mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
motionpath_update |= motionpath_need_update_object(t->scene, ob);
|
|
|
|
|
|
|
|
/* restore rigid body transform */
|
|
|
|
if (ob->rigidbody_object && canceled) {
|
|
|
|
float ctime = BKE_scene_frame_get(t->scene);
|
|
|
|
if (BKE_rigidbody_check_sim_running(t->scene->rigidbody_world, ctime)) {
|
|
|
|
BKE_rigidbody_aftertrans_update(ob,
|
|
|
|
td->ext->oloc,
|
|
|
|
td->ext->orot,
|
|
|
|
td->ext->oquat,
|
|
|
|
td->ext->orotAxis,
|
|
|
|
td->ext->orotAngle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (motionpath_update) {
|
|
|
|
/* Update motion paths once for all transformed objects. */
|
|
|
|
bool current_frame_only = canceled;
|
|
|
|
ED_objects_recalculate_paths(C, t->scene, current_frame_only);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
clear_trans_object_base_flags(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
int special_transform_moving(TransInfo *t)
|
|
|
|
{
|
|
|
|
if (t->spacetype == SPACE_SEQ) {
|
|
|
|
return G_TRANSFORM_SEQ;
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_GRAPH) {
|
|
|
|
return G_TRANSFORM_FCURVES;
|
|
|
|
}
|
|
|
|
else if ((t->flag & T_EDIT) || (t->flag & T_POSE)) {
|
|
|
|
return G_TRANSFORM_EDIT;
|
|
|
|
}
|
|
|
|
else if (t->flag & (T_OBJECT | T_TEXTURE)) {
|
|
|
|
return G_TRANSFORM_OBJ;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
static int countAndCleanTransDataContainer(TransInfo *t)
|
|
|
|
{
|
|
|
|
BLI_assert(ELEM(t->data_len_all, 0, -1));
|
|
|
|
t->data_len_all = 0;
|
|
|
|
uint data_container_len_orig = t->data_container_len;
|
|
|
|
for (TransDataContainer *th_end = t->data_container - 1,
|
|
|
|
*tc = t->data_container + (t->data_container_len - 1);
|
|
|
|
tc != th_end;
|
|
|
|
tc--) {
|
|
|
|
if (tc->data_len == 0) {
|
|
|
|
uint index = tc - t->data_container;
|
|
|
|
if (index + 1 != t->data_container_len) {
|
|
|
|
SWAP(TransDataContainer,
|
|
|
|
t->data_container[index],
|
|
|
|
t->data_container[t->data_container_len - 1]);
|
|
|
|
}
|
|
|
|
t->data_container_len -= 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
t->data_len_all += tc->data_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (data_container_len_orig != t->data_container_len) {
|
|
|
|
t->data_container = MEM_reallocN(t->data_container,
|
|
|
|
sizeof(*t->data_container) * t->data_container_len);
|
|
|
|
}
|
|
|
|
return t->data_len_all;
|
|
|
|
}
|
|
|
|
|
|
|
|
void createTransData(bContext *C, TransInfo *t)
|
|
|
|
{
|
|
|
|
Scene *scene = t->scene;
|
|
|
|
ViewLayer *view_layer = t->view_layer;
|
|
|
|
Object *ob = OBACT(view_layer);
|
|
|
|
|
|
|
|
bool has_transform_context = true;
|
|
|
|
t->data_len_all = -1;
|
|
|
|
|
|
|
|
/* if tests must match recalcData for correct updates */
|
|
|
|
if (t->options & CTX_CURSOR) {
|
|
|
|
t->flag |= T_CURSOR;
|
|
|
|
t->obedit_type = -1;
|
|
|
|
|
|
|
|
if (t->spacetype == SPACE_IMAGE) {
|
|
|
|
createTransCursor_image(t);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
createTransCursor_view3d(t);
|
|
|
|
}
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
}
|
|
|
|
else if (t->options & CTX_TEXTURE) {
|
|
|
|
t->flag |= T_TEXTURE;
|
|
|
|
t->obedit_type = -1;
|
|
|
|
|
|
|
|
createTransTexspace(t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
}
|
|
|
|
else if (t->options & CTX_EDGE) {
|
|
|
|
/* Multi object editing. */
|
|
|
|
initTransDataContainers_FromObjectData(t, ob, NULL, 0);
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
tc->data_ext = NULL;
|
|
|
|
}
|
|
|
|
t->flag |= T_EDIT;
|
|
|
|
|
|
|
|
createTransEdge(t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
|
|
|
|
if (t->data_len_all && t->flag & T_PROP_EDIT) {
|
|
|
|
sort_trans_data_selected_first(t);
|
|
|
|
set_prop_dist(t, 1);
|
|
|
|
sort_trans_data_dist(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->options & CTX_GPENCIL_STROKES) {
|
|
|
|
t->options |= CTX_GPENCIL_STROKES;
|
|
|
|
t->flag |= T_POINTS | T_EDIT;
|
|
|
|
|
|
|
|
initTransDataContainers_FromObjectData(t, ob, NULL, 0);
|
|
|
|
createTransGPencil(C, t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
|
|
|
|
if (t->data_len_all && (t->flag & T_PROP_EDIT)) {
|
|
|
|
sort_trans_data_selected_first(t);
|
|
|
|
set_prop_dist(t, 1);
|
|
|
|
sort_trans_data_dist(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_IMAGE) {
|
|
|
|
t->flag |= T_POINTS | T_2D_EDIT;
|
|
|
|
if (t->options & CTX_MASK) {
|
|
|
|
|
|
|
|
/* copied from below */
|
|
|
|
createTransMaskingData(C, t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
|
|
|
|
if (t->data_len_all && (t->flag & T_PROP_EDIT)) {
|
|
|
|
sort_trans_data_selected_first(t);
|
|
|
|
set_prop_dist(t, true);
|
|
|
|
sort_trans_data_dist(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->options & CTX_PAINT_CURVE) {
|
|
|
|
if (!ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) {
|
|
|
|
createTransPaintCurveVerts(C, t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
has_transform_context = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->obedit_type == OB_MESH) {
|
|
|
|
|
|
|
|
initTransDataContainers_FromObjectData(t, ob, NULL, 0);
|
|
|
|
createTransUVs(C, t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
|
|
|
|
t->flag |= T_EDIT;
|
|
|
|
|
|
|
|
if (t->data_len_all && (t->flag & T_PROP_EDIT)) {
|
|
|
|
sort_trans_data_selected_first(t);
|
|
|
|
set_prop_dist(t, 1);
|
|
|
|
sort_trans_data_dist(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
has_transform_context = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_ACTION) {
|
|
|
|
t->flag |= T_POINTS | T_2D_EDIT;
|
|
|
|
t->obedit_type = -1;
|
|
|
|
|
|
|
|
createTransActionData(C, t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
|
|
|
|
if (t->data_len_all && (t->flag & T_PROP_EDIT)) {
|
|
|
|
sort_trans_data_selected_first(t);
|
|
|
|
/* don't do that, distance has been set in createTransActionData already */
|
|
|
|
// set_prop_dist(t, false);
|
|
|
|
sort_trans_data_dist(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_NLA) {
|
|
|
|
t->flag |= T_POINTS | T_2D_EDIT;
|
|
|
|
t->obedit_type = -1;
|
|
|
|
|
|
|
|
createTransNlaData(C, t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_SEQ) {
|
|
|
|
t->flag |= T_POINTS | T_2D_EDIT;
|
|
|
|
t->obedit_type = -1;
|
|
|
|
|
|
|
|
t->num.flag |= NUM_NO_FRACTION; /* sequencer has no use for floating point trasnform */
|
|
|
|
createTransSeqData(C, t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_GRAPH) {
|
|
|
|
t->flag |= T_POINTS | T_2D_EDIT;
|
|
|
|
t->obedit_type = -1;
|
|
|
|
|
|
|
|
createTransGraphEditData(C, t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
|
|
|
|
if (t->data_len_all && (t->flag & T_PROP_EDIT)) {
|
|
|
|
/* makes selected become first in array */
|
|
|
|
sort_trans_data_selected_first(t);
|
|
|
|
|
|
|
|
/* don't do that, distance has been set in createTransGraphEditData already */
|
|
|
|
set_prop_dist(t, false);
|
|
|
|
|
|
|
|
sort_trans_data_dist(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_NODE) {
|
|
|
|
t->flag |= T_POINTS | T_2D_EDIT;
|
|
|
|
t->obedit_type = -1;
|
|
|
|
|
|
|
|
createTransNodeData(C, t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
|
|
|
|
if (t->data_len_all && (t->flag & T_PROP_EDIT)) {
|
|
|
|
sort_trans_data_selected_first(t);
|
|
|
|
set_prop_dist(t, 1);
|
|
|
|
sort_trans_data_dist(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->spacetype == SPACE_CLIP) {
|
|
|
|
t->flag |= T_POINTS | T_2D_EDIT;
|
|
|
|
t->obedit_type = -1;
|
|
|
|
|
|
|
|
if (t->options & CTX_MOVIECLIP) {
|
|
|
|
createTransTrackingData(C, t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
}
|
|
|
|
else if (t->options & CTX_MASK) {
|
|
|
|
/* copied from above */
|
|
|
|
createTransMaskingData(C, t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
|
|
|
|
if (t->data_len_all && (t->flag & T_PROP_EDIT)) {
|
|
|
|
sort_trans_data_selected_first(t);
|
|
|
|
set_prop_dist(t, true);
|
|
|
|
sort_trans_data_dist(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
has_transform_context = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t->obedit_type != -1) {
|
|
|
|
/* Multi object editing. */
|
|
|
|
initTransDataContainers_FromObjectData(t, ob, NULL, 0);
|
|
|
|
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
tc->data_ext = NULL;
|
|
|
|
}
|
|
|
|
if (t->obedit_type == OB_MESH) {
|
|
|
|
createTransEditVerts(t);
|
|
|
|
}
|
|
|
|
else if (ELEM(t->obedit_type, OB_CURVE, OB_SURF)) {
|
|
|
|
createTransCurveVerts(t);
|
|
|
|
}
|
|
|
|
else if (t->obedit_type == OB_LATTICE) {
|
|
|
|
createTransLatticeVerts(t);
|
|
|
|
}
|
|
|
|
else if (t->obedit_type == OB_MBALL) {
|
|
|
|
createTransMBallVerts(t);
|
|
|
|
}
|
|
|
|
else if (t->obedit_type == OB_ARMATURE) {
|
|
|
|
t->flag &= ~T_PROP_EDIT;
|
|
|
|
createTransArmatureVerts(t);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
printf("edit type not implemented!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
|
|
|
|
t->flag |= T_EDIT | T_POINTS;
|
|
|
|
|
|
|
|
if (t->data_len_all) {
|
|
|
|
if (t->flag & T_PROP_EDIT) {
|
|
|
|
if (ELEM(t->obedit_type, OB_CURVE, OB_MESH)) {
|
|
|
|
sort_trans_data_selected_first(t);
|
|
|
|
if ((t->obedit_type == OB_MESH) && (t->flag & T_PROP_CONNECTED)) {
|
|
|
|
/* already calculated by editmesh_set_connectivity_distance */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
set_prop_dist(t, 0);
|
|
|
|
}
|
|
|
|
sort_trans_data_dist(t);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sort_trans_data_selected_first(t);
|
|
|
|
set_prop_dist(t, 1);
|
|
|
|
sort_trans_data_dist(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (ELEM(t->obedit_type, OB_CURVE)) {
|
|
|
|
/* Needed because bezier handles can be partially selected
|
|
|
|
* and are still added into transform data. */
|
|
|
|
sort_trans_data_selected_first(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* exception... hackish, we want bonesize to use bone orientation matrix (ton) */
|
|
|
|
if (t->mode == TFM_BONESIZE) {
|
|
|
|
t->flag &= ~(T_EDIT | T_POINTS);
|
|
|
|
t->flag |= T_POSE;
|
|
|
|
t->obedit_type = -1;
|
|
|
|
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
|
|
tc->poseobj = tc->obedit;
|
|
|
|
tc->obedit = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ob && (ob->mode & OB_MODE_POSE)) {
|
|
|
|
/* XXX this is currently limited to active armature only... */
|
|
|
|
|
|
|
|
/* XXX active-layer checking isn't done
|
|
|
|
* as that should probably be checked through context instead. */
|
|
|
|
|
|
|
|
/* Multi object editing. */
|
|
|
|
initTransDataContainers_FromObjectData(t, ob, NULL, 0);
|
|
|
|
createTransPose(t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
}
|
|
|
|
else if (ob && (ob->mode & OB_MODE_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) {
|
|
|
|
/* important that ob_armature can be set even when its not selected [#23412]
|
|
|
|
* lines below just check is also visible */
|
|
|
|
has_transform_context = false;
|
|
|
|
Object *ob_armature = modifiers_isDeformedByArmature(ob);
|
|
|
|
if (ob_armature && ob_armature->mode & OB_MODE_POSE) {
|
|
|
|
Base *base_arm = BKE_view_layer_base_find(t->view_layer, ob_armature);
|
|
|
|
if (base_arm) {
|
|
|
|
View3D *v3d = t->view;
|
|
|
|
if (BASE_VISIBLE(v3d, base_arm)) {
|
|
|
|
Object *objects[1];
|
|
|
|
objects[0] = ob_armature;
|
|
|
|
uint objects_len = 1;
|
|
|
|
initTransDataContainers_FromObjectData(t, ob_armature, objects, objects_len);
|
|
|
|
createTransPose(t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
has_transform_context = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT) && PE_start_edit(PE_get_current(scene, ob))) {
|
|
|
|
createTransParticleVerts(C, t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
t->flag |= T_POINTS;
|
|
|
|
|
|
|
|
if (t->data_len_all && t->flag & T_PROP_EDIT) {
|
|
|
|
sort_trans_data_selected_first(t);
|
|
|
|
set_prop_dist(t, 1);
|
|
|
|
sort_trans_data_dist(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
|
|
|
|
if ((t->options & CTX_PAINT_CURVE) && !ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) {
|
|
|
|
t->flag |= T_POINTS | T_2D_EDIT;
|
|
|
|
createTransPaintCurveVerts(C, t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
has_transform_context = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((ob) &&
|
|
|
|
(ELEM(
|
|
|
|
ob->mode, OB_MODE_PAINT_GPENCIL, OB_MODE_SCULPT_GPENCIL, OB_MODE_WEIGHT_GPENCIL))) {
|
|
|
|
/* In grease pencil all transformations must be canceled if not Object or Edit. */
|
|
|
|
has_transform_context = false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Needed for correct Object.obmat after duplication, see: T62135. */
|
|
|
|
BKE_scene_graph_evaluated_ensure(t->depsgraph, CTX_data_main(t->context));
|
|
|
|
|
|
|
|
if ((scene->toolsettings->transform_flag & SCE_XFORM_DATA_ORIGIN) != 0) {
|
|
|
|
t->options |= CTX_OBMODE_XFORM_OBDATA;
|
|
|
|
}
|
|
|
|
if ((scene->toolsettings->transform_flag & SCE_XFORM_SKIP_CHILDREN) != 0) {
|
|
|
|
t->options |= CTX_OBMODE_XFORM_SKIP_CHILDREN;
|
|
|
|
}
|
|
|
|
|
|
|
|
createTransObject(C, t);
|
|
|
|
countAndCleanTransDataContainer(t);
|
|
|
|
t->flag |= T_OBJECT;
|
|
|
|
|
|
|
|
if (t->data_len_all && t->flag & T_PROP_EDIT) {
|
|
|
|
// selected objects are already first, no need to presort
|
|
|
|
set_prop_dist(t, 1);
|
|
|
|
sort_trans_data_dist(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if we're transforming the camera from the camera */
|
|
|
|
if ((t->spacetype == SPACE_VIEW3D) && (t->ar->regiontype == RGN_TYPE_WINDOW)) {
|
|
|
|
View3D *v3d = t->view;
|
|
|
|
RegionView3D *rv3d = t->ar->regiondata;
|
|
|
|
if ((rv3d->persp == RV3D_CAMOB) && v3d->camera) {
|
|
|
|
/* we could have a flag to easily check an object is being transformed */
|
|
|
|
if (v3d->camera->id.tag & LIB_TAG_DOIT) {
|
|
|
|
t->flag |= T_CAMERA;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that 'countAndCleanTransDataContainer' ran. */
|
|
|
|
if (has_transform_context) {
|
|
|
|
BLI_assert(t->data_len_all != -1);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
BLI_assert(t->data_len_all == -1);
|
|
|
|
t->data_len_all = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
BLI_assert((!(t->flag & T_EDIT)) == (!(t->obedit_type != -1)));
|
|
|
|
}
|