Transform: Split transform_conversions into multiple files.
Part of T68836 `transform conversions.c` is a file that is getting too big (almost 10,000 lines). So it's a good idea to split it into smaller files. differential revision: https://developer.blender.org/D5677
This commit is contained in:
parent
c90b81172b
commit
4e731ec97b
|
@ -40,7 +40,24 @@ set(INC_SYS
|
|||
set(SRC
|
||||
transform.c
|
||||
transform_constraints.c
|
||||
transform_conversions.c
|
||||
transform_convert.c
|
||||
transform_convert_action.c
|
||||
transform_convert_armature.c
|
||||
transform_convert_paintcurve.c
|
||||
transform_convert_cursor.c
|
||||
transform_convert_curve.c
|
||||
transform_convert_graph.c
|
||||
transform_convert_gpencil.c
|
||||
transform_convert_lattice.c
|
||||
transform_convert_mask.c
|
||||
transform_convert_mball.c
|
||||
transform_convert_mesh.c
|
||||
transform_convert_nla.c
|
||||
transform_convert_node.c
|
||||
transform_convert_object.c
|
||||
transform_convert_particle.c
|
||||
transform_convert_sequencer.c
|
||||
transform_convert_tracking.c
|
||||
transform_generics.c
|
||||
transform_gizmo_2d.c
|
||||
transform_gizmo_3d.c
|
||||
|
@ -52,6 +69,7 @@ set(SRC
|
|||
transform_snap_object.c
|
||||
|
||||
transform.h
|
||||
transform_convert.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
|
|
@ -95,6 +95,7 @@
|
|||
#include "BLT_translation.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
/* Disabling, since when you type you know what you are doing,
|
||||
* and being able to set it to zero is handy. */
|
||||
|
|
|
@ -909,23 +909,6 @@ void drawPropCircle(const struct bContext *C, TransInfo *t);
|
|||
|
||||
struct wmKeyMap *transform_modal_keymap(struct wmKeyConfig *keyconf);
|
||||
|
||||
/*********************** transform_conversions.c ********** */
|
||||
|
||||
void flushTransIntFrameActionData(TransInfo *t);
|
||||
void flushTransGraphData(TransInfo *t);
|
||||
void remake_graph_transdata(TransInfo *t, struct ListBase *anim_data);
|
||||
void flushTransUVs(TransInfo *t);
|
||||
void flushTransParticles(TransInfo *t);
|
||||
bool clipUVTransform(TransInfo *t, float vec[2], const bool resize);
|
||||
void clipUVData(TransInfo *t);
|
||||
void flushTransNodes(TransInfo *t);
|
||||
void flushTransSeq(TransInfo *t);
|
||||
void flushTransTracking(TransInfo *t);
|
||||
void flushTransMasking(TransInfo *t);
|
||||
void flushTransPaintCurve(TransInfo *t);
|
||||
void restoreMirrorPoseBones(TransDataContainer *tc);
|
||||
void restoreBones(TransDataContainer *tc);
|
||||
|
||||
/*********************** transform_gizmo.c ********** */
|
||||
|
||||
#define GIZMO_AXIS_LINE_WIDTH 2.0f
|
||||
|
@ -935,32 +918,8 @@ bool gimbal_axis(struct Object *ob, float gmat[3][3]);
|
|||
void drawDial3d(const TransInfo *t);
|
||||
|
||||
/*********************** TransData Creation and General Handling *********** */
|
||||
void createTransData(struct bContext *C, TransInfo *t);
|
||||
void sort_trans_data_dist(TransInfo *t);
|
||||
void special_aftertrans_update(struct bContext *C, TransInfo *t);
|
||||
int special_transform_moving(TransInfo *t);
|
||||
|
||||
void transform_autoik_update(TransInfo *t, short mode);
|
||||
bool transdata_check_local_islands(TransInfo *t, short around);
|
||||
|
||||
int count_set_pose_transflags(struct Object *ob,
|
||||
const int mode,
|
||||
const short around,
|
||||
bool has_translate_rotate[2]);
|
||||
|
||||
/* Auto-keyframe applied after transform, returns true if motion paths need to be updated. */
|
||||
void autokeyframe_object(struct bContext *C,
|
||||
struct Scene *scene,
|
||||
struct ViewLayer *view_layer,
|
||||
struct Object *ob,
|
||||
int tmode);
|
||||
void autokeyframe_pose(
|
||||
struct bContext *C, struct Scene *scene, struct Object *ob, int tmode, short targetless_ik);
|
||||
|
||||
/* Test if we need to update motion paths for a given object. */
|
||||
bool motionpath_need_update_object(struct Scene *scene, struct Object *ob);
|
||||
bool motionpath_need_update_pose(struct Scene *scene, struct Object *ob);
|
||||
|
||||
/*********************** Constraints *****************************/
|
||||
|
||||
void drawConstraint(TransInfo *t);
|
||||
|
@ -1165,7 +1124,4 @@ bool checkUseAxisMatrix(TransInfo *t);
|
|||
th != tc_end; \
|
||||
th++, i++)
|
||||
|
||||
void trans_obdata_in_obmode_update_all(struct TransInfo *t);
|
||||
void trans_obchild_in_obmode_update_all(struct TransInfo *t);
|
||||
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef __TRANSFORM_CONVERT_H__
|
||||
#define __TRANSFORM_CONVERT_H__
|
||||
|
||||
struct bContext;
|
||||
struct bKinematicConstraint;
|
||||
struct bPoseChannel;
|
||||
struct BezTriple;
|
||||
struct ListBase;
|
||||
struct Object;
|
||||
struct TransData;
|
||||
struct TransDataContainer;
|
||||
struct TransDataCurveHandleFlags;
|
||||
struct TransInfo;
|
||||
|
||||
/* transform_convert.c */
|
||||
int count_set_pose_transflags(Object *ob,
|
||||
const int mode,
|
||||
const short around,
|
||||
bool has_translate_rotate[2]);
|
||||
void transform_autoik_update(TransInfo *t, short mode);
|
||||
void autokeyframe_object(struct bContext *C,
|
||||
struct Scene *scene,
|
||||
struct ViewLayer *view_layer,
|
||||
struct Object *ob,
|
||||
int tmode);
|
||||
void autokeyframe_pose(
|
||||
struct bContext *C, struct Scene *scene, struct Object *ob, int tmode, short targetless_ik);
|
||||
bool motionpath_need_update_object(struct Scene *scene, struct Object *ob);
|
||||
bool motionpath_need_update_pose(struct Scene *scene, struct Object *ob);
|
||||
int special_transform_moving(TransInfo *t);
|
||||
void remake_graph_transdata(TransInfo *t, struct ListBase *anim_data);
|
||||
void special_aftertrans_update(struct bContext *C, TransInfo *t);
|
||||
void sort_trans_data_dist(TransInfo *t);
|
||||
void createTransData(struct bContext *C, TransInfo *t);
|
||||
bool clipUVTransform(TransInfo *t, float vec[2], const bool resize);
|
||||
void clipUVData(TransInfo *t);
|
||||
|
||||
/* transform_convert_action.c */
|
||||
void flushTransIntFrameActionData(TransInfo *t);
|
||||
/* transform_convert_armature.c */
|
||||
void restoreMirrorPoseBones(TransDataContainer *tc);
|
||||
void restoreBones(TransDataContainer *tc);
|
||||
/* transform_convert_graph.c */
|
||||
void flushTransGraphData(TransInfo *t);
|
||||
/* transform_convert_mask.c */
|
||||
void flushTransMasking(TransInfo *t);
|
||||
/* transform_convert_mesh.c */
|
||||
void flushTransUVs(TransInfo *t);
|
||||
/* transform_convert_node.c */
|
||||
void flushTransNodes(TransInfo *t);
|
||||
/* transform_convert_object.c */
|
||||
void trans_obdata_in_obmode_update_all(struct TransInfo *t);
|
||||
void trans_obchild_in_obmode_update_all(struct TransInfo *t);
|
||||
/* transform_convert_paintcurve.c */
|
||||
void flushTransPaintCurve(TransInfo *t);
|
||||
/* transform_convert_particle.c */
|
||||
void flushTransParticles(TransInfo *t);
|
||||
/* transform_convert_sequencer.c */
|
||||
void flushTransSeq(TransInfo *t);
|
||||
/* transform_convert_tracking.c */
|
||||
void flushTransTracking(TransInfo *t);
|
||||
|
||||
/********************* intern **********************/
|
||||
|
||||
/* transform_convert.c */
|
||||
void transform_around_single_fallback(TransInfo *t);
|
||||
bool constraints_list_needinv(TransInfo *t, ListBase *list);
|
||||
void calc_distanceCurveVerts(TransData *head, TransData *tail);
|
||||
struct TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTriple *bezt);
|
||||
bool FrameOnMouseSide(char side, float frame, float cframe);
|
||||
|
||||
/* transform_convert_action.c */
|
||||
void createTransActionData(bContext *C, TransInfo *t);
|
||||
/* transform_convert_armature.c */
|
||||
struct bKinematicConstraint *has_targetless_ik(struct bPoseChannel *pchan);
|
||||
void createTransPose(TransInfo *t);
|
||||
void createTransArmatureVerts(TransInfo *t);
|
||||
/* transform_convert_cursor.c */
|
||||
void createTransCursor_image(TransInfo *t);
|
||||
void createTransCursor_view3d(TransInfo *t);
|
||||
/* transform_convert_curve.c */
|
||||
void createTransCurveVerts(TransInfo *t);
|
||||
/* transform_convert_graph.c */
|
||||
void createTransGraphEditData(bContext *C, TransInfo *t);
|
||||
/* transform_convert_gpencil.c */
|
||||
void createTransGPencil(bContext *C, TransInfo *t);
|
||||
/* transform_convert_lattice.c */
|
||||
void createTransLatticeVerts(TransInfo *t);
|
||||
/* transform_convert_mask.c */
|
||||
void createTransMaskingData(bContext *C, TransInfo *t);
|
||||
/* transform_convert_mball.c */
|
||||
void createTransMBallVerts(TransInfo *t);
|
||||
/* transform_convert_mesh.c */
|
||||
void createTransEditVerts(TransInfo *t);
|
||||
void createTransEdge(TransInfo *t);
|
||||
void createTransUVs(bContext *C, TransInfo *t);
|
||||
/* transform_convert_nla.c */
|
||||
void createTransNlaData(bContext *C, TransInfo *t);
|
||||
/* transform_convert_node.c */
|
||||
void createTransNodeData(bContext *UNUSED(C), TransInfo *t);
|
||||
/* transform_convert_object.c */
|
||||
void clear_trans_object_base_flags(TransInfo *t);
|
||||
void createTransObject(bContext *C, TransInfo *t);
|
||||
void createTransTexspace(TransInfo *t);
|
||||
/* transform_convert_paintcurve.c */
|
||||
void createTransPaintCurveVerts(bContext *C, TransInfo *t);
|
||||
/* transform_convert_particle.c */
|
||||
void createTransParticleVerts(bContext *C, TransInfo *t);
|
||||
/* transform_convert_sequence.c */
|
||||
void createTransSeqData(bContext *C, TransInfo *t);
|
||||
/* transform_convert_tracking.c */
|
||||
void createTransTrackingData(bContext *C, TransInfo *t);
|
||||
void cancelTransTracking(TransInfo *t);
|
||||
#endif
|
|
@ -0,0 +1,575 @@
|
|||
/*
|
||||
* 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_gpencil_types.h"
|
||||
#include "DNA_mask_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "BKE_nla.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "ED_anim_api.h"
|
||||
|
||||
#include "UI_view2d.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
/* helper struct for gp-frame transforms */
|
||||
typedef struct tGPFtransdata {
|
||||
float val; /* where transdata writes transform */
|
||||
int *sdata; /* pointer to gpf->framenum */
|
||||
} tGPFtransdata;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Action Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/* fully select selected beztriples, but only include if it's on the right side of cfra */
|
||||
static int count_fcurve_keys(FCurve *fcu, char side, float cfra, bool is_prop_edit)
|
||||
{
|
||||
BezTriple *bezt;
|
||||
int i, count = 0, count_all = 0;
|
||||
|
||||
if (ELEM(NULL, fcu, fcu->bezt)) {
|
||||
return count;
|
||||
}
|
||||
|
||||
/* only include points that occur on the right side of cfra */
|
||||
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
|
||||
if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) {
|
||||
/* no need to adjust the handle selection since they are assumed
|
||||
* selected (like graph editor with SIPO_NOHANDLES) */
|
||||
if (bezt->f2 & SELECT) {
|
||||
count++;
|
||||
}
|
||||
|
||||
count_all++;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_prop_edit && count > 0) {
|
||||
return count_all;
|
||||
}
|
||||
else {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/* fully select selected beztriples, but only include if it's on the right side of cfra */
|
||||
static int count_gplayer_frames(bGPDlayer *gpl, char side, float cfra, bool is_prop_edit)
|
||||
{
|
||||
bGPDframe *gpf;
|
||||
int count = 0, count_all = 0;
|
||||
|
||||
if (gpl == NULL) {
|
||||
return count;
|
||||
}
|
||||
|
||||
/* only include points that occur on the right side of cfra */
|
||||
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
|
||||
if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) {
|
||||
if (gpf->flag & GP_FRAME_SELECT) {
|
||||
count++;
|
||||
}
|
||||
count_all++;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_prop_edit && count > 0) {
|
||||
return count_all;
|
||||
}
|
||||
else {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/* fully select selected beztriples, but only include if it's on the right side of cfra */
|
||||
static int count_masklayer_frames(MaskLayer *masklay, char side, float cfra, bool is_prop_edit)
|
||||
{
|
||||
MaskLayerShape *masklayer_shape;
|
||||
int count = 0, count_all = 0;
|
||||
|
||||
if (masklay == NULL) {
|
||||
return count;
|
||||
}
|
||||
|
||||
/* only include points that occur on the right side of cfra */
|
||||
for (masklayer_shape = masklay->splines_shapes.first; masklayer_shape;
|
||||
masklayer_shape = masklayer_shape->next) {
|
||||
if (FrameOnMouseSide(side, (float)masklayer_shape->frame, cfra)) {
|
||||
if (masklayer_shape->flag & MASK_SHAPE_SELECT) {
|
||||
count++;
|
||||
}
|
||||
count_all++;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_prop_edit && count > 0) {
|
||||
return count_all;
|
||||
}
|
||||
else {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/* This function assigns the information to transdata */
|
||||
static void TimeToTransData(TransData *td, float *time, AnimData *adt, float ypos)
|
||||
{
|
||||
/* memory is calloc'ed, so that should zero everything nicely for us */
|
||||
td->val = time;
|
||||
td->ival = *(time);
|
||||
|
||||
td->center[0] = td->ival;
|
||||
td->center[1] = ypos;
|
||||
|
||||
/* store the AnimData where this keyframe exists as a keyframe of the
|
||||
* active action as td->extra.
|
||||
*/
|
||||
td->extra = adt;
|
||||
}
|
||||
|
||||
/* This function advances the address to which td points to, so it must return
|
||||
* the new address so that the next time new transform data is added, it doesn't
|
||||
* overwrite the existing ones... i.e. td = IcuToTransData(td, icu, ob, side, cfra);
|
||||
*
|
||||
* The 'side' argument is needed for the extend mode. 'B' = both sides, 'R'/'L' mean only data
|
||||
* on the named side are used.
|
||||
*/
|
||||
static TransData *ActionFCurveToTransData(TransData *td,
|
||||
TransData2D **td2dv,
|
||||
FCurve *fcu,
|
||||
AnimData *adt,
|
||||
char side,
|
||||
float cfra,
|
||||
bool is_prop_edit,
|
||||
float ypos)
|
||||
{
|
||||
BezTriple *bezt;
|
||||
TransData2D *td2d = *td2dv;
|
||||
int i;
|
||||
|
||||
if (ELEM(NULL, fcu, fcu->bezt)) {
|
||||
return td;
|
||||
}
|
||||
|
||||
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
|
||||
/* only add selected keyframes (for now, proportional edit is not enabled) */
|
||||
if (is_prop_edit || (bezt->f2 & SELECT)) { /* note this MUST match count_fcurve_keys(),
|
||||
* so can't use BEZT_ISSEL_ANY() macro */
|
||||
/* only add if on the right 'side' of the current frame */
|
||||
if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) {
|
||||
TimeToTransData(td, bezt->vec[1], adt, ypos);
|
||||
|
||||
if (bezt->f2 & SELECT) {
|
||||
td->flag |= TD_SELECTED;
|
||||
}
|
||||
|
||||
/*set flags to move handles as necessary*/
|
||||
td->flag |= TD_MOVEHANDLE1 | TD_MOVEHANDLE2;
|
||||
td2d->h1 = bezt->vec[0];
|
||||
td2d->h2 = bezt->vec[2];
|
||||
|
||||
copy_v2_v2(td2d->ih1, td2d->h1);
|
||||
copy_v2_v2(td2d->ih2, td2d->h2);
|
||||
|
||||
td++;
|
||||
td2d++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*td2dv = td2d;
|
||||
|
||||
return td;
|
||||
}
|
||||
|
||||
/* This function advances the address to which td points to, so it must return
|
||||
* the new address so that the next time new transform data is added, it doesn't
|
||||
* overwrite the existing ones... i.e. td = GPLayerToTransData(td, ipo, ob, side, cfra);
|
||||
*
|
||||
* The 'side' argument is needed for the extend mode. 'B' = both sides, 'R'/'L' mean only data
|
||||
* on the named side are used.
|
||||
*/
|
||||
static int GPLayerToTransData(TransData *td,
|
||||
tGPFtransdata *tfd,
|
||||
bGPDlayer *gpl,
|
||||
char side,
|
||||
float cfra,
|
||||
bool is_prop_edit,
|
||||
float ypos)
|
||||
{
|
||||
bGPDframe *gpf;
|
||||
int count = 0;
|
||||
|
||||
/* check for select frames on right side of current frame */
|
||||
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
|
||||
if (is_prop_edit || (gpf->flag & GP_FRAME_SELECT)) {
|
||||
if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) {
|
||||
/* memory is calloc'ed, so that should zero everything nicely for us */
|
||||
td->val = &tfd->val;
|
||||
td->ival = (float)gpf->framenum;
|
||||
|
||||
td->center[0] = td->ival;
|
||||
td->center[1] = ypos;
|
||||
|
||||
tfd->val = (float)gpf->framenum;
|
||||
tfd->sdata = &gpf->framenum;
|
||||
|
||||
/* advance td now */
|
||||
td++;
|
||||
tfd++;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* refer to comment above #GPLayerToTransData, this is the same but for masks */
|
||||
static int MaskLayerToTransData(TransData *td,
|
||||
tGPFtransdata *tfd,
|
||||
MaskLayer *masklay,
|
||||
char side,
|
||||
float cfra,
|
||||
bool is_prop_edit,
|
||||
float ypos)
|
||||
{
|
||||
MaskLayerShape *masklay_shape;
|
||||
int count = 0;
|
||||
|
||||
/* check for select frames on right side of current frame */
|
||||
for (masklay_shape = masklay->splines_shapes.first; masklay_shape;
|
||||
masklay_shape = masklay_shape->next) {
|
||||
if (is_prop_edit || (masklay_shape->flag & MASK_SHAPE_SELECT)) {
|
||||
if (FrameOnMouseSide(side, (float)masklay_shape->frame, cfra)) {
|
||||
/* memory is calloc'ed, so that should zero everything nicely for us */
|
||||
td->val = &tfd->val;
|
||||
td->ival = (float)masklay_shape->frame;
|
||||
|
||||
td->center[0] = td->ival;
|
||||
td->center[1] = ypos;
|
||||
|
||||
tfd->val = (float)masklay_shape->frame;
|
||||
tfd->sdata = &masklay_shape->frame;
|
||||
|
||||
/* advance td now */
|
||||
td++;
|
||||
tfd++;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void createTransActionData(bContext *C, TransInfo *t)
|
||||
{
|
||||
Scene *scene = t->scene;
|
||||
TransData *td = NULL;
|
||||
TransData2D *td2d = NULL;
|
||||
tGPFtransdata *tfd = NULL;
|
||||
|
||||
rcti *mask = &t->ar->v2d.mask;
|
||||
rctf *datamask = &t->ar->v2d.cur;
|
||||
|
||||
float xsize = BLI_rctf_size_x(datamask);
|
||||
float ysize = BLI_rctf_size_y(datamask);
|
||||
float xmask = BLI_rcti_size_x(mask);
|
||||
float ymask = BLI_rcti_size_y(mask);
|
||||
|
||||
bAnimContext ac;
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
bAnimListElem *ale;
|
||||
int filter;
|
||||
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
|
||||
|
||||
int count = 0;
|
||||
float cfra;
|
||||
float ypos = 1.0f / ((ysize / xsize) * (xmask / ymask)) * BLI_rctf_cent_y(&t->ar->v2d.cur);
|
||||
|
||||
/* determine what type of data we are operating on */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* filter data */
|
||||
if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
|
||||
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT);
|
||||
}
|
||||
else {
|
||||
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/);
|
||||
}
|
||||
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
|
||||
|
||||
/* which side of the current frame should be allowed */
|
||||
if (t->mode == TFM_TIME_EXTEND) {
|
||||
/* only side on which mouse is gets transformed */
|
||||
float xmouse, ymouse;
|
||||
|
||||
UI_view2d_region_to_view(&ac.ar->v2d, t->mouse.imval[0], t->mouse.imval[1], &xmouse, &ymouse);
|
||||
t->frame_side = (xmouse > CFRA) ? 'R' : 'L'; // XXX use t->frame_side
|
||||
}
|
||||
else {
|
||||
/* normal transform - both sides of current frame are considered */
|
||||
t->frame_side = 'B';
|
||||
}
|
||||
|
||||
/* loop 1: fully select ipo-keys and count how many BezTriples are selected */
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
|
||||
int adt_count = 0;
|
||||
/* convert current-frame to action-time (slightly less accurate, especially under
|
||||
* higher scaling ratios, but is faster than converting all points)
|
||||
*/
|
||||
if (adt) {
|
||||
cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
|
||||
}
|
||||
else {
|
||||
cfra = (float)CFRA;
|
||||
}
|
||||
|
||||
if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) {
|
||||
adt_count = count_fcurve_keys(ale->key_data, t->frame_side, cfra, is_prop_edit);
|
||||
}
|
||||
else if (ale->type == ANIMTYPE_GPLAYER) {
|
||||
adt_count = count_gplayer_frames(ale->data, t->frame_side, cfra, is_prop_edit);
|
||||
}
|
||||
else if (ale->type == ANIMTYPE_MASKLAYER) {
|
||||
adt_count = count_masklayer_frames(ale->data, t->frame_side, cfra, is_prop_edit);
|
||||
}
|
||||
else {
|
||||
BLI_assert(0);
|
||||
}
|
||||
|
||||
if (adt_count > 0) {
|
||||
count += adt_count;
|
||||
ale->tag = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* stop if trying to build list if nothing selected */
|
||||
if (count == 0) {
|
||||
/* cleanup temp list */
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
return;
|
||||
}
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
/* allocate memory for data */
|
||||
tc->data_len = count;
|
||||
|
||||
tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(Action Editor)");
|
||||
tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "transdata2d");
|
||||
td = tc->data;
|
||||
td2d = tc->data_2d;
|
||||
|
||||
if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
|
||||
tc->custom.type.data = tfd = MEM_callocN(sizeof(tGPFtransdata) * count, "tGPFtransdata");
|
||||
tc->custom.type.use_free = true;
|
||||
}
|
||||
|
||||
/* loop 2: build transdata array */
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
|
||||
if (is_prop_edit && !ale->tag) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cfra = (float)CFRA;
|
||||
|
||||
{
|
||||
AnimData *adt;
|
||||
adt = ANIM_nla_mapping_get(&ac, ale);
|
||||
if (adt) {
|
||||
cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
|
||||
}
|
||||
}
|
||||
|
||||
if (ale->type == ANIMTYPE_GPLAYER) {
|
||||
bGPDlayer *gpl = (bGPDlayer *)ale->data;
|
||||
int i;
|
||||
|
||||
i = GPLayerToTransData(td, tfd, gpl, t->frame_side, cfra, is_prop_edit, ypos);
|
||||
td += i;
|
||||
tfd += i;
|
||||
}
|
||||
else if (ale->type == ANIMTYPE_MASKLAYER) {
|
||||
MaskLayer *masklay = (MaskLayer *)ale->data;
|
||||
int i;
|
||||
|
||||
i = MaskLayerToTransData(td, tfd, masklay, t->frame_side, cfra, is_prop_edit, ypos);
|
||||
td += i;
|
||||
tfd += i;
|
||||
}
|
||||
else {
|
||||
AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
|
||||
FCurve *fcu = (FCurve *)ale->key_data;
|
||||
|
||||
td = ActionFCurveToTransData(td, &td2d, fcu, adt, t->frame_side, cfra, is_prop_edit, ypos);
|
||||
}
|
||||
}
|
||||
|
||||
/* calculate distances for proportional editing */
|
||||
if (is_prop_edit) {
|
||||
td = tc->data;
|
||||
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
AnimData *adt;
|
||||
|
||||
/* F-Curve may not have any keyframes */
|
||||
if (!ale->tag) {
|
||||
continue;
|
||||
}
|
||||
|
||||
adt = ANIM_nla_mapping_get(&ac, ale);
|
||||
if (adt) {
|
||||
cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
|
||||
}
|
||||
else {
|
||||
cfra = (float)CFRA;
|
||||
}
|
||||
|
||||
if (ale->type == ANIMTYPE_GPLAYER) {
|
||||
bGPDlayer *gpl = (bGPDlayer *)ale->data;
|
||||
bGPDframe *gpf;
|
||||
|
||||
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
|
||||
if (gpf->flag & GP_FRAME_SELECT) {
|
||||
td->dist = td->rdist = 0.0f;
|
||||
}
|
||||
else {
|
||||
bGPDframe *gpf_iter;
|
||||
int min = INT_MAX;
|
||||
for (gpf_iter = gpl->frames.first; gpf_iter; gpf_iter = gpf_iter->next) {
|
||||
if (gpf_iter->flag & GP_FRAME_SELECT) {
|
||||
if (FrameOnMouseSide(t->frame_side, (float)gpf_iter->framenum, cfra)) {
|
||||
int val = abs(gpf->framenum - gpf_iter->framenum);
|
||||
if (val < min) {
|
||||
min = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
td->dist = td->rdist = min;
|
||||
}
|
||||
td++;
|
||||
}
|
||||
}
|
||||
else if (ale->type == ANIMTYPE_MASKLAYER) {
|
||||
MaskLayer *masklay = (MaskLayer *)ale->data;
|
||||
MaskLayerShape *masklay_shape;
|
||||
|
||||
for (masklay_shape = masklay->splines_shapes.first; masklay_shape;
|
||||
masklay_shape = masklay_shape->next) {
|
||||
if (FrameOnMouseSide(t->frame_side, (float)masklay_shape->frame, cfra)) {
|
||||
if (masklay_shape->flag & MASK_SHAPE_SELECT) {
|
||||
td->dist = td->rdist = 0.0f;
|
||||
}
|
||||
else {
|
||||
MaskLayerShape *masklay_iter;
|
||||
int min = INT_MAX;
|
||||
for (masklay_iter = masklay->splines_shapes.first; masklay_iter;
|
||||
masklay_iter = masklay_iter->next) {
|
||||
if (masklay_iter->flag & MASK_SHAPE_SELECT) {
|
||||
if (FrameOnMouseSide(t->frame_side, (float)masklay_iter->frame, cfra)) {
|
||||
int val = abs(masklay_shape->frame - masklay_iter->frame);
|
||||
if (val < min) {
|
||||
min = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
td->dist = td->rdist = min;
|
||||
}
|
||||
td++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
FCurve *fcu = (FCurve *)ale->key_data;
|
||||
BezTriple *bezt;
|
||||
int i;
|
||||
|
||||
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
|
||||
if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
|
||||
if (bezt->f2 & SELECT) {
|
||||
td->dist = td->rdist = 0.0f;
|
||||
}
|
||||
else {
|
||||
BezTriple *bezt_iter;
|
||||
int j;
|
||||
float min = FLT_MAX;
|
||||
for (j = 0, bezt_iter = fcu->bezt; j < fcu->totvert; j++, bezt_iter++) {
|
||||
if (bezt_iter->f2 & SELECT) {
|
||||
if (FrameOnMouseSide(t->frame_side, (float)bezt_iter->vec[1][0], cfra)) {
|
||||
float val = fabs(bezt->vec[1][0] - bezt_iter->vec[1][0]);
|
||||
if (val < min) {
|
||||
min = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
td->dist = td->rdist = min;
|
||||
}
|
||||
td++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* cleanup temp list */
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Action Transform Flush
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/* This function helps flush transdata written to tempdata into the gp-frames */
|
||||
void flushTransIntFrameActionData(TransInfo *t)
|
||||
{
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
tGPFtransdata *tfd = tc->custom.type.data;
|
||||
|
||||
/* flush data! */
|
||||
for (int i = 0; i < tc->data_len; i++, tfd++) {
|
||||
*(tfd->sdata) = round_fl_to_int(tfd->val);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -0,0 +1,933 @@
|
|||
/*
|
||||
* 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_armature_types.h"
|
||||
#include "DNA_constraint_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_action.h"
|
||||
#include "BKE_armature.h"
|
||||
#include "BKE_constraint.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "BIK_api.h"
|
||||
|
||||
#include "ED_armature.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
bKinematicConstraint *has_targetless_ik(bPoseChannel *pchan)
|
||||
{
|
||||
bConstraint *con = pchan->constraints.first;
|
||||
|
||||
for (; con; con = con->next) {
|
||||
if (con->type == CONSTRAINT_TYPE_KINEMATIC && (con->enforce != 0.0f)) {
|
||||
bKinematicConstraint *data = con->data;
|
||||
|
||||
if (data->tar == NULL) {
|
||||
return data;
|
||||
}
|
||||
if (data->tar->type == OB_ARMATURE && data->subtarget[0] == 0) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void add_pose_transdata(
|
||||
TransInfo *t, bPoseChannel *pchan, Object *ob, TransDataContainer *tc, TransData *td)
|
||||
{
|
||||
Bone *bone = pchan->bone;
|
||||
float pmat[3][3], omat[3][3];
|
||||
float cmat[3][3], tmat[3][3];
|
||||
float vec[3];
|
||||
|
||||
copy_v3_v3(vec, pchan->pose_mat[3]);
|
||||
copy_v3_v3(td->center, vec);
|
||||
|
||||
td->ob = ob;
|
||||
td->flag = TD_SELECTED;
|
||||
if (bone->flag & BONE_HINGE_CHILD_TRANSFORM) {
|
||||
td->flag |= TD_NOCENTER;
|
||||
}
|
||||
|
||||
if (bone->flag & BONE_TRANSFORM_CHILD) {
|
||||
td->flag |= TD_NOCENTER;
|
||||
td->flag |= TD_NO_LOC;
|
||||
}
|
||||
|
||||
td->protectflag = pchan->protectflag;
|
||||
|
||||
td->loc = pchan->loc;
|
||||
copy_v3_v3(td->iloc, pchan->loc);
|
||||
|
||||
td->ext->size = pchan->size;
|
||||
copy_v3_v3(td->ext->isize, pchan->size);
|
||||
|
||||
if (pchan->rotmode > 0) {
|
||||
td->ext->rot = pchan->eul;
|
||||
td->ext->rotAxis = NULL;
|
||||
td->ext->rotAngle = NULL;
|
||||
td->ext->quat = NULL;
|
||||
|
||||
copy_v3_v3(td->ext->irot, pchan->eul);
|
||||
}
|
||||
else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
|
||||
td->ext->rot = NULL;
|
||||
td->ext->rotAxis = pchan->rotAxis;
|
||||
td->ext->rotAngle = &pchan->rotAngle;
|
||||
td->ext->quat = NULL;
|
||||
|
||||
td->ext->irotAngle = pchan->rotAngle;
|
||||
copy_v3_v3(td->ext->irotAxis, pchan->rotAxis);
|
||||
}
|
||||
else {
|
||||
td->ext->rot = NULL;
|
||||
td->ext->rotAxis = NULL;
|
||||
td->ext->rotAngle = NULL;
|
||||
td->ext->quat = pchan->quat;
|
||||
|
||||
copy_qt_qt(td->ext->iquat, pchan->quat);
|
||||
}
|
||||
td->ext->rotOrder = pchan->rotmode;
|
||||
|
||||
/* proper way to get parent transform + own transform + constraints transform */
|
||||
copy_m3_m4(omat, ob->obmat);
|
||||
|
||||
/* New code, using "generic" BKE_bone_parent_transform_calc_from_pchan(). */
|
||||
{
|
||||
BoneParentTransform bpt;
|
||||
float rpmat[3][3];
|
||||
|
||||
BKE_bone_parent_transform_calc_from_pchan(pchan, &bpt);
|
||||
if (t->mode == TFM_TRANSLATION) {
|
||||
copy_m3_m4(pmat, bpt.loc_mat);
|
||||
}
|
||||
else {
|
||||
copy_m3_m4(pmat, bpt.rotscale_mat);
|
||||
}
|
||||
|
||||
/* Grrr! Exceptional case: When translating pose bones that are either Hinge or NoLocal,
|
||||
* and want align snapping, we just need both loc_mat and rotscale_mat.
|
||||
* So simply always store rotscale mat in td->ext, and always use it to apply rotations...
|
||||
* Ugly to need such hacks! :/ */
|
||||
copy_m3_m4(rpmat, bpt.rotscale_mat);
|
||||
|
||||
if (constraints_list_needinv(t, &pchan->constraints)) {
|
||||
copy_m3_m4(tmat, pchan->constinv);
|
||||
invert_m3_m3(cmat, tmat);
|
||||
mul_m3_series(td->mtx, cmat, omat, pmat);
|
||||
mul_m3_series(td->ext->r_mtx, cmat, omat, rpmat);
|
||||
}
|
||||
else {
|
||||
mul_m3_series(td->mtx, omat, pmat);
|
||||
mul_m3_series(td->ext->r_mtx, omat, rpmat);
|
||||
}
|
||||
invert_m3_m3(td->ext->r_smtx, td->ext->r_mtx);
|
||||
}
|
||||
|
||||
pseudoinverse_m3_m3(td->smtx, td->mtx, PSEUDOINVERSE_EPSILON);
|
||||
|
||||
/* exceptional case: rotate the pose bone which also applies transformation
|
||||
* when a parentless bone has BONE_NO_LOCAL_LOCATION [] */
|
||||
if (!ELEM(t->mode, TFM_TRANSLATION, TFM_RESIZE) &&
|
||||
(pchan->bone->flag & BONE_NO_LOCAL_LOCATION)) {
|
||||
if (pchan->parent) {
|
||||
/* same as td->smtx but without pchan->bone->bone_mat */
|
||||
td->flag |= TD_PBONE_LOCAL_MTX_C;
|
||||
mul_m3_m3m3(td->ext->l_smtx, pchan->bone->bone_mat, td->smtx);
|
||||
}
|
||||
else {
|
||||
td->flag |= TD_PBONE_LOCAL_MTX_P;
|
||||
}
|
||||
}
|
||||
|
||||
/* for axismat we use bone's own transform */
|
||||
copy_m3_m4(pmat, pchan->pose_mat);
|
||||
mul_m3_m3m3(td->axismtx, omat, pmat);
|
||||
normalize_m3(td->axismtx);
|
||||
|
||||
if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) {
|
||||
bArmature *arm = tc->poseobj->data;
|
||||
|
||||
if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) {
|
||||
td->loc = NULL;
|
||||
td->val = &bone->dist;
|
||||
td->ival = bone->dist;
|
||||
}
|
||||
else {
|
||||
// abusive storage of scale in the loc pointer :)
|
||||
td->loc = &bone->xwidth;
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
td->val = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* in this case we can do target-less IK grabbing */
|
||||
if (t->mode == TFM_TRANSLATION) {
|
||||
bKinematicConstraint *data = has_targetless_ik(pchan);
|
||||
if (data) {
|
||||
if (data->flag & CONSTRAINT_IK_TIP) {
|
||||
copy_v3_v3(data->grabtarget, pchan->pose_tail);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(data->grabtarget, pchan->pose_head);
|
||||
}
|
||||
td->loc = data->grabtarget;
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
data->flag |= CONSTRAINT_IK_AUTO;
|
||||
|
||||
/* only object matrix correction */
|
||||
copy_m3_m3(td->mtx, omat);
|
||||
pseudoinverse_m3_m3(td->smtx, td->mtx, PSEUDOINVERSE_EPSILON);
|
||||
}
|
||||
}
|
||||
|
||||
/* store reference to first constraint */
|
||||
td->con = pchan->constraints.first;
|
||||
}
|
||||
|
||||
/* adds the IK to pchan - returns if added */
|
||||
static short pose_grab_with_ik_add(bPoseChannel *pchan)
|
||||
{
|
||||
bKinematicConstraint *targetless = NULL;
|
||||
bKinematicConstraint *data;
|
||||
bConstraint *con;
|
||||
|
||||
/* Sanity check */
|
||||
if (pchan == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Rule: not if there's already an IK on this channel */
|
||||
for (con = pchan->constraints.first; con; con = con->next) {
|
||||
if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
|
||||
data = con->data;
|
||||
|
||||
if (data->tar == NULL || (data->tar->type == OB_ARMATURE && data->subtarget[0] == '\0')) {
|
||||
/* make reference to constraint to base things off later
|
||||
* (if it's the last targetless constraint encountered) */
|
||||
targetless = (bKinematicConstraint *)con->data;
|
||||
|
||||
/* but, if this is a targetless IK, we make it auto anyway (for the children loop) */
|
||||
if (con->enforce != 0.0f) {
|
||||
data->flag |= CONSTRAINT_IK_AUTO;
|
||||
|
||||
/* if no chain length has been specified,
|
||||
* just make things obey standard rotation locks too */
|
||||
if (data->rootbone == 0) {
|
||||
for (; pchan; pchan = pchan->parent) {
|
||||
/* here, we set ik-settings for bone from pchan->protectflag */
|
||||
// XXX: careful with quats/axis-angle rotations where we're locking 4d components
|
||||
if (pchan->protectflag & OB_LOCK_ROTX) {
|
||||
pchan->ikflag |= BONE_IK_NO_XDOF_TEMP;
|
||||
}
|
||||
if (pchan->protectflag & OB_LOCK_ROTY) {
|
||||
pchan->ikflag |= BONE_IK_NO_YDOF_TEMP;
|
||||
}
|
||||
if (pchan->protectflag & OB_LOCK_ROTZ) {
|
||||
pchan->ikflag |= BONE_IK_NO_ZDOF_TEMP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((con->flag & CONSTRAINT_DISABLE) == 0 && (con->enforce != 0.0f)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
con = BKE_constraint_add_for_pose(NULL, pchan, "TempConstraint", CONSTRAINT_TYPE_KINEMATIC);
|
||||
|
||||
/* for draw, but also for detecting while pose solving */
|
||||
pchan->constflag |= (PCHAN_HAS_IK | PCHAN_HAS_TARGET);
|
||||
|
||||
data = con->data;
|
||||
if (targetless) {
|
||||
/* if exists, use values from last targetless (but disabled) IK-constraint as base */
|
||||
*data = *targetless;
|
||||
}
|
||||
else {
|
||||
data->flag = CONSTRAINT_IK_TIP;
|
||||
}
|
||||
data->flag |= CONSTRAINT_IK_TEMP | CONSTRAINT_IK_AUTO | CONSTRAINT_IK_POS;
|
||||
copy_v3_v3(data->grabtarget, pchan->pose_tail);
|
||||
|
||||
/* watch-it! has to be 0 here, since we're still on the
|
||||
* same bone for the first time through the loop T25885. */
|
||||
data->rootbone = 0;
|
||||
|
||||
/* we only include bones that are part of a continual connected chain */
|
||||
do {
|
||||
/* here, we set ik-settings for bone from pchan->protectflag */
|
||||
// XXX: careful with quats/axis-angle rotations where we're locking 4d components
|
||||
if (pchan->protectflag & OB_LOCK_ROTX) {
|
||||
pchan->ikflag |= BONE_IK_NO_XDOF_TEMP;
|
||||
}
|
||||
if (pchan->protectflag & OB_LOCK_ROTY) {
|
||||
pchan->ikflag |= BONE_IK_NO_YDOF_TEMP;
|
||||
}
|
||||
if (pchan->protectflag & OB_LOCK_ROTZ) {
|
||||
pchan->ikflag |= BONE_IK_NO_ZDOF_TEMP;
|
||||
}
|
||||
|
||||
/* now we count this pchan as being included */
|
||||
data->rootbone++;
|
||||
|
||||
/* continue to parent, but only if we're connected to it */
|
||||
if (pchan->bone->flag & BONE_CONNECTED) {
|
||||
pchan = pchan->parent;
|
||||
}
|
||||
else {
|
||||
pchan = NULL;
|
||||
}
|
||||
} while (pchan);
|
||||
|
||||
/* make a copy of maximum chain-length */
|
||||
data->max_rootbone = data->rootbone;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* bone is a candidate to get IK, but we don't do it if it has children connected */
|
||||
static short pose_grab_with_ik_children(bPose *pose, Bone *bone)
|
||||
{
|
||||
Bone *bonec;
|
||||
short wentdeeper = 0, added = 0;
|
||||
|
||||
/* go deeper if children & children are connected */
|
||||
for (bonec = bone->childbase.first; bonec; bonec = bonec->next) {
|
||||
if (bonec->flag & BONE_CONNECTED) {
|
||||
wentdeeper = 1;
|
||||
added += pose_grab_with_ik_children(pose, bonec);
|
||||
}
|
||||
}
|
||||
if (wentdeeper == 0) {
|
||||
bPoseChannel *pchan = BKE_pose_channel_find_name(pose, bone->name);
|
||||
if (pchan) {
|
||||
added += pose_grab_with_ik_add(pchan);
|
||||
}
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
/* main call which adds temporal IK chains */
|
||||
static short pose_grab_with_ik(Main *bmain, Object *ob)
|
||||
{
|
||||
bArmature *arm;
|
||||
bPoseChannel *pchan, *parent;
|
||||
Bone *bonec;
|
||||
short tot_ik = 0;
|
||||
|
||||
if ((ob == NULL) || (ob->pose == NULL) || (ob->mode & OB_MODE_POSE) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
arm = ob->data;
|
||||
|
||||
/* Rule: allow multiple Bones
|
||||
* (but they must be selected, and only one ik-solver per chain should get added) */
|
||||
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
||||
if (pchan->bone->layer & arm->layer) {
|
||||
if (pchan->bone->flag & BONE_SELECTED) {
|
||||
/* Rule: no IK for solitatry (unconnected) bones */
|
||||
for (bonec = pchan->bone->childbase.first; bonec; bonec = bonec->next) {
|
||||
if (bonec->flag & BONE_CONNECTED) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((pchan->bone->flag & BONE_CONNECTED) == 0 && (bonec == NULL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* rule: if selected Bone is not a root bone, it gets a temporal IK */
|
||||
if (pchan->parent) {
|
||||
/* only adds if there's no IK yet (and no parent bone was selected) */
|
||||
for (parent = pchan->parent; parent; parent = parent->parent) {
|
||||
if (parent->bone->flag & BONE_SELECTED) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (parent == NULL) {
|
||||
tot_ik += pose_grab_with_ik_add(pchan);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* rule: go over the children and add IK to the tips */
|
||||
tot_ik += pose_grab_with_ik_children(ob->pose, pchan->bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* iTaSC needs clear for new IK constraints */
|
||||
if (tot_ik) {
|
||||
BIK_clear_data(ob->pose);
|
||||
/* TODO(sergey): Consider doing partial update only. */
|
||||
DEG_relations_tag_update(bmain);
|
||||
}
|
||||
|
||||
return (tot_ik) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void pose_mirror_info_init(PoseInitData_Mirror *pid,
|
||||
bPoseChannel *pchan,
|
||||
bPoseChannel *pchan_orig,
|
||||
bool is_mirror_relative)
|
||||
{
|
||||
pid->pchan = pchan;
|
||||
copy_v3_v3(pid->orig.loc, pchan->loc);
|
||||
copy_v3_v3(pid->orig.size, pchan->size);
|
||||
pid->orig.curve_in_x = pchan->curve_in_x;
|
||||
pid->orig.curve_out_x = pchan->curve_out_x;
|
||||
pid->orig.roll1 = pchan->roll1;
|
||||
pid->orig.roll2 = pchan->roll2;
|
||||
|
||||
if (pchan->rotmode > 0) {
|
||||
copy_v3_v3(pid->orig.eul, pchan->eul);
|
||||
}
|
||||
else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
|
||||
copy_v3_v3(pid->orig.axis_angle, pchan->rotAxis);
|
||||
pid->orig.axis_angle[3] = pchan->rotAngle;
|
||||
}
|
||||
else {
|
||||
copy_qt_qt(pid->orig.quat, pchan->quat);
|
||||
}
|
||||
|
||||
if (is_mirror_relative) {
|
||||
float pchan_mtx[4][4];
|
||||
float pchan_mtx_mirror[4][4];
|
||||
|
||||
float flip_mtx[4][4];
|
||||
unit_m4(flip_mtx);
|
||||
flip_mtx[0][0] = -1;
|
||||
|
||||
BKE_pchan_to_mat4(pchan_orig, pchan_mtx_mirror);
|
||||
BKE_pchan_to_mat4(pchan, pchan_mtx);
|
||||
|
||||
mul_m4_m4m4(pchan_mtx_mirror, pchan_mtx_mirror, flip_mtx);
|
||||
mul_m4_m4m4(pchan_mtx_mirror, flip_mtx, pchan_mtx_mirror);
|
||||
|
||||
invert_m4(pchan_mtx_mirror);
|
||||
mul_m4_m4m4(pid->offset_mtx, pchan_mtx, pchan_mtx_mirror);
|
||||
}
|
||||
else {
|
||||
unit_m4(pid->offset_mtx);
|
||||
}
|
||||
}
|
||||
|
||||
static void pose_mirror_info_restore(const PoseInitData_Mirror *pid)
|
||||
{
|
||||
bPoseChannel *pchan = pid->pchan;
|
||||
copy_v3_v3(pchan->loc, pid->orig.loc);
|
||||
copy_v3_v3(pchan->size, pid->orig.size);
|
||||
pchan->curve_in_x = pid->orig.curve_in_x;
|
||||
pchan->curve_out_x = pid->orig.curve_out_x;
|
||||
pchan->roll1 = pid->orig.roll1;
|
||||
pchan->roll2 = pid->orig.roll2;
|
||||
|
||||
if (pchan->rotmode > 0) {
|
||||
copy_v3_v3(pchan->eul, pid->orig.eul);
|
||||
}
|
||||
else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
|
||||
copy_v3_v3(pchan->rotAxis, pid->orig.axis_angle);
|
||||
pchan->rotAngle = pid->orig.axis_angle[3];
|
||||
}
|
||||
else {
|
||||
copy_qt_qt(pchan->quat, pid->orig.quat);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When objects array is NULL, use 't->data_container' as is.
|
||||
*/
|
||||
void createTransPose(TransInfo *t)
|
||||
{
|
||||
Main *bmain = CTX_data_main(t->context);
|
||||
|
||||
t->data_len_all = 0;
|
||||
|
||||
bool has_translate_rotate_buf[2] = {false, false};
|
||||
bool *has_translate_rotate = (t->mode == TFM_TRANSLATION) ? has_translate_rotate_buf : NULL;
|
||||
|
||||
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
||||
Object *ob = tc->poseobj;
|
||||
bPose *pose = ob->pose;
|
||||
|
||||
bArmature *arm;
|
||||
|
||||
/* check validity of state */
|
||||
arm = BKE_armature_from_object(tc->poseobj);
|
||||
if ((arm == NULL) || (pose == NULL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool mirror = ((pose->flag & POSE_MIRROR_EDIT) != 0);
|
||||
|
||||
/* set flags and count total */
|
||||
tc->data_len = count_set_pose_transflags(ob, t->mode, t->around, has_translate_rotate);
|
||||
if (tc->data_len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arm->flag & ARM_RESTPOS) {
|
||||
if (ELEM(t->mode, TFM_DUMMY, TFM_BONESIZE) == 0) {
|
||||
BKE_report(t->reports, RPT_ERROR, "Cannot change Pose when 'Rest Position' is enabled");
|
||||
tc->data_len = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* do we need to add temporal IK chains? */
|
||||
if ((pose->flag & POSE_AUTO_IK) && t->mode == TFM_TRANSLATION) {
|
||||
if (pose_grab_with_ik(bmain, ob)) {
|
||||
t->flag |= T_AUTOIK;
|
||||
has_translate_rotate[0] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mirror) {
|
||||
int total_mirrored = 0;
|
||||
for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
||||
if ((pchan->bone->flag & BONE_TRANSFORM) &&
|
||||
BKE_pose_channel_get_mirrored(ob->pose, pchan->name)) {
|
||||
total_mirrored++;
|
||||
}
|
||||
}
|
||||
|
||||
PoseInitData_Mirror *pid = MEM_mallocN((total_mirrored + 1) * sizeof(PoseInitData_Mirror),
|
||||
"PoseInitData_Mirror");
|
||||
|
||||
/* Trick to terminate iteration. */
|
||||
pid[total_mirrored].pchan = NULL;
|
||||
|
||||
tc->custom.type.data = pid;
|
||||
tc->custom.type.use_free = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* if there are no translatable bones, do rotation */
|
||||
if ((t->mode == TFM_TRANSLATION) && !has_translate_rotate[0]) {
|
||||
if (has_translate_rotate[1]) {
|
||||
t->mode = TFM_ROTATION;
|
||||
}
|
||||
else {
|
||||
t->mode = TFM_RESIZE;
|
||||
}
|
||||
}
|
||||
|
||||
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
||||
if (tc->data_len == 0) {
|
||||
continue;
|
||||
}
|
||||
Object *ob = tc->poseobj;
|
||||
TransData *td;
|
||||
TransDataExtension *tdx;
|
||||
int i;
|
||||
|
||||
PoseInitData_Mirror *pid = tc->custom.type.data;
|
||||
int pid_index = 0;
|
||||
bPose *pose = ob->pose;
|
||||
|
||||
if (pose == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool mirror = ((pose->flag & POSE_MIRROR_EDIT) != 0);
|
||||
const bool is_mirror_relative = ((pose->flag & POSE_MIRROR_RELATIVE) != 0);
|
||||
|
||||
tc->poseobj = ob; /* we also allow non-active objects to be transformed, in weightpaint */
|
||||
|
||||
/* init trans data */
|
||||
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransPoseBone");
|
||||
tdx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension),
|
||||
"TransPoseBoneExt");
|
||||
for (i = 0; i < tc->data_len; i++, td++, tdx++) {
|
||||
td->ext = tdx;
|
||||
td->val = NULL;
|
||||
}
|
||||
|
||||
/* use pose channels to fill trans data */
|
||||
td = tc->data;
|
||||
for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
||||
if (pchan->bone->flag & BONE_TRANSFORM) {
|
||||
add_pose_transdata(t, pchan, ob, tc, td);
|
||||
|
||||
if (mirror) {
|
||||
bPoseChannel *pchan_mirror = BKE_pose_channel_get_mirrored(ob->pose, pchan->name);
|
||||
if (pchan_mirror) {
|
||||
pose_mirror_info_init(&pid[pid_index], pchan_mirror, pchan, is_mirror_relative);
|
||||
pid_index++;
|
||||
}
|
||||
}
|
||||
|
||||
td++;
|
||||
}
|
||||
}
|
||||
|
||||
if (td != (tc->data + tc->data_len)) {
|
||||
BKE_report(t->reports, RPT_DEBUG, "Bone selection count error");
|
||||
}
|
||||
|
||||
/* initialize initial auto=ik chainlen's? */
|
||||
if (t->flag & T_AUTOIK) {
|
||||
transform_autoik_update(t, 0);
|
||||
}
|
||||
}
|
||||
|
||||
t->flag |= T_POSE;
|
||||
/* disable PET, its not usable in pose mode yet [#32444] */
|
||||
t->flag &= ~T_PROP_EDIT_ALL;
|
||||
}
|
||||
|
||||
void restoreMirrorPoseBones(TransDataContainer *tc)
|
||||
{
|
||||
bPose *pose = tc->poseobj->pose;
|
||||
|
||||
if (!(pose->flag & POSE_MIRROR_EDIT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (PoseInitData_Mirror *pid = tc->custom.type.data; pid->pchan; pid++) {
|
||||
pose_mirror_info_restore(pid);
|
||||
}
|
||||
}
|
||||
|
||||
void restoreBones(TransDataContainer *tc)
|
||||
{
|
||||
bArmature *arm;
|
||||
BoneInitData *bid = tc->custom.type.data;
|
||||
EditBone *ebo;
|
||||
|
||||
if (tc->obedit) {
|
||||
arm = tc->obedit->data;
|
||||
}
|
||||
else {
|
||||
BLI_assert(tc->poseobj != NULL);
|
||||
arm = tc->poseobj->data;
|
||||
}
|
||||
|
||||
while (bid->bone) {
|
||||
ebo = bid->bone;
|
||||
|
||||
ebo->dist = bid->dist;
|
||||
ebo->rad_head = bid->rad_head;
|
||||
ebo->rad_tail = bid->rad_tail;
|
||||
ebo->roll = bid->roll;
|
||||
ebo->xwidth = bid->xwidth;
|
||||
ebo->zwidth = bid->zwidth;
|
||||
copy_v3_v3(ebo->head, bid->head);
|
||||
copy_v3_v3(ebo->tail, bid->tail);
|
||||
|
||||
if (arm->flag & ARM_MIRROR_EDIT) {
|
||||
EditBone *ebo_child;
|
||||
|
||||
/* Also move connected ebo_child, in case ebo_child's name aren't mirrored properly */
|
||||
for (ebo_child = arm->edbo->first; ebo_child; ebo_child = ebo_child->next) {
|
||||
if ((ebo_child->flag & BONE_CONNECTED) && (ebo_child->parent == ebo)) {
|
||||
copy_v3_v3(ebo_child->head, ebo->tail);
|
||||
ebo_child->rad_head = ebo->rad_tail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Also move connected parent, in case parent's name isn't mirrored properly */
|
||||
if ((ebo->flag & BONE_CONNECTED) && ebo->parent) {
|
||||
EditBone *parent = ebo->parent;
|
||||
copy_v3_v3(parent->tail, ebo->head);
|
||||
parent->rad_tail = ebo->rad_head;
|
||||
}
|
||||
}
|
||||
|
||||
bid++;
|
||||
}
|
||||
}
|
||||
|
||||
/* ********************* armature ************** */
|
||||
void createTransArmatureVerts(TransInfo *t)
|
||||
{
|
||||
t->data_len_all = 0;
|
||||
|
||||
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
||||
EditBone *ebo, *eboflip;
|
||||
bArmature *arm = tc->obedit->data;
|
||||
ListBase *edbo = arm->edbo;
|
||||
bool mirror = ((arm->flag & ARM_MIRROR_EDIT) != 0);
|
||||
int total_mirrored = 0;
|
||||
|
||||
tc->data_len = 0;
|
||||
for (ebo = edbo->first; ebo; ebo = ebo->next) {
|
||||
const int data_len_prev = tc->data_len;
|
||||
|
||||
if (EBONE_VISIBLE(arm, ebo) && !(ebo->flag & BONE_EDITMODE_LOCKED)) {
|
||||
if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) {
|
||||
if (ebo->flag & BONE_SELECTED) {
|
||||
tc->data_len++;
|
||||
}
|
||||
}
|
||||
else if (t->mode == TFM_BONE_ROLL) {
|
||||
if (ebo->flag & BONE_SELECTED) {
|
||||
tc->data_len++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ebo->flag & BONE_TIPSEL) {
|
||||
tc->data_len++;
|
||||
}
|
||||
if (ebo->flag & BONE_ROOTSEL) {
|
||||
tc->data_len++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mirror && (data_len_prev < tc->data_len)) {
|
||||
eboflip = ED_armature_ebone_get_mirrored(arm->edbo, ebo);
|
||||
if (eboflip) {
|
||||
total_mirrored++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!tc->data_len) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mirror) {
|
||||
BoneInitData *bid = MEM_mallocN((total_mirrored + 1) * sizeof(BoneInitData), "BoneInitData");
|
||||
|
||||
/* trick to terminate iteration */
|
||||
bid[total_mirrored].bone = NULL;
|
||||
|
||||
tc->custom.type.data = bid;
|
||||
tc->custom.type.use_free = true;
|
||||
}
|
||||
t->data_len_all += tc->data_len;
|
||||
}
|
||||
|
||||
transform_around_single_fallback(t);
|
||||
t->data_len_all = -1;
|
||||
|
||||
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
||||
if (!tc->data_len) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EditBone *ebo, *eboflip;
|
||||
bArmature *arm = tc->obedit->data;
|
||||
ListBase *edbo = arm->edbo;
|
||||
TransData *td, *td_old;
|
||||
float mtx[3][3], smtx[3][3], bonemat[3][3];
|
||||
bool mirror = ((arm->flag & ARM_MIRROR_EDIT) != 0);
|
||||
BoneInitData *bid = tc->custom.type.data;
|
||||
|
||||
copy_m3_m4(mtx, tc->obedit->obmat);
|
||||
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
|
||||
|
||||
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransEditBone");
|
||||
int i = 0;
|
||||
|
||||
for (ebo = edbo->first; ebo; ebo = ebo->next) {
|
||||
td_old = td;
|
||||
ebo->oldlength =
|
||||
ebo->length; // length==0.0 on extrude, used for scaling radius of bone points
|
||||
|
||||
if (EBONE_VISIBLE(arm, ebo) && !(ebo->flag & BONE_EDITMODE_LOCKED)) {
|
||||
if (t->mode == TFM_BONE_ENVELOPE) {
|
||||
if (ebo->flag & BONE_ROOTSEL) {
|
||||
td->val = &ebo->rad_head;
|
||||
td->ival = *td->val;
|
||||
|
||||
copy_v3_v3(td->center, ebo->head);
|
||||
td->flag = TD_SELECTED;
|
||||
|
||||
copy_m3_m3(td->smtx, smtx);
|
||||
copy_m3_m3(td->mtx, mtx);
|
||||
|
||||
td->loc = NULL;
|
||||
td->ext = NULL;
|
||||
td->ob = tc->obedit;
|
||||
|
||||
td++;
|
||||
}
|
||||
if (ebo->flag & BONE_TIPSEL) {
|
||||
td->val = &ebo->rad_tail;
|
||||
td->ival = *td->val;
|
||||
copy_v3_v3(td->center, ebo->tail);
|
||||
td->flag = TD_SELECTED;
|
||||
|
||||
copy_m3_m3(td->smtx, smtx);
|
||||
copy_m3_m3(td->mtx, mtx);
|
||||
|
||||
td->loc = NULL;
|
||||
td->ext = NULL;
|
||||
td->ob = tc->obedit;
|
||||
|
||||
td++;
|
||||
}
|
||||
}
|
||||
else if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) {
|
||||
if (ebo->flag & BONE_SELECTED) {
|
||||
if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) {
|
||||
td->loc = NULL;
|
||||
td->val = &ebo->dist;
|
||||
td->ival = ebo->dist;
|
||||
}
|
||||
else {
|
||||
// abusive storage of scale in the loc pointer :)
|
||||
td->loc = &ebo->xwidth;
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
td->val = NULL;
|
||||
}
|
||||
copy_v3_v3(td->center, ebo->head);
|
||||
td->flag = TD_SELECTED;
|
||||
|
||||
/* use local bone matrix */
|
||||
ED_armature_ebone_to_mat3(ebo, bonemat);
|
||||
mul_m3_m3m3(td->mtx, mtx, bonemat);
|
||||
invert_m3_m3(td->smtx, td->mtx);
|
||||
|
||||
copy_m3_m3(td->axismtx, td->mtx);
|
||||
normalize_m3(td->axismtx);
|
||||
|
||||
td->ext = NULL;
|
||||
td->ob = tc->obedit;
|
||||
|
||||
td++;
|
||||
}
|
||||
}
|
||||
else if (t->mode == TFM_BONE_ROLL) {
|
||||
if (ebo->flag & BONE_SELECTED) {
|
||||
td->loc = NULL;
|
||||
td->val = &(ebo->roll);
|
||||
td->ival = ebo->roll;
|
||||
|
||||
copy_v3_v3(td->center, ebo->head);
|
||||
td->flag = TD_SELECTED;
|
||||
|
||||
td->ext = NULL;
|
||||
td->ob = tc->obedit;
|
||||
|
||||
td++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ebo->flag & BONE_TIPSEL) {
|
||||
copy_v3_v3(td->iloc, ebo->tail);
|
||||
|
||||
/* Don't allow single selected tips to have a modified center,
|
||||
* causes problem with snapping (see T45974).
|
||||
* However, in rotation mode, we want to keep that 'rotate bone around root with
|
||||
* only its tip selected' behavior (see T46325). */
|
||||
if ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
|
||||
((t->mode == TFM_ROTATION) || (ebo->flag & BONE_ROOTSEL))) {
|
||||
copy_v3_v3(td->center, ebo->head);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(td->center, td->iloc);
|
||||
}
|
||||
|
||||
td->loc = ebo->tail;
|
||||
td->flag = TD_SELECTED;
|
||||
if (ebo->flag & BONE_EDITMODE_LOCKED) {
|
||||
td->protectflag = OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE;
|
||||
}
|
||||
|
||||
copy_m3_m3(td->smtx, smtx);
|
||||
copy_m3_m3(td->mtx, mtx);
|
||||
|
||||
ED_armature_ebone_to_mat3(ebo, td->axismtx);
|
||||
|
||||
if ((ebo->flag & BONE_ROOTSEL) == 0) {
|
||||
td->extra = ebo;
|
||||
td->ival = ebo->roll;
|
||||
}
|
||||
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
td->ob = tc->obedit;
|
||||
|
||||
td++;
|
||||
}
|
||||
if (ebo->flag & BONE_ROOTSEL) {
|
||||
copy_v3_v3(td->iloc, ebo->head);
|
||||
copy_v3_v3(td->center, td->iloc);
|
||||
td->loc = ebo->head;
|
||||
td->flag = TD_SELECTED;
|
||||
if (ebo->flag & BONE_EDITMODE_LOCKED) {
|
||||
td->protectflag = OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE;
|
||||
}
|
||||
|
||||
copy_m3_m3(td->smtx, smtx);
|
||||
copy_m3_m3(td->mtx, mtx);
|
||||
|
||||
ED_armature_ebone_to_mat3(ebo, td->axismtx);
|
||||
|
||||
td->extra = ebo; /* to fix roll */
|
||||
td->ival = ebo->roll;
|
||||
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
td->ob = tc->obedit;
|
||||
|
||||
td++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mirror && (td_old != td)) {
|
||||
eboflip = ED_armature_ebone_get_mirrored(arm->edbo, ebo);
|
||||
if (eboflip) {
|
||||
bid[i].bone = eboflip;
|
||||
bid[i].dist = eboflip->dist;
|
||||
bid[i].rad_head = eboflip->rad_head;
|
||||
bid[i].rad_tail = eboflip->rad_tail;
|
||||
bid[i].roll = eboflip->roll;
|
||||
bid[i].xwidth = eboflip->xwidth;
|
||||
bid[i].zwidth = eboflip->zwidth;
|
||||
copy_v3_v3(bid[i].head, eboflip->head);
|
||||
copy_v3_v3(bid[i].tail, eboflip->tail);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mirror) {
|
||||
/* trick to terminate iteration */
|
||||
BLI_assert(i + 1 == (MEM_allocN_len(bid) / sizeof(*bid)));
|
||||
bid[i].bone = NULL;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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_space_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_scene.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Cursor Transform Creation
|
||||
*
|
||||
* Instead of transforming the selection, move the 2D/3D cursor.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void createTransCursor_image(TransInfo *t)
|
||||
{
|
||||
TransData *td;
|
||||
SpaceImage *sima = t->sa->spacedata.first;
|
||||
float *cursor_location = sima->cursor;
|
||||
|
||||
{
|
||||
BLI_assert(t->data_container_len == 1);
|
||||
TransDataContainer *tc = t->data_container;
|
||||
tc->data_len = 1;
|
||||
td = tc->data = MEM_callocN(sizeof(TransData), "TransTexspace");
|
||||
td->ext = tc->data_ext = MEM_callocN(sizeof(TransDataExtension), "TransTexspace");
|
||||
}
|
||||
|
||||
td->flag = TD_SELECTED;
|
||||
copy_v3_v3(td->center, cursor_location);
|
||||
td->ob = NULL;
|
||||
|
||||
unit_m3(td->mtx);
|
||||
unit_m3(td->axismtx);
|
||||
pseudoinverse_m3_m3(td->smtx, td->mtx, PSEUDOINVERSE_EPSILON);
|
||||
|
||||
td->loc = cursor_location;
|
||||
copy_v3_v3(td->iloc, cursor_location);
|
||||
}
|
||||
|
||||
void createTransCursor_view3d(TransInfo *t)
|
||||
{
|
||||
TransData *td;
|
||||
|
||||
Scene *scene = t->scene;
|
||||
if (ID_IS_LINKED(scene)) {
|
||||
BKE_report(t->reports, RPT_ERROR, "Linked data can't text-space transform");
|
||||
return;
|
||||
}
|
||||
|
||||
View3DCursor *cursor = &scene->cursor;
|
||||
{
|
||||
BLI_assert(t->data_container_len == 1);
|
||||
TransDataContainer *tc = t->data_container;
|
||||
tc->data_len = 1;
|
||||
td = tc->data = MEM_callocN(sizeof(TransData), "TransTexspace");
|
||||
td->ext = tc->data_ext = MEM_callocN(sizeof(TransDataExtension), "TransTexspace");
|
||||
}
|
||||
|
||||
td->flag = TD_SELECTED;
|
||||
copy_v3_v3(td->center, cursor->location);
|
||||
td->ob = NULL;
|
||||
|
||||
unit_m3(td->mtx);
|
||||
BKE_scene_cursor_rot_to_mat3(cursor, td->axismtx);
|
||||
normalize_m3(td->axismtx);
|
||||
pseudoinverse_m3_m3(td->smtx, td->mtx, PSEUDOINVERSE_EPSILON);
|
||||
|
||||
td->loc = cursor->location;
|
||||
copy_v3_v3(td->iloc, cursor->location);
|
||||
|
||||
if (cursor->rotation_mode > 0) {
|
||||
td->ext->rot = cursor->rotation_euler;
|
||||
td->ext->rotAxis = NULL;
|
||||
td->ext->rotAngle = NULL;
|
||||
td->ext->quat = NULL;
|
||||
|
||||
copy_v3_v3(td->ext->irot, cursor->rotation_euler);
|
||||
}
|
||||
else if (cursor->rotation_mode == ROT_MODE_AXISANGLE) {
|
||||
td->ext->rot = NULL;
|
||||
td->ext->rotAxis = cursor->rotation_axis;
|
||||
td->ext->rotAngle = &cursor->rotation_angle;
|
||||
td->ext->quat = NULL;
|
||||
|
||||
td->ext->irotAngle = cursor->rotation_angle;
|
||||
copy_v3_v3(td->ext->irotAxis, cursor->rotation_axis);
|
||||
}
|
||||
else {
|
||||
td->ext->rot = NULL;
|
||||
td->ext->rotAxis = NULL;
|
||||
td->ext->rotAngle = NULL;
|
||||
td->ext->quat = cursor->rotation_quaternion;
|
||||
|
||||
copy_qt_qt(td->ext->iquat, cursor->rotation_quaternion);
|
||||
}
|
||||
td->ext->rotOrder = cursor->rotation_mode;
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -0,0 +1,422 @@
|
|||
/*
|
||||
* 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_curve_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Curve/Surfaces Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* For the purpose of transform code we need to behave as if handles are selected,
|
||||
* even when they aren't (see special case below).
|
||||
*/
|
||||
static int bezt_select_to_transform_triple_flag(const BezTriple *bezt, const bool hide_handles)
|
||||
{
|
||||
int flag = 0;
|
||||
|
||||
if (hide_handles) {
|
||||
if (bezt->f2 & SELECT) {
|
||||
flag = (1 << 0) | (1 << 1) | (1 << 2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
flag = (((bezt->f1 & SELECT) ? (1 << 0) : 0) | ((bezt->f2 & SELECT) ? (1 << 1) : 0) |
|
||||
((bezt->f3 & SELECT) ? (1 << 2) : 0));
|
||||
}
|
||||
|
||||
/* Special case for auto & aligned handles:
|
||||
* When a center point is being moved without the handles,
|
||||
* leaving the handles stationary makes no sense and only causes strange behavior,
|
||||
* where one handle is arbitrarily anchored, the other one is aligned and lengthened
|
||||
* based on where the center point is moved. Also a bug when cancelling, see: T52007.
|
||||
*
|
||||
* A more 'correct' solution could be to store handle locations in 'TransDataCurveHandleFlags'.
|
||||
* However that doesn't resolve odd behavior, so best transform the handles in this case.
|
||||
*/
|
||||
if ((flag != ((1 << 0) | (1 << 1) | (1 << 2))) && (flag & (1 << 1))) {
|
||||
if (ELEM(bezt->h1, HD_AUTO, HD_ALIGN) && ELEM(bezt->h2, HD_AUTO, HD_ALIGN)) {
|
||||
flag = (1 << 0) | (1 << 1) | (1 << 2);
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
void createTransCurveVerts(TransInfo *t)
|
||||
{
|
||||
|
||||
#define SEL_F1 (1 << 0)
|
||||
#define SEL_F2 (1 << 1)
|
||||
#define SEL_F3 (1 << 2)
|
||||
|
||||
t->data_len_all = 0;
|
||||
|
||||
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
||||
Curve *cu = tc->obedit->data;
|
||||
BLI_assert(cu->editnurb != NULL);
|
||||
BezTriple *bezt;
|
||||
BPoint *bp;
|
||||
int a;
|
||||
int count = 0, countsel = 0;
|
||||
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
|
||||
View3D *v3d = t->view;
|
||||
short hide_handles = (v3d != NULL) ?
|
||||
((v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_HANDLES) == 0) :
|
||||
false;
|
||||
|
||||
/* count total of vertices, check identical as in 2nd loop for making transdata! */
|
||||
ListBase *nurbs = BKE_curve_editNurbs_get(cu);
|
||||
for (Nurb *nu = nurbs->first; nu; nu = nu->next) {
|
||||
if (nu->type == CU_BEZIER) {
|
||||
for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
|
||||
if (bezt->hide == 0) {
|
||||
const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, hide_handles);
|
||||
if (bezt_tx & SEL_F1) {
|
||||
countsel++;
|
||||
}
|
||||
if (bezt_tx & SEL_F2) {
|
||||
countsel++;
|
||||
}
|
||||
if (bezt_tx & SEL_F3) {
|
||||
countsel++;
|
||||
}
|
||||
if (is_prop_edit) {
|
||||
count += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) {
|
||||
if (bp->hide == 0) {
|
||||
if (is_prop_edit) {
|
||||
count++;
|
||||
}
|
||||
if (bp->f1 & SELECT) {
|
||||
countsel++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* note: in prop mode we need at least 1 selected */
|
||||
if (countsel == 0) {
|
||||
tc->data_len = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_prop_edit) {
|
||||
tc->data_len = count;
|
||||
}
|
||||
else {
|
||||
tc->data_len = countsel;
|
||||
}
|
||||
tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Curve EditMode)");
|
||||
|
||||
t->data_len_all += tc->data_len;
|
||||
}
|
||||
|
||||
transform_around_single_fallback(t);
|
||||
t->data_len_all = -1;
|
||||
|
||||
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
||||
if (tc->data_len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Curve *cu = tc->obedit->data;
|
||||
BezTriple *bezt;
|
||||
BPoint *bp;
|
||||
int a;
|
||||
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
|
||||
View3D *v3d = t->view;
|
||||
short hide_handles = (v3d != NULL) ?
|
||||
((v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_HANDLES) == 0) :
|
||||
false;
|
||||
|
||||
float mtx[3][3], smtx[3][3];
|
||||
|
||||
copy_m3_m4(mtx, tc->obedit->obmat);
|
||||
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
|
||||
|
||||
TransData *td = tc->data;
|
||||
ListBase *nurbs = BKE_curve_editNurbs_get(cu);
|
||||
for (Nurb *nu = nurbs->first; nu; nu = nu->next) {
|
||||
if (nu->type == CU_BEZIER) {
|
||||
TransData *head, *tail;
|
||||
head = tail = td;
|
||||
for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
|
||||
if (bezt->hide == 0) {
|
||||
TransDataCurveHandleFlags *hdata = NULL;
|
||||
float axismtx[3][3];
|
||||
|
||||
if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
|
||||
float normal[3], plane[3];
|
||||
|
||||
BKE_nurb_bezt_calc_normal(nu, bezt, normal);
|
||||
BKE_nurb_bezt_calc_plane(nu, bezt, plane);
|
||||
|
||||
if (createSpaceNormalTangent(axismtx, normal, plane)) {
|
||||
/* pass */
|
||||
}
|
||||
else {
|
||||
normalize_v3(normal);
|
||||
axis_dominant_v3_to_m3(axismtx, normal);
|
||||
invert_m3(axismtx);
|
||||
}
|
||||
}
|
||||
|
||||
/* Elements that will be transform (not always a match to selection). */
|
||||
const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, hide_handles);
|
||||
|
||||
if (is_prop_edit || bezt_tx & SEL_F1) {
|
||||
copy_v3_v3(td->iloc, bezt->vec[0]);
|
||||
td->loc = bezt->vec[0];
|
||||
copy_v3_v3(td->center,
|
||||
bezt->vec[(hide_handles || (t->around == V3D_AROUND_LOCAL_ORIGINS) ||
|
||||
(bezt->f2 & SELECT)) ?
|
||||
1 :
|
||||
0]);
|
||||
if (hide_handles) {
|
||||
if (bezt->f2 & SELECT) {
|
||||
td->flag = TD_SELECTED;
|
||||
}
|
||||
else {
|
||||
td->flag = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (bezt->f1 & SELECT) {
|
||||
td->flag = TD_SELECTED;
|
||||
}
|
||||
else {
|
||||
td->flag = 0;
|
||||
}
|
||||
}
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
|
||||
hdata = initTransDataCurveHandles(td, bezt);
|
||||
|
||||
copy_m3_m3(td->smtx, smtx);
|
||||
copy_m3_m3(td->mtx, mtx);
|
||||
if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
|
||||
copy_m3_m3(td->axismtx, axismtx);
|
||||
}
|
||||
|
||||
td++;
|
||||
tail++;
|
||||
}
|
||||
|
||||
/* This is the Curve Point, the other two are handles */
|
||||
if (is_prop_edit || bezt_tx & SEL_F2) {
|
||||
copy_v3_v3(td->iloc, bezt->vec[1]);
|
||||
td->loc = bezt->vec[1];
|
||||
copy_v3_v3(td->center, td->loc);
|
||||
if (bezt->f2 & SELECT) {
|
||||
td->flag = TD_SELECTED;
|
||||
}
|
||||
else {
|
||||
td->flag = 0;
|
||||
}
|
||||
td->ext = NULL;
|
||||
|
||||
/* TODO - make points scale */
|
||||
if (t->mode == TFM_CURVE_SHRINKFATTEN) { /* || t->mode==TFM_RESIZE) {*/
|
||||
td->val = &(bezt->radius);
|
||||
td->ival = bezt->radius;
|
||||
}
|
||||
else if (t->mode == TFM_TILT) {
|
||||
td->val = &(bezt->tilt);
|
||||
td->ival = bezt->tilt;
|
||||
}
|
||||
else {
|
||||
td->val = NULL;
|
||||
}
|
||||
|
||||
copy_m3_m3(td->smtx, smtx);
|
||||
copy_m3_m3(td->mtx, mtx);
|
||||
if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
|
||||
copy_m3_m3(td->axismtx, axismtx);
|
||||
}
|
||||
|
||||
if ((bezt_tx & SEL_F1) == 0 && (bezt_tx & SEL_F3) == 0) {
|
||||
/* If the middle is selected but the sides arnt, this is needed */
|
||||
if (hdata == NULL) {
|
||||
/* if the handle was not saved by the previous handle */
|
||||
hdata = initTransDataCurveHandles(td, bezt);
|
||||
}
|
||||
}
|
||||
|
||||
td++;
|
||||
tail++;
|
||||
}
|
||||
if (is_prop_edit || bezt_tx & SEL_F3) {
|
||||
copy_v3_v3(td->iloc, bezt->vec[2]);
|
||||
td->loc = bezt->vec[2];
|
||||
copy_v3_v3(td->center,
|
||||
bezt->vec[(hide_handles || (t->around == V3D_AROUND_LOCAL_ORIGINS) ||
|
||||
(bezt->f2 & SELECT)) ?
|
||||
1 :
|
||||
2]);
|
||||
if (hide_handles) {
|
||||
if (bezt->f2 & SELECT) {
|
||||
td->flag = TD_SELECTED;
|
||||
}
|
||||
else {
|
||||
td->flag = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (bezt->f3 & SELECT) {
|
||||
td->flag = TD_SELECTED;
|
||||
}
|
||||
else {
|
||||
td->flag = 0;
|
||||
}
|
||||
}
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
|
||||
if (hdata == NULL) {
|
||||
/* if the handle was not saved by the previous handle */
|
||||
hdata = initTransDataCurveHandles(td, bezt);
|
||||
}
|
||||
|
||||
copy_m3_m3(td->smtx, smtx);
|
||||
copy_m3_m3(td->mtx, mtx);
|
||||
if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
|
||||
copy_m3_m3(td->axismtx, axismtx);
|
||||
}
|
||||
|
||||
td++;
|
||||
tail++;
|
||||
}
|
||||
|
||||
(void)hdata; /* quiet warning */
|
||||
}
|
||||
else if (is_prop_edit && head != tail) {
|
||||
calc_distanceCurveVerts(head, tail - 1);
|
||||
head = tail;
|
||||
}
|
||||
}
|
||||
if (is_prop_edit && head != tail) {
|
||||
calc_distanceCurveVerts(head, tail - 1);
|
||||
}
|
||||
|
||||
/* TODO - in the case of tilt and radius we can also avoid allocating the
|
||||
* initTransDataCurveHandles but for now just don't change handle types */
|
||||
if (ELEM(t->mode, TFM_CURVE_SHRINKFATTEN, TFM_TILT, TFM_DUMMY) == 0) {
|
||||
/* sets the handles based on their selection,
|
||||
* do this after the data is copied to the TransData */
|
||||
BKE_nurb_handles_test(nu, !hide_handles);
|
||||
}
|
||||
}
|
||||
else {
|
||||
TransData *head, *tail;
|
||||
head = tail = td;
|
||||
for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) {
|
||||
if (bp->hide == 0) {
|
||||
if (is_prop_edit || (bp->f1 & SELECT)) {
|
||||
float axismtx[3][3];
|
||||
|
||||
if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
|
||||
if (nu->pntsv == 1) {
|
||||
float normal[3], plane[3];
|
||||
|
||||
BKE_nurb_bpoint_calc_normal(nu, bp, normal);
|
||||
BKE_nurb_bpoint_calc_plane(nu, bp, plane);
|
||||
|
||||
if (createSpaceNormalTangent(axismtx, normal, plane)) {
|
||||
/* pass */
|
||||
}
|
||||
else {
|
||||
normalize_v3(normal);
|
||||
axis_dominant_v3_to_m3(axismtx, normal);
|
||||
invert_m3(axismtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
copy_v3_v3(td->iloc, bp->vec);
|
||||
td->loc = bp->vec;
|
||||
copy_v3_v3(td->center, td->loc);
|
||||
if (bp->f1 & SELECT) {
|
||||
td->flag = TD_SELECTED;
|
||||
}
|
||||
else {
|
||||
td->flag = 0;
|
||||
}
|
||||
td->ext = NULL;
|
||||
|
||||
if (t->mode == TFM_CURVE_SHRINKFATTEN || t->mode == TFM_RESIZE) {
|
||||
td->val = &(bp->radius);
|
||||
td->ival = bp->radius;
|
||||
}
|
||||
else {
|
||||
td->val = &(bp->tilt);
|
||||
td->ival = bp->tilt;
|
||||
}
|
||||
|
||||
copy_m3_m3(td->smtx, smtx);
|
||||
copy_m3_m3(td->mtx, mtx);
|
||||
if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
|
||||
if (nu->pntsv == 1) {
|
||||
copy_m3_m3(td->axismtx, axismtx);
|
||||
}
|
||||
}
|
||||
|
||||
td++;
|
||||
tail++;
|
||||
}
|
||||
}
|
||||
else if (is_prop_edit && head != tail) {
|
||||
calc_distanceCurveVerts(head, tail - 1);
|
||||
head = tail;
|
||||
}
|
||||
}
|
||||
if (is_prop_edit && head != tail) {
|
||||
calc_distanceCurveVerts(head, tail - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef SEL_F1
|
||||
#undef SEL_F2
|
||||
#undef SEL_F3
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
* 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_gpencil_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_colortools.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "ED_gpencil.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Gpencil Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
static void createTransGPencil_center_get(bGPDstroke *gps, float r_center[3])
|
||||
{
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
zero_v3(r_center);
|
||||
int tot_sel = 0;
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
add_v3_v3(r_center, &pt->x);
|
||||
tot_sel++;
|
||||
}
|
||||
}
|
||||
|
||||
if (tot_sel > 0) {
|
||||
mul_v3_fl(r_center, 1.0f / tot_sel);
|
||||
}
|
||||
}
|
||||
|
||||
void createTransGPencil(bContext *C, TransInfo *t)
|
||||
{
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
|
||||
bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
|
||||
bool use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0;
|
||||
|
||||
Object *obact = CTX_data_active_object(C);
|
||||
bGPDlayer *gpl;
|
||||
TransData *td = NULL;
|
||||
float mtx[3][3], smtx[3][3];
|
||||
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
const int cfra_scene = CFRA;
|
||||
|
||||
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
|
||||
const bool is_prop_edit_connected = (t->flag & T_PROP_CONNECTED) != 0;
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
/* == Grease Pencil Strokes to Transform Data ==
|
||||
* Grease Pencil stroke points can be a mixture of 2D (screen-space),
|
||||
* or 3D coordinates. However, they're always saved as 3D points.
|
||||
* For now, we just do these without creating TransData2D for the 2D
|
||||
* strokes. This may cause issues in future though.
|
||||
*/
|
||||
tc->data_len = 0;
|
||||
|
||||
if (gpd == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* initialize falloff curve */
|
||||
if (is_multiedit) {
|
||||
BKE_curvemapping_initialize(ts->gp_sculpt.cur_falloff);
|
||||
}
|
||||
|
||||
/* First Pass: Count the number of data-points required for the strokes,
|
||||
* (and additional info about the configuration - e.g. 2D/3D?).
|
||||
*/
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
/* only editable and visible layers are considered */
|
||||
if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
|
||||
bGPDframe *gpf;
|
||||
bGPDstroke *gps;
|
||||
bGPDframe *init_gpf = gpl->actframe;
|
||||
if (is_multiedit) {
|
||||
init_gpf = gpl->frames.first;
|
||||
}
|
||||
|
||||
for (gpf = init_gpf; gpf; gpf = gpf->next) {
|
||||
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
|
||||
for (gps = gpf->strokes.first; gps; gps = gps->next) {
|
||||
/* skip strokes that are invalid for current view */
|
||||
if (ED_gpencil_stroke_can_use(C, gps) == false) {
|
||||
continue;
|
||||
}
|
||||
/* check if the color is editable */
|
||||
if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_prop_edit) {
|
||||
/* Proportional Editing... */
|
||||
if (is_prop_edit_connected) {
|
||||
/* connected only - so only if selected */
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
tc->data_len += gps->totpoints;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* everything goes - connection status doesn't matter */
|
||||
tc->data_len += gps->totpoints;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* only selected stroke points are considered */
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
// TODO: 2D vs 3D?
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
tc->data_len++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* if not multiedit out of loop */
|
||||
if (!is_multiedit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Stop trying if nothing selected */
|
||||
if (tc->data_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate memory for data */
|
||||
tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(GPencil)");
|
||||
td = tc->data;
|
||||
|
||||
unit_m3(smtx);
|
||||
unit_m3(mtx);
|
||||
|
||||
/* Second Pass: Build transdata array */
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
/* only editable and visible layers are considered */
|
||||
if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
|
||||
const int cfra = (gpl->flag & GP_LAYER_FRAMELOCK) ? gpl->actframe->framenum : cfra_scene;
|
||||
bGPDframe *gpf = gpl->actframe;
|
||||
bGPDstroke *gps;
|
||||
float diff_mat[4][4];
|
||||
float inverse_diff_mat[4][4];
|
||||
|
||||
bGPDframe *init_gpf = gpl->actframe;
|
||||
if (is_multiedit) {
|
||||
init_gpf = gpl->frames.first;
|
||||
}
|
||||
/* init multiframe falloff options */
|
||||
int f_init = 0;
|
||||
int f_end = 0;
|
||||
|
||||
if (use_multiframe_falloff) {
|
||||
BKE_gpencil_get_range_selected(gpl, &f_init, &f_end);
|
||||
}
|
||||
|
||||
/* calculate difference matrix */
|
||||
ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
|
||||
/* undo matrix */
|
||||
invert_m4_m4(inverse_diff_mat, diff_mat);
|
||||
|
||||
/* Make a new frame to work on if the layer's frame
|
||||
* and the current scene frame don't match up.
|
||||
*
|
||||
* - This is useful when animating as it saves that "uh-oh" moment when you realize you've
|
||||
* spent too much time editing the wrong frame...
|
||||
*/
|
||||
// XXX: should this be allowed when framelock is enabled?
|
||||
if ((gpf->framenum != cfra) && (!is_multiedit)) {
|
||||
gpf = BKE_gpencil_frame_addcopy(gpl, cfra);
|
||||
/* in some weird situations (framelock enabled) return NULL */
|
||||
if (gpf == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (!is_multiedit) {
|
||||
init_gpf = gpf;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop over strokes, adding TransData for points as needed... */
|
||||
for (gpf = init_gpf; gpf; gpf = gpf->next) {
|
||||
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
|
||||
|
||||
/* if multiframe and falloff, recalculate and save value */
|
||||
float falloff = 1.0f; /* by default no falloff */
|
||||
if ((is_multiedit) && (use_multiframe_falloff)) {
|
||||
/* Faloff depends on distance to active frame (relative to the overall frame range) */
|
||||
falloff = BKE_gpencil_multiframe_falloff_calc(
|
||||
gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff);
|
||||
}
|
||||
|
||||
for (gps = gpf->strokes.first; gps; gps = gps->next) {
|
||||
TransData *head = td;
|
||||
TransData *tail = td;
|
||||
bool stroke_ok;
|
||||
|
||||
/* skip strokes that are invalid for current view */
|
||||
if (ED_gpencil_stroke_can_use(C, gps) == false) {
|
||||
continue;
|
||||
}
|
||||
/* check if the color is editable */
|
||||
if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) {
|
||||
continue;
|
||||
}
|
||||
/* What we need to include depends on proportional editing settings... */
|
||||
if (is_prop_edit) {
|
||||
if (is_prop_edit_connected) {
|
||||
/* A) "Connected" - Only those in selected strokes */
|
||||
stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0;
|
||||
}
|
||||
else {
|
||||
/* B) All points, always */
|
||||
stroke_ok = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* C) Only selected points in selected strokes */
|
||||
stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0;
|
||||
}
|
||||
|
||||
/* Do stroke... */
|
||||
if (stroke_ok && gps->totpoints) {
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
/* save falloff factor */
|
||||
gps->runtime.multi_frame_falloff = falloff;
|
||||
|
||||
/* calculate stroke center */
|
||||
float center[3];
|
||||
createTransGPencil_center_get(gps, center);
|
||||
|
||||
/* add all necessary points... */
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
bool point_ok;
|
||||
|
||||
/* include point? */
|
||||
if (is_prop_edit) {
|
||||
/* Always all points in strokes that get included */
|
||||
point_ok = true;
|
||||
}
|
||||
else {
|
||||
/* Only selected points in selected strokes */
|
||||
point_ok = (pt->flag & GP_SPOINT_SELECT) != 0;
|
||||
}
|
||||
|
||||
/* do point... */
|
||||
if (point_ok) {
|
||||
copy_v3_v3(td->iloc, &pt->x);
|
||||
/* only copy center in local origins.
|
||||
* This allows get interesting effects also when move
|
||||
* using proportional editing */
|
||||
if ((gps->flag & GP_STROKE_SELECT) &&
|
||||
(ts->transform_pivot_point == V3D_AROUND_LOCAL_ORIGINS)) {
|
||||
copy_v3_v3(td->center, center);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(td->center, &pt->x);
|
||||
}
|
||||
|
||||
td->loc = &pt->x;
|
||||
|
||||
td->flag = 0;
|
||||
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
td->flag |= TD_SELECTED;
|
||||
}
|
||||
|
||||
/* for other transform modes (e.g. shrink-fatten), need to additional data
|
||||
* but never for scale or mirror
|
||||
*/
|
||||
if ((t->mode != TFM_RESIZE) && (t->mode != TFM_MIRROR)) {
|
||||
if (t->mode != TFM_GPENCIL_OPACITY) {
|
||||
td->val = &pt->pressure;
|
||||
td->ival = pt->pressure;
|
||||
}
|
||||
else {
|
||||
td->val = &pt->strength;
|
||||
td->ival = pt->strength;
|
||||
}
|
||||
}
|
||||
|
||||
/* screenspace needs special matrices... */
|
||||
if ((gps->flag & (GP_STROKE_3DSPACE | GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) ==
|
||||
0) {
|
||||
/* screenspace */
|
||||
td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ;
|
||||
}
|
||||
else {
|
||||
/* configure 2D dataspace points so that they don't play up... */
|
||||
if (gps->flag & (GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) {
|
||||
td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ;
|
||||
}
|
||||
}
|
||||
/* apply parent transformations */
|
||||
copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */
|
||||
copy_m3_m4(td->mtx, diff_mat); /* display position */
|
||||
copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */
|
||||
|
||||
/* Triangulation must be calculated again,
|
||||
* so save the stroke for recalc function */
|
||||
td->extra = gps;
|
||||
|
||||
/* save pointer to object */
|
||||
td->ob = obact;
|
||||
|
||||
td++;
|
||||
tail++;
|
||||
}
|
||||
}
|
||||
|
||||
/* March over these points, and calculate the proportional editing distances */
|
||||
if (is_prop_edit && (head != tail)) {
|
||||
/* XXX: for now, we are similar enough that this works... */
|
||||
calc_distanceCurveVerts(head, tail - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* if not multiedit out of loop */
|
||||
if (!is_multiedit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -0,0 +1,700 @@
|
|||
/*
|
||||
* 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_space_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_fcurve.h"
|
||||
#include "BKE_nla.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "ED_anim_api.h"
|
||||
#include "ED_markers.h"
|
||||
|
||||
#include "UI_view2d.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
typedef struct TransDataGraph {
|
||||
float unit_scale;
|
||||
float offset;
|
||||
} TransDataGraph;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Graph Editor Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/* Helper function for createTransGraphEditData, which is responsible for associating
|
||||
* source data with transform data
|
||||
*/
|
||||
static void bezt_to_transdata(TransData *td,
|
||||
TransData2D *td2d,
|
||||
TransDataGraph *tdg,
|
||||
AnimData *adt,
|
||||
BezTriple *bezt,
|
||||
int bi,
|
||||
bool selected,
|
||||
bool ishandle,
|
||||
bool intvals,
|
||||
float mtx[3][3],
|
||||
float smtx[3][3],
|
||||
float unit_scale,
|
||||
float offset)
|
||||
{
|
||||
float *loc = bezt->vec[bi];
|
||||
const float *cent = bezt->vec[1];
|
||||
|
||||
/* New location from td gets dumped onto the old-location of td2d, which then
|
||||
* gets copied to the actual data at td2d->loc2d (bezt->vec[n])
|
||||
*
|
||||
* Due to NLA mapping, we apply NLA mapping to some of the verts here,
|
||||
* and then that mapping will be undone after transform is done.
|
||||
*/
|
||||
|
||||
if (adt) {
|
||||
td2d->loc[0] = BKE_nla_tweakedit_remap(adt, loc[0], NLATIME_CONVERT_MAP);
|
||||
td2d->loc[1] = (loc[1] + offset) * unit_scale;
|
||||
td2d->loc[2] = 0.0f;
|
||||
td2d->loc2d = loc;
|
||||
|
||||
td->loc = td2d->loc;
|
||||
td->center[0] = BKE_nla_tweakedit_remap(adt, cent[0], NLATIME_CONVERT_MAP);
|
||||
td->center[1] = (cent[1] + offset) * unit_scale;
|
||||
td->center[2] = 0.0f;
|
||||
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
}
|
||||
else {
|
||||
td2d->loc[0] = loc[0];
|
||||
td2d->loc[1] = (loc[1] + offset) * unit_scale;
|
||||
td2d->loc[2] = 0.0f;
|
||||
td2d->loc2d = loc;
|
||||
|
||||
td->loc = td2d->loc;
|
||||
copy_v3_v3(td->center, cent);
|
||||
td->center[1] = (td->center[1] + offset) * unit_scale;
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
}
|
||||
|
||||
if (!ishandle) {
|
||||
td2d->h1 = bezt->vec[0];
|
||||
td2d->h2 = bezt->vec[2];
|
||||
copy_v2_v2(td2d->ih1, td2d->h1);
|
||||
copy_v2_v2(td2d->ih2, td2d->h2);
|
||||
}
|
||||
else {
|
||||
td2d->h1 = NULL;
|
||||
td2d->h2 = NULL;
|
||||
}
|
||||
|
||||
memset(td->axismtx, 0, sizeof(td->axismtx));
|
||||
td->axismtx[2][2] = 1.0f;
|
||||
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
|
||||
/* store AnimData info in td->extra, for applying mapping when flushing */
|
||||
td->extra = adt;
|
||||
|
||||
if (selected) {
|
||||
td->flag |= TD_SELECTED;
|
||||
td->dist = 0.0f;
|
||||
}
|
||||
else {
|
||||
td->dist = FLT_MAX;
|
||||
}
|
||||
|
||||
if (ishandle) {
|
||||
td->flag |= TD_NOTIMESNAP;
|
||||
}
|
||||
if (intvals) {
|
||||
td->flag |= TD_INTVALUES;
|
||||
}
|
||||
|
||||
/* copy space-conversion matrices for dealing with non-uniform scales */
|
||||
copy_m3_m3(td->mtx, mtx);
|
||||
copy_m3_m3(td->smtx, smtx);
|
||||
|
||||
tdg->unit_scale = unit_scale;
|
||||
tdg->offset = offset;
|
||||
}
|
||||
|
||||
static bool graph_edit_is_translation_mode(TransInfo *t)
|
||||
{
|
||||
return ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_TRANSLATE, TFM_TIME_SLIDE, TFM_TIME_DUPLICATE);
|
||||
}
|
||||
|
||||
static bool graph_edit_use_local_center(TransInfo *t)
|
||||
{
|
||||
return ((t->around == V3D_AROUND_LOCAL_ORIGINS) && (graph_edit_is_translation_mode(t) == false));
|
||||
}
|
||||
|
||||
static void graph_key_shortest_dist(
|
||||
TransInfo *t, FCurve *fcu, TransData *td_start, TransData *td, int cfra, bool use_handle)
|
||||
{
|
||||
int j = 0;
|
||||
TransData *td_iter = td_start;
|
||||
|
||||
td->dist = FLT_MAX;
|
||||
for (; j < fcu->totvert; j++) {
|
||||
BezTriple *bezt = fcu->bezt + j;
|
||||
if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
|
||||
const bool sel2 = (bezt->f2 & SELECT) != 0;
|
||||
const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2;
|
||||
const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2;
|
||||
|
||||
if (sel1 || sel2 || sel3) {
|
||||
td->dist = td->rdist = min_ff(td->dist, fabs(td_iter->center[0] - td->center[0]));
|
||||
}
|
||||
|
||||
td_iter += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void createTransGraphEditData(bContext *C, TransInfo *t)
|
||||
{
|
||||
SpaceGraph *sipo = (SpaceGraph *)t->sa->spacedata.first;
|
||||
Scene *scene = t->scene;
|
||||
ARegion *ar = t->ar;
|
||||
View2D *v2d = &ar->v2d;
|
||||
|
||||
TransData *td = NULL;
|
||||
TransData2D *td2d = NULL;
|
||||
TransDataGraph *tdg = NULL;
|
||||
|
||||
bAnimContext ac;
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
bAnimListElem *ale;
|
||||
int filter;
|
||||
|
||||
BezTriple *bezt;
|
||||
int count = 0, i;
|
||||
float mtx[3][3], smtx[3][3];
|
||||
const bool is_translation_mode = graph_edit_is_translation_mode(t);
|
||||
const bool use_handle = !(sipo->flag & SIPO_NOHANDLES);
|
||||
const bool use_local_center = graph_edit_use_local_center(t);
|
||||
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
|
||||
short anim_map_flag = ANIM_UNITCONV_ONLYSEL | ANIM_UNITCONV_SELVERTS;
|
||||
|
||||
/* determine what type of data we are operating on */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
anim_map_flag |= ANIM_get_normalization_flags(&ac);
|
||||
|
||||
/* filter data */
|
||||
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE);
|
||||
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
|
||||
|
||||
/* which side of the current frame should be allowed */
|
||||
// XXX we still want this mode, but how to get this using standard transform too?
|
||||
if (t->mode == TFM_TIME_EXTEND) {
|
||||
/* only side on which mouse is gets transformed */
|
||||
float xmouse, ymouse;
|
||||
|
||||
UI_view2d_region_to_view(v2d, t->mouse.imval[0], t->mouse.imval[1], &xmouse, &ymouse);
|
||||
t->frame_side = (xmouse > CFRA) ? 'R' : 'L'; // XXX use t->frame_side
|
||||
}
|
||||
else {
|
||||
/* normal transform - both sides of current frame are considered */
|
||||
t->frame_side = 'B';
|
||||
}
|
||||
|
||||
/* Loop 1: count how many BezTriples (specifically their verts)
|
||||
* are selected (or should be edited). */
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
|
||||
FCurve *fcu = (FCurve *)ale->key_data;
|
||||
float cfra;
|
||||
int curvecount = 0;
|
||||
bool selected = false;
|
||||
|
||||
/* F-Curve may not have any keyframes */
|
||||
if (fcu->bezt == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* convert current-frame to action-time (slightly less accurate, especially under
|
||||
* higher scaling ratios, but is faster than converting all points)
|
||||
*/
|
||||
if (adt) {
|
||||
cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
|
||||
}
|
||||
else {
|
||||
cfra = (float)CFRA;
|
||||
}
|
||||
|
||||
/* Only include BezTriples whose 'keyframe'
|
||||
* occurs on the same side of the current frame as mouse. */
|
||||
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
|
||||
if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
|
||||
const bool sel2 = (bezt->f2 & SELECT) != 0;
|
||||
const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2;
|
||||
const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2;
|
||||
|
||||
if (is_prop_edit) {
|
||||
curvecount += 3;
|
||||
if (sel2 || sel1 || sel3) {
|
||||
selected = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!is_translation_mode || !(sel2)) {
|
||||
if (sel1) {
|
||||
count++;
|
||||
}
|
||||
|
||||
if (sel3) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* only include main vert if selected */
|
||||
if (sel2 && !use_local_center) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_prop_edit) {
|
||||
if (selected) {
|
||||
count += curvecount;
|
||||
ale->tag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* stop if trying to build list if nothing selected */
|
||||
if (count == 0) {
|
||||
/* cleanup temp list */
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
return;
|
||||
}
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
/* allocate memory for data */
|
||||
tc->data_len = count;
|
||||
|
||||
tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData (Graph Editor)");
|
||||
/* For each 2d vert a 3d vector is allocated,
|
||||
* so that they can be treated just as if they were 3d verts. */
|
||||
tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransData2D (Graph Editor)");
|
||||
tc->custom.type.data = MEM_callocN(tc->data_len * sizeof(TransDataGraph), "TransDataGraph");
|
||||
tc->custom.type.use_free = true;
|
||||
|
||||
td = tc->data;
|
||||
td2d = tc->data_2d;
|
||||
tdg = tc->custom.type.data;
|
||||
|
||||
/* precompute space-conversion matrices for dealing with non-uniform scaling of Graph Editor */
|
||||
unit_m3(mtx);
|
||||
unit_m3(smtx);
|
||||
|
||||
if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) {
|
||||
float xscale, yscale;
|
||||
|
||||
/* apply scale factors to x and y axes of space-conversion matrices */
|
||||
UI_view2d_scale_get(v2d, &xscale, &yscale);
|
||||
|
||||
/* mtx is data to global (i.e. view) conversion */
|
||||
mul_v3_fl(mtx[0], xscale);
|
||||
mul_v3_fl(mtx[1], yscale);
|
||||
|
||||
/* smtx is global (i.e. view) to data conversion */
|
||||
if (IS_EQF(xscale, 0.0f) == 0) {
|
||||
mul_v3_fl(smtx[0], 1.0f / xscale);
|
||||
}
|
||||
if (IS_EQF(yscale, 0.0f) == 0) {
|
||||
mul_v3_fl(smtx[1], 1.0f / yscale);
|
||||
}
|
||||
}
|
||||
|
||||
/* loop 2: build transdata arrays */
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
|
||||
FCurve *fcu = (FCurve *)ale->key_data;
|
||||
bool intvals = (fcu->flag & FCURVE_INT_VALUES) != 0;
|
||||
float unit_scale, offset;
|
||||
float cfra;
|
||||
|
||||
/* F-Curve may not have any keyframes */
|
||||
if (fcu->bezt == NULL || (is_prop_edit && ale->tag == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* convert current-frame to action-time (slightly less accurate, especially under
|
||||
* higher scaling ratios, but is faster than converting all points)
|
||||
*/
|
||||
if (adt) {
|
||||
cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
|
||||
}
|
||||
else {
|
||||
cfra = (float)CFRA;
|
||||
}
|
||||
|
||||
unit_scale = ANIM_unit_mapping_get_factor(
|
||||
ac.scene, ale->id, ale->key_data, anim_map_flag, &offset);
|
||||
|
||||
/* only include BezTriples whose 'keyframe' occurs on the same side
|
||||
* of the current frame as mouse (if applicable) */
|
||||
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
|
||||
if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
|
||||
const bool sel2 = (bezt->f2 & SELECT) != 0;
|
||||
const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2;
|
||||
const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2;
|
||||
|
||||
TransDataCurveHandleFlags *hdata = NULL;
|
||||
/* short h1=1, h2=1; */ /* UNUSED */
|
||||
|
||||
if (is_prop_edit) {
|
||||
bool is_sel = (sel2 || sel1 || sel3);
|
||||
/* we always select all handles for proportional editing if central handle is selected */
|
||||
initTransDataCurveHandles(td, bezt);
|
||||
bezt_to_transdata(td++,
|
||||
td2d++,
|
||||
tdg++,
|
||||
adt,
|
||||
bezt,
|
||||
0,
|
||||
is_sel,
|
||||
true,
|
||||
intvals,
|
||||
mtx,
|
||||
smtx,
|
||||
unit_scale,
|
||||
offset);
|
||||
initTransDataCurveHandles(td, bezt);
|
||||
bezt_to_transdata(td++,
|
||||
td2d++,
|
||||
tdg++,
|
||||
adt,
|
||||
bezt,
|
||||
1,
|
||||
is_sel,
|
||||
false,
|
||||
intvals,
|
||||
mtx,
|
||||
smtx,
|
||||
unit_scale,
|
||||
offset);
|
||||
initTransDataCurveHandles(td, bezt);
|
||||
bezt_to_transdata(td++,
|
||||
td2d++,
|
||||
tdg++,
|
||||
adt,
|
||||
bezt,
|
||||
2,
|
||||
is_sel,
|
||||
true,
|
||||
intvals,
|
||||
mtx,
|
||||
smtx,
|
||||
unit_scale,
|
||||
offset);
|
||||
}
|
||||
else {
|
||||
/* only include handles if selected, irrespective of the interpolation modes.
|
||||
* also, only treat handles specially if the center point isn't selected.
|
||||
*/
|
||||
if (!is_translation_mode || !(sel2)) {
|
||||
if (sel1) {
|
||||
hdata = initTransDataCurveHandles(td, bezt);
|
||||
bezt_to_transdata(td++,
|
||||
td2d++,
|
||||
tdg++,
|
||||
adt,
|
||||
bezt,
|
||||
0,
|
||||
sel1,
|
||||
true,
|
||||
intvals,
|
||||
mtx,
|
||||
smtx,
|
||||
unit_scale,
|
||||
offset);
|
||||
}
|
||||
else {
|
||||
/* h1 = 0; */ /* UNUSED */
|
||||
}
|
||||
|
||||
if (sel3) {
|
||||
if (hdata == NULL) {
|
||||
hdata = initTransDataCurveHandles(td, bezt);
|
||||
}
|
||||
bezt_to_transdata(td++,
|
||||
td2d++,
|
||||
tdg++,
|
||||
adt,
|
||||
bezt,
|
||||
2,
|
||||
sel3,
|
||||
true,
|
||||
intvals,
|
||||
mtx,
|
||||
smtx,
|
||||
unit_scale,
|
||||
offset);
|
||||
}
|
||||
else {
|
||||
/* h2 = 0; */ /* UNUSED */
|
||||
}
|
||||
}
|
||||
|
||||
/* only include main vert if selected */
|
||||
if (sel2 && !use_local_center) {
|
||||
/* move handles relative to center */
|
||||
if (is_translation_mode) {
|
||||
if (sel1) {
|
||||
td->flag |= TD_MOVEHANDLE1;
|
||||
}
|
||||
if (sel3) {
|
||||
td->flag |= TD_MOVEHANDLE2;
|
||||
}
|
||||
}
|
||||
|
||||
/* if handles were not selected, store their selection status */
|
||||
if (!(sel1) || !(sel3)) {
|
||||
if (hdata == NULL) {
|
||||
hdata = initTransDataCurveHandles(td, bezt);
|
||||
}
|
||||
}
|
||||
|
||||
bezt_to_transdata(td++,
|
||||
td2d++,
|
||||
tdg++,
|
||||
adt,
|
||||
bezt,
|
||||
1,
|
||||
sel2,
|
||||
false,
|
||||
intvals,
|
||||
mtx,
|
||||
smtx,
|
||||
unit_scale,
|
||||
offset);
|
||||
}
|
||||
/* Special hack (must be done after #initTransDataCurveHandles(),
|
||||
* as that stores handle settings to restore...):
|
||||
*
|
||||
* - Check if we've got entire BezTriple selected and we're scaling/rotating that point,
|
||||
* then check if we're using auto-handles.
|
||||
* - If so, change them auto-handles to aligned handles so that handles get affected too
|
||||
*/
|
||||
if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) && ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM) &&
|
||||
ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) {
|
||||
if (hdata && (sel1) && (sel3)) {
|
||||
bezt->h1 = HD_ALIGN;
|
||||
bezt->h2 = HD_ALIGN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sets handles based on the selection */
|
||||
testhandles_fcurve(fcu, use_handle);
|
||||
}
|
||||
|
||||
if (is_prop_edit) {
|
||||
/* loop 2: build transdata arrays */
|
||||
td = tc->data;
|
||||
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
|
||||
FCurve *fcu = (FCurve *)ale->key_data;
|
||||
TransData *td_start = td;
|
||||
float cfra;
|
||||
|
||||
/* F-Curve may not have any keyframes */
|
||||
if (fcu->bezt == NULL || (ale->tag == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* convert current-frame to action-time (slightly less accurate, especially under
|
||||
* higher scaling ratios, but is faster than converting all points)
|
||||
*/
|
||||
if (adt) {
|
||||
cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
|
||||
}
|
||||
else {
|
||||
cfra = (float)CFRA;
|
||||
}
|
||||
|
||||
/* only include BezTriples whose 'keyframe' occurs on the
|
||||
* same side of the current frame as mouse (if applicable) */
|
||||
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
|
||||
if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
|
||||
const bool sel2 = (bezt->f2 & SELECT) != 0;
|
||||
const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2;
|
||||
const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2;
|
||||
|
||||
if (sel1 || sel2) {
|
||||
td->dist = td->rdist = 0.0f;
|
||||
}
|
||||
else {
|
||||
graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle);
|
||||
}
|
||||
td++;
|
||||
|
||||
if (sel2) {
|
||||
td->dist = td->rdist = 0.0f;
|
||||
}
|
||||
else {
|
||||
graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle);
|
||||
}
|
||||
td++;
|
||||
|
||||
if (sel3 || sel2) {
|
||||
td->dist = td->rdist = 0.0f;
|
||||
}
|
||||
else {
|
||||
graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle);
|
||||
}
|
||||
td++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* cleanup temp list */
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Graph Editor Transform Flush
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/* this function is called on recalcData to apply the transforms applied
|
||||
* to the transdata on to the actual keyframe data
|
||||
*/
|
||||
void flushTransGraphData(TransInfo *t)
|
||||
{
|
||||
SpaceGraph *sipo = (SpaceGraph *)t->sa->spacedata.first;
|
||||
TransData *td;
|
||||
TransData2D *td2d;
|
||||
TransDataGraph *tdg;
|
||||
Scene *scene = t->scene;
|
||||
double secf = FPS;
|
||||
int a;
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
/* flush to 2d vector from internally used 3d vector */
|
||||
for (a = 0, td = tc->data, td2d = tc->data_2d, tdg = tc->custom.type.data; a < tc->data_len;
|
||||
a++, td++, td2d++, tdg++) {
|
||||
/* pointers to relevant AnimData blocks are stored in the td->extra pointers */
|
||||
AnimData *adt = (AnimData *)td->extra;
|
||||
|
||||
float inv_unit_scale = 1.0f / tdg->unit_scale;
|
||||
|
||||
/* handle snapping for time values
|
||||
* - we should still be in NLA-mapping timespace
|
||||
* - only apply to keyframes (but never to handles)
|
||||
* - don't do this when canceling, or else these changes won't go away
|
||||
*/
|
||||
if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0) {
|
||||
switch (sipo->autosnap) {
|
||||
case SACTSNAP_FRAME: /* snap to nearest frame */
|
||||
td2d->loc[0] = floor((double)td2d->loc[0] + 0.5);
|
||||
break;
|
||||
|
||||
case SACTSNAP_SECOND: /* snap to nearest second */
|
||||
td2d->loc[0] = floor(((double)td2d->loc[0] / secf) + 0.5) * secf;
|
||||
break;
|
||||
|
||||
case SACTSNAP_MARKER: /* snap to nearest marker */
|
||||
td2d->loc[0] = (float)ED_markers_find_nearest_marker_time(&t->scene->markers,
|
||||
td2d->loc[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* we need to unapply the nla-mapping from the time in some situations */
|
||||
if (adt) {
|
||||
td2d->loc2d[0] = BKE_nla_tweakedit_remap(adt, td2d->loc[0], NLATIME_CONVERT_UNMAP);
|
||||
}
|
||||
else {
|
||||
td2d->loc2d[0] = td2d->loc[0];
|
||||
}
|
||||
|
||||
/** Time-stepping auto-snapping modes don't get applied for Graph Editor transforms,
|
||||
* as these use the generic transform modes which don't account for this sort of thing.
|
||||
* These ones aren't affected by NLA mapping, so we do this after the conversion...
|
||||
*
|
||||
* \note We also have to apply to td->loc,
|
||||
* as that's what the handle-adjustment step below looks to,
|
||||
* otherwise we get "swimming handles".
|
||||
*
|
||||
* \note We don't do this when canceling transforms, or else these changes don't go away.
|
||||
*/
|
||||
if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0 &&
|
||||
ELEM(sipo->autosnap, SACTSNAP_STEP, SACTSNAP_TSTEP)) {
|
||||
switch (sipo->autosnap) {
|
||||
case SACTSNAP_STEP: /* frame step */
|
||||
td2d->loc2d[0] = floor((double)td2d->loc[0] + 0.5);
|
||||
td->loc[0] = floor((double)td->loc[0] + 0.5);
|
||||
break;
|
||||
|
||||
case SACTSNAP_TSTEP: /* second step */
|
||||
/* XXX: the handle behavior in this case is still not quite right... */
|
||||
td2d->loc[0] = floor(((double)td2d->loc[0] / secf) + 0.5) * secf;
|
||||
td->loc[0] = floor(((double)td->loc[0] / secf) + 0.5) * secf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if int-values only, truncate to integers */
|
||||
if (td->flag & TD_INTVALUES) {
|
||||
td2d->loc2d[1] = floorf(td2d->loc[1] * inv_unit_scale - tdg->offset + 0.5f);
|
||||
}
|
||||
else {
|
||||
td2d->loc2d[1] = td2d->loc[1] * inv_unit_scale - tdg->offset;
|
||||
}
|
||||
|
||||
if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) {
|
||||
td2d->h1[0] = td2d->ih1[0] + td->loc[0] - td->iloc[0];
|
||||
td2d->h1[1] = td2d->ih1[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale;
|
||||
}
|
||||
|
||||
if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) {
|
||||
td2d->h2[0] = td2d->ih2[0] + td->loc[0] - td->iloc[0];
|
||||
td2d->h2[1] = td2d->ih2[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* 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_curve_types.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Curve/Surfaces Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void createTransLatticeVerts(TransInfo *t)
|
||||
{
|
||||
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
||||
|
||||
Lattice *latt = ((Lattice *)tc->obedit->data)->editlatt->latt;
|
||||
TransData *td = NULL;
|
||||
BPoint *bp;
|
||||
float mtx[3][3], smtx[3][3];
|
||||
int a;
|
||||
int count = 0, countsel = 0;
|
||||
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
|
||||
|
||||
bp = latt->def;
|
||||
a = latt->pntsu * latt->pntsv * latt->pntsw;
|
||||
while (a--) {
|
||||
if (bp->hide == 0) {
|
||||
if (bp->f1 & SELECT) {
|
||||
countsel++;
|
||||
}
|
||||
if (is_prop_edit) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
bp++;
|
||||
}
|
||||
|
||||
/* note: in prop mode we need at least 1 selected */
|
||||
if (countsel == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_prop_edit) {
|
||||
tc->data_len = count;
|
||||
}
|
||||
else {
|
||||
tc->data_len = countsel;
|
||||
}
|
||||
tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Lattice EditMode)");
|
||||
|
||||
copy_m3_m4(mtx, tc->obedit->obmat);
|
||||
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
|
||||
|
||||
td = tc->data;
|
||||
bp = latt->def;
|
||||
a = latt->pntsu * latt->pntsv * latt->pntsw;
|
||||
while (a--) {
|
||||
if (is_prop_edit || (bp->f1 & SELECT)) {
|
||||
if (bp->hide == 0) {
|
||||
copy_v3_v3(td->iloc, bp->vec);
|
||||
td->loc = bp->vec;
|
||||
copy_v3_v3(td->center, td->loc);
|
||||
if (bp->f1 & SELECT) {
|
||||
td->flag = TD_SELECTED;
|
||||
}
|
||||
else {
|
||||
td->flag = 0;
|
||||
}
|
||||
copy_m3_m3(td->smtx, smtx);
|
||||
copy_m3_m3(td->mtx, mtx);
|
||||
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
|
||||
td++;
|
||||
}
|
||||
}
|
||||
bp++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -0,0 +1,442 @@
|
|||
/*
|
||||
* 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_mask_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_mask.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "ED_clip.h"
|
||||
#include "ED_mask.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
typedef struct TransDataMasking {
|
||||
bool is_handle;
|
||||
|
||||
float handle[2], orig_handle[2];
|
||||
float vec[3][3];
|
||||
struct MaskSplinePoint *point;
|
||||
float parent_matrix[3][3];
|
||||
float parent_inverse_matrix[3][3];
|
||||
char orig_handle_type;
|
||||
|
||||
eMaskWhichHandle which_handle;
|
||||
} TransDataMasking;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Masking Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
static void MaskHandleToTransData(MaskSplinePoint *point,
|
||||
eMaskWhichHandle which_handle,
|
||||
TransData *td,
|
||||
TransData2D *td2d,
|
||||
TransDataMasking *tdm,
|
||||
const float asp[2],
|
||||
/*const*/ float parent_matrix[3][3],
|
||||
/*const*/ float parent_inverse_matrix[3][3])
|
||||
{
|
||||
BezTriple *bezt = &point->bezt;
|
||||
const bool is_sel_any = MASKPOINT_ISSEL_ANY(point);
|
||||
|
||||
tdm->point = point;
|
||||
copy_m3_m3(tdm->vec, bezt->vec);
|
||||
|
||||
tdm->is_handle = true;
|
||||
copy_m3_m3(tdm->parent_matrix, parent_matrix);
|
||||
copy_m3_m3(tdm->parent_inverse_matrix, parent_inverse_matrix);
|
||||
|
||||
BKE_mask_point_handle(point, which_handle, tdm->handle);
|
||||
tdm->which_handle = which_handle;
|
||||
|
||||
copy_v2_v2(tdm->orig_handle, tdm->handle);
|
||||
|
||||
mul_v2_m3v2(td2d->loc, parent_matrix, tdm->handle);
|
||||
td2d->loc[0] *= asp[0];
|
||||
td2d->loc[1] *= asp[1];
|
||||
td2d->loc[2] = 0.0f;
|
||||
|
||||
td2d->loc2d = tdm->handle;
|
||||
|
||||
td->flag = 0;
|
||||
td->loc = td2d->loc;
|
||||
mul_v2_m3v2(td->center, parent_matrix, bezt->vec[1]);
|
||||
td->center[0] *= asp[0];
|
||||
td->center[1] *= asp[1];
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
|
||||
memset(td->axismtx, 0, sizeof(td->axismtx));
|
||||
td->axismtx[2][2] = 1.0f;
|
||||
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
|
||||
if (is_sel_any) {
|
||||
td->flag |= TD_SELECTED;
|
||||
}
|
||||
|
||||
td->dist = 0.0;
|
||||
|
||||
unit_m3(td->mtx);
|
||||
unit_m3(td->smtx);
|
||||
|
||||
if (which_handle == MASK_WHICH_HANDLE_LEFT) {
|
||||
tdm->orig_handle_type = bezt->h1;
|
||||
}
|
||||
else if (which_handle == MASK_WHICH_HANDLE_RIGHT) {
|
||||
tdm->orig_handle_type = bezt->h2;
|
||||
}
|
||||
}
|
||||
|
||||
static void MaskPointToTransData(Scene *scene,
|
||||
MaskSplinePoint *point,
|
||||
TransData *td,
|
||||
TransData2D *td2d,
|
||||
TransDataMasking *tdm,
|
||||
const bool is_prop_edit,
|
||||
const float asp[2])
|
||||
{
|
||||
BezTriple *bezt = &point->bezt;
|
||||
const bool is_sel_point = MASKPOINT_ISSEL_KNOT(point);
|
||||
const bool is_sel_any = MASKPOINT_ISSEL_ANY(point);
|
||||
float parent_matrix[3][3], parent_inverse_matrix[3][3];
|
||||
|
||||
BKE_mask_point_parent_matrix_get(point, CFRA, parent_matrix);
|
||||
invert_m3_m3(parent_inverse_matrix, parent_matrix);
|
||||
|
||||
if (is_prop_edit || is_sel_point) {
|
||||
int i;
|
||||
|
||||
tdm->point = point;
|
||||
copy_m3_m3(tdm->vec, bezt->vec);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
copy_m3_m3(tdm->parent_matrix, parent_matrix);
|
||||
copy_m3_m3(tdm->parent_inverse_matrix, parent_inverse_matrix);
|
||||
|
||||
/* CV coords are scaled by aspects. this is needed for rotations and
|
||||
* proportional editing to be consistent with the stretched CV coords
|
||||
* that are displayed. this also means that for display and numinput,
|
||||
* and when the CV coords are flushed, these are converted each time */
|
||||
mul_v2_m3v2(td2d->loc, parent_matrix, bezt->vec[i]);
|
||||
td2d->loc[0] *= asp[0];
|
||||
td2d->loc[1] *= asp[1];
|
||||
td2d->loc[2] = 0.0f;
|
||||
|
||||
td2d->loc2d = bezt->vec[i];
|
||||
|
||||
td->flag = 0;
|
||||
td->loc = td2d->loc;
|
||||
mul_v2_m3v2(td->center, parent_matrix, bezt->vec[1]);
|
||||
td->center[0] *= asp[0];
|
||||
td->center[1] *= asp[1];
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
|
||||
memset(td->axismtx, 0, sizeof(td->axismtx));
|
||||
td->axismtx[2][2] = 1.0f;
|
||||
|
||||
td->ext = NULL;
|
||||
|
||||
if (i == 1) {
|
||||
/* scaling weights */
|
||||
td->val = &bezt->weight;
|
||||
td->ival = *td->val;
|
||||
}
|
||||
else {
|
||||
td->val = NULL;
|
||||
}
|
||||
|
||||
if (is_sel_any) {
|
||||
td->flag |= TD_SELECTED;
|
||||
}
|
||||
td->dist = 0.0;
|
||||
|
||||
unit_m3(td->mtx);
|
||||
unit_m3(td->smtx);
|
||||
|
||||
if (i == 0) {
|
||||
tdm->orig_handle_type = bezt->h1;
|
||||
}
|
||||
else if (i == 2) {
|
||||
tdm->orig_handle_type = bezt->h2;
|
||||
}
|
||||
|
||||
td++;
|
||||
td2d++;
|
||||
tdm++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (BKE_mask_point_handles_mode_get(point) == MASK_HANDLE_MODE_STICK) {
|
||||
MaskHandleToTransData(point,
|
||||
MASK_WHICH_HANDLE_STICK,
|
||||
td,
|
||||
td2d,
|
||||
tdm,
|
||||
asp,
|
||||
parent_matrix,
|
||||
parent_inverse_matrix);
|
||||
|
||||
td++;
|
||||
td2d++;
|
||||
tdm++;
|
||||
}
|
||||
else {
|
||||
if (bezt->f1 & SELECT) {
|
||||
MaskHandleToTransData(point,
|
||||
MASK_WHICH_HANDLE_LEFT,
|
||||
td,
|
||||
td2d,
|
||||
tdm,
|
||||
asp,
|
||||
parent_matrix,
|
||||
parent_inverse_matrix);
|
||||
|
||||
if (bezt->h1 == HD_VECT) {
|
||||
bezt->h1 = HD_FREE;
|
||||
}
|
||||
else if (bezt->h1 == HD_AUTO) {
|
||||
bezt->h1 = HD_ALIGN_DOUBLESIDE;
|
||||
bezt->h2 = HD_ALIGN_DOUBLESIDE;
|
||||
}
|
||||
|
||||
td++;
|
||||
td2d++;
|
||||
tdm++;
|
||||
}
|
||||
if (bezt->f3 & SELECT) {
|
||||
MaskHandleToTransData(point,
|
||||
MASK_WHICH_HANDLE_RIGHT,
|
||||
td,
|
||||
td2d,
|
||||
tdm,
|
||||
asp,
|
||||
parent_matrix,
|
||||
parent_inverse_matrix);
|
||||
|
||||
if (bezt->h2 == HD_VECT) {
|
||||
bezt->h2 = HD_FREE;
|
||||
}
|
||||
else if (bezt->h2 == HD_AUTO) {
|
||||
bezt->h1 = HD_ALIGN_DOUBLESIDE;
|
||||
bezt->h2 = HD_ALIGN_DOUBLESIDE;
|
||||
}
|
||||
|
||||
td++;
|
||||
td2d++;
|
||||
tdm++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void createTransMaskingData(bContext *C, TransInfo *t)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Mask *mask = CTX_data_edit_mask(C);
|
||||
MaskLayer *masklay;
|
||||
TransData *td = NULL;
|
||||
TransData2D *td2d = NULL;
|
||||
TransDataMasking *tdm = NULL;
|
||||
int count = 0, countsel = 0;
|
||||
const bool is_prop_edit = (t->flag & T_PROP_EDIT);
|
||||
float asp[2];
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
tc->data_len = 0;
|
||||
|
||||
if (!mask) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (t->spacetype == SPACE_CLIP) {
|
||||
SpaceClip *sc = t->sa->spacedata.first;
|
||||
MovieClip *clip = ED_space_clip_get_clip(sc);
|
||||
if (!clip) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* count */
|
||||
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
|
||||
MaskSpline *spline;
|
||||
|
||||
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (spline = masklay->splines.first; spline; spline = spline->next) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < spline->tot_point; i++) {
|
||||
MaskSplinePoint *point = &spline->points[i];
|
||||
|
||||
if (MASKPOINT_ISSEL_ANY(point)) {
|
||||
if (MASKPOINT_ISSEL_KNOT(point)) {
|
||||
countsel += 3;
|
||||
}
|
||||
else {
|
||||
if (BKE_mask_point_handles_mode_get(point) == MASK_HANDLE_MODE_STICK) {
|
||||
countsel += 1;
|
||||
}
|
||||
else {
|
||||
BezTriple *bezt = &point->bezt;
|
||||
if (bezt->f1 & SELECT) {
|
||||
countsel++;
|
||||
}
|
||||
if (bezt->f3 & SELECT) {
|
||||
countsel++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_prop_edit) {
|
||||
count += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* note: in prop mode we need at least 1 selected */
|
||||
if (countsel == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ED_mask_get_aspect(t->sa, t->ar, &asp[0], &asp[1]);
|
||||
|
||||
tc->data_len = (is_prop_edit) ? count : countsel;
|
||||
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Mask Editing)");
|
||||
/* for each 2d uv coord a 3d vector is allocated, so that they can be
|
||||
* treated just as if they were 3d verts */
|
||||
td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D),
|
||||
"TransObData2D(Mask Editing)");
|
||||
tc->custom.type.data = tdm = MEM_callocN(tc->data_len * sizeof(TransDataMasking),
|
||||
"TransDataMasking(Mask Editing)");
|
||||
tc->custom.type.use_free = true;
|
||||
|
||||
/* create data */
|
||||
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
|
||||
MaskSpline *spline;
|
||||
|
||||
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (spline = masklay->splines.first; spline; spline = spline->next) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < spline->tot_point; i++) {
|
||||
MaskSplinePoint *point = &spline->points[i];
|
||||
|
||||
if (is_prop_edit || MASKPOINT_ISSEL_ANY(point)) {
|
||||
MaskPointToTransData(scene, point, td, td2d, tdm, is_prop_edit, asp);
|
||||
|
||||
if (is_prop_edit || MASKPOINT_ISSEL_KNOT(point)) {
|
||||
td += 3;
|
||||
td2d += 3;
|
||||
tdm += 3;
|
||||
}
|
||||
else {
|
||||
if (BKE_mask_point_handles_mode_get(point) == MASK_HANDLE_MODE_STICK) {
|
||||
td++;
|
||||
td2d++;
|
||||
tdm++;
|
||||
}
|
||||
else {
|
||||
BezTriple *bezt = &point->bezt;
|
||||
if (bezt->f1 & SELECT) {
|
||||
td++;
|
||||
td2d++;
|
||||
tdm++;
|
||||
}
|
||||
if (bezt->f3 & SELECT) {
|
||||
td++;
|
||||
td2d++;
|
||||
tdm++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Masking Transform Flush
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void flushTransMasking(TransInfo *t)
|
||||
{
|
||||
TransData2D *td;
|
||||
TransDataMasking *tdm;
|
||||
int a;
|
||||
float asp[2], inv[2];
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
ED_mask_get_aspect(t->sa, t->ar, &asp[0], &asp[1]);
|
||||
inv[0] = 1.0f / asp[0];
|
||||
inv[1] = 1.0f / asp[1];
|
||||
|
||||
/* flush to 2d vector from internally used 3d vector */
|
||||
for (a = 0, td = tc->data_2d, tdm = tc->custom.type.data; a < tc->data_len; a++, td++, tdm++) {
|
||||
td->loc2d[0] = td->loc[0] * inv[0];
|
||||
td->loc2d[1] = td->loc[1] * inv[1];
|
||||
mul_m3_v2(tdm->parent_inverse_matrix, td->loc2d);
|
||||
|
||||
if (tdm->is_handle) {
|
||||
BKE_mask_point_set_handle(tdm->point,
|
||||
tdm->which_handle,
|
||||
td->loc2d,
|
||||
(t->flag & T_ALT_TRANSFORM) != 0,
|
||||
tdm->orig_handle,
|
||||
tdm->vec);
|
||||
}
|
||||
|
||||
if (t->state == TRANS_CANCEL) {
|
||||
if (tdm->which_handle == MASK_WHICH_HANDLE_LEFT) {
|
||||
tdm->point->bezt.h1 = tdm->orig_handle_type;
|
||||
}
|
||||
else if (tdm->which_handle == MASK_WHICH_HANDLE_RIGHT) {
|
||||
tdm->point->bezt.h2 = tdm->orig_handle_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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_meta_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Meta Elements Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void createTransMBallVerts(TransInfo *t)
|
||||
{
|
||||
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
||||
MetaBall *mb = (MetaBall *)tc->obedit->data;
|
||||
MetaElem *ml;
|
||||
TransData *td;
|
||||
TransDataExtension *tx;
|
||||
float mtx[3][3], smtx[3][3];
|
||||
int count = 0, countsel = 0;
|
||||
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
|
||||
|
||||
/* count totals */
|
||||
for (ml = mb->editelems->first; ml; ml = ml->next) {
|
||||
if (ml->flag & SELECT) {
|
||||
countsel++;
|
||||
}
|
||||
if (is_prop_edit) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* note: in prop mode we need at least 1 selected */
|
||||
if (countsel == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_prop_edit) {
|
||||
tc->data_len = count;
|
||||
}
|
||||
else {
|
||||
tc->data_len = countsel;
|
||||
}
|
||||
|
||||
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(MBall EditMode)");
|
||||
tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension),
|
||||
"MetaElement_TransExtension");
|
||||
|
||||
copy_m3_m4(mtx, tc->obedit->obmat);
|
||||
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
|
||||
|
||||
for (ml = mb->editelems->first; ml; ml = ml->next) {
|
||||
if (is_prop_edit || (ml->flag & SELECT)) {
|
||||
td->loc = &ml->x;
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
copy_v3_v3(td->center, td->loc);
|
||||
|
||||
quat_to_mat3(td->axismtx, ml->quat);
|
||||
|
||||
if (ml->flag & SELECT) {
|
||||
td->flag = TD_SELECTED | TD_USEQUAT | TD_SINGLESIZE;
|
||||
}
|
||||
else {
|
||||
td->flag = TD_USEQUAT;
|
||||
}
|
||||
|
||||
copy_m3_m3(td->smtx, smtx);
|
||||
copy_m3_m3(td->mtx, mtx);
|
||||
|
||||
td->ext = tx;
|
||||
|
||||
/* Radius of MetaElem (mass of MetaElem influence) */
|
||||
if (ml->flag & MB_SCALE_RAD) {
|
||||
td->val = &ml->rad;
|
||||
td->ival = ml->rad;
|
||||
}
|
||||
else {
|
||||
td->val = &ml->s;
|
||||
td->ival = ml->s;
|
||||
}
|
||||
|
||||
/* expx/expy/expz determine "shape" of some MetaElem types */
|
||||
tx->size = &ml->expx;
|
||||
tx->isize[0] = ml->expx;
|
||||
tx->isize[1] = ml->expy;
|
||||
tx->isize[2] = ml->expz;
|
||||
|
||||
/* quat is used for rotation of MetaElem */
|
||||
tx->quat = ml->quat;
|
||||
copy_qt_qt(tx->iquat, ml->quat);
|
||||
|
||||
tx->rot = NULL;
|
||||
|
||||
td++;
|
||||
tx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* 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_space_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "BKE_nla.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "ED_anim_api.h"
|
||||
|
||||
#include "UI_view2d.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name NLA Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void createTransNlaData(bContext *C, TransInfo *t)
|
||||
{
|
||||
Scene *scene = t->scene;
|
||||
SpaceNla *snla = NULL;
|
||||
TransData *td = NULL;
|
||||
TransDataNla *tdn = NULL;
|
||||
|
||||
bAnimContext ac;
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
bAnimListElem *ale;
|
||||
int filter;
|
||||
|
||||
int count = 0;
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
/* determine what type of data we are operating on */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
||||
return;
|
||||
}
|
||||
snla = (SpaceNla *)ac.sl;
|
||||
|
||||
/* filter data */
|
||||
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
|
||||
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
|
||||
|
||||
/* which side of the current frame should be allowed */
|
||||
if (t->mode == TFM_TIME_EXTEND) {
|
||||
/* only side on which mouse is gets transformed */
|
||||
float xmouse, ymouse;
|
||||
|
||||
UI_view2d_region_to_view(&ac.ar->v2d, t->mouse.imval[0], t->mouse.imval[1], &xmouse, &ymouse);
|
||||
t->frame_side = (xmouse > CFRA) ? 'R' : 'L';
|
||||
}
|
||||
else {
|
||||
/* normal transform - both sides of current frame are considered */
|
||||
t->frame_side = 'B';
|
||||
}
|
||||
|
||||
/* loop 1: count how many strips are selected (consider each strip as 2 points) */
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
NlaTrack *nlt = (NlaTrack *)ale->data;
|
||||
NlaStrip *strip;
|
||||
|
||||
/* make some meta-strips for chains of selected strips */
|
||||
BKE_nlastrips_make_metas(&nlt->strips, 1);
|
||||
|
||||
/* only consider selected strips */
|
||||
for (strip = nlt->strips.first; strip; strip = strip->next) {
|
||||
// TODO: we can make strips have handles later on...
|
||||
/* transition strips can't get directly transformed */
|
||||
if (strip->type != NLASTRIP_TYPE_TRANSITION) {
|
||||
if (strip->flag & NLASTRIP_FLAG_SELECT) {
|
||||
if (FrameOnMouseSide(t->frame_side, strip->start, (float)CFRA)) {
|
||||
count++;
|
||||
}
|
||||
if (FrameOnMouseSide(t->frame_side, strip->end, (float)CFRA)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* stop if trying to build list if nothing selected */
|
||||
if (count == 0) {
|
||||
/* clear temp metas that may have been created but aren't needed now
|
||||
* because they fell on the wrong side of CFRA
|
||||
*/
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
NlaTrack *nlt = (NlaTrack *)ale->data;
|
||||
BKE_nlastrips_clear_metas(&nlt->strips, 0, 1);
|
||||
}
|
||||
|
||||
/* cleanup temp list */
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate memory for data */
|
||||
tc->data_len = count;
|
||||
|
||||
tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(NLA Editor)");
|
||||
td = tc->data;
|
||||
tc->custom.type.data = tdn = MEM_callocN(tc->data_len * sizeof(TransDataNla),
|
||||
"TransDataNla (NLA Editor)");
|
||||
tc->custom.type.use_free = true;
|
||||
|
||||
/* loop 2: build transdata array */
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
/* only if a real NLA-track */
|
||||
if (ale->type == ANIMTYPE_NLATRACK) {
|
||||
AnimData *adt = ale->adt;
|
||||
NlaTrack *nlt = (NlaTrack *)ale->data;
|
||||
NlaStrip *strip;
|
||||
|
||||
/* only consider selected strips */
|
||||
for (strip = nlt->strips.first; strip; strip = strip->next) {
|
||||
// TODO: we can make strips have handles later on...
|
||||
/* transition strips can't get directly transformed */
|
||||
if (strip->type != NLASTRIP_TYPE_TRANSITION) {
|
||||
if (strip->flag & NLASTRIP_FLAG_SELECT) {
|
||||
/* our transform data is constructed as follows:
|
||||
* - only the handles on the right side of the current-frame get included
|
||||
* - td structs are transform-elements operated on by the transform system
|
||||
* and represent a single handle. The storage/pointer used (val or loc) depends on
|
||||
* whether we're scaling or transforming. Ultimately though, the handles
|
||||
* the td writes to will simply be a dummy in tdn
|
||||
* - for each strip being transformed, a single tdn struct is used, so in some
|
||||
* cases, there will need to be 1 of these tdn elements in the array skipped...
|
||||
*/
|
||||
float center[3], yval;
|
||||
|
||||
/* firstly, init tdn settings */
|
||||
tdn->id = ale->id;
|
||||
tdn->oldTrack = tdn->nlt = nlt;
|
||||
tdn->strip = strip;
|
||||
tdn->trackIndex = BLI_findindex(&adt->nla_tracks, nlt);
|
||||
|
||||
yval = (float)(tdn->trackIndex * NLACHANNEL_STEP(snla));
|
||||
|
||||
tdn->h1[0] = strip->start;
|
||||
tdn->h1[1] = yval;
|
||||
tdn->h2[0] = strip->end;
|
||||
tdn->h2[1] = yval;
|
||||
|
||||
center[0] = (float)CFRA;
|
||||
center[1] = yval;
|
||||
center[2] = 0.0f;
|
||||
|
||||
/* set td's based on which handles are applicable */
|
||||
if (FrameOnMouseSide(t->frame_side, strip->start, (float)CFRA)) {
|
||||
/* just set tdn to assume that it only has one handle for now */
|
||||
tdn->handle = -1;
|
||||
|
||||
/* now, link the transform data up to this data */
|
||||
if (ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_EXTEND)) {
|
||||
td->loc = tdn->h1;
|
||||
copy_v3_v3(td->iloc, tdn->h1);
|
||||
|
||||
/* store all the other gunk that is required by transform */
|
||||
copy_v3_v3(td->center, center);
|
||||
memset(td->axismtx, 0, sizeof(td->axismtx));
|
||||
td->axismtx[2][2] = 1.0f;
|
||||
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
|
||||
td->flag |= TD_SELECTED;
|
||||
td->dist = 0.0f;
|
||||
|
||||
unit_m3(td->mtx);
|
||||
unit_m3(td->smtx);
|
||||
}
|
||||
else {
|
||||
/* time scaling only needs single value */
|
||||
td->val = &tdn->h1[0];
|
||||
td->ival = tdn->h1[0];
|
||||
}
|
||||
|
||||
td->extra = tdn;
|
||||
td++;
|
||||
}
|
||||
if (FrameOnMouseSide(t->frame_side, strip->end, (float)CFRA)) {
|
||||
/* if tdn is already holding the start handle,
|
||||
* then we're doing both, otherwise, only end */
|
||||
tdn->handle = (tdn->handle) ? 2 : 1;
|
||||
|
||||
/* now, link the transform data up to this data */
|
||||
if (ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_EXTEND)) {
|
||||
td->loc = tdn->h2;
|
||||
copy_v3_v3(td->iloc, tdn->h2);
|
||||
|
||||
/* store all the other gunk that is required by transform */
|
||||
copy_v3_v3(td->center, center);
|
||||
memset(td->axismtx, 0, sizeof(td->axismtx));
|
||||
td->axismtx[2][2] = 1.0f;
|
||||
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
|
||||
td->flag |= TD_SELECTED;
|
||||
td->dist = 0.0f;
|
||||
|
||||
unit_m3(td->mtx);
|
||||
unit_m3(td->smtx);
|
||||
}
|
||||
else {
|
||||
/* time scaling only needs single value */
|
||||
td->val = &tdn->h2[0];
|
||||
td->ival = tdn->h2[0];
|
||||
}
|
||||
|
||||
td->extra = tdn;
|
||||
td++;
|
||||
}
|
||||
|
||||
/* If both handles were used, skip the next tdn (i.e. leave it blank)
|
||||
* since the counting code is dumb.
|
||||
* Otherwise, just advance to the next one.
|
||||
*/
|
||||
if (tdn->handle == 2) {
|
||||
tdn += 2;
|
||||
}
|
||||
else {
|
||||
tdn++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* cleanup temp list */
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* 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_space_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_node.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "ED_node.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Node Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/* transcribe given node into TransData2D for Transforming */
|
||||
static void NodeToTransData(TransData *td, TransData2D *td2d, bNode *node, const float dpi_fac)
|
||||
{
|
||||
float locx, locy;
|
||||
|
||||
/* account for parents (nested nodes) */
|
||||
if (node->parent) {
|
||||
nodeToView(node->parent, node->locx, node->locy, &locx, &locy);
|
||||
}
|
||||
else {
|
||||
locx = node->locx;
|
||||
locy = node->locy;
|
||||
}
|
||||
|
||||
/* use top-left corner as the transform origin for nodes */
|
||||
/* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */
|
||||
#ifdef USE_NODE_CENTER
|
||||
td2d->loc[0] = (locx * dpi_fac) + (BLI_rctf_size_x(&node->totr) * +0.5f);
|
||||
td2d->loc[1] = (locy * dpi_fac) + (BLI_rctf_size_y(&node->totr) * -0.5f);
|
||||
#else
|
||||
td2d->loc[0] = locx * dpi_fac;
|
||||
td2d->loc[1] = locy * dpi_fac;
|
||||
#endif
|
||||
td2d->loc[2] = 0.0f;
|
||||
td2d->loc2d = td2d->loc; /* current location */
|
||||
|
||||
td->flag = 0;
|
||||
|
||||
td->loc = td2d->loc;
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
/* use node center instead of origin (top-left corner) */
|
||||
td->center[0] = td2d->loc[0];
|
||||
td->center[1] = td2d->loc[1];
|
||||
td->center[2] = 0.0f;
|
||||
|
||||
memset(td->axismtx, 0, sizeof(td->axismtx));
|
||||
td->axismtx[2][2] = 1.0f;
|
||||
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
|
||||
td->flag |= TD_SELECTED;
|
||||
td->dist = 0.0;
|
||||
|
||||
unit_m3(td->mtx);
|
||||
unit_m3(td->smtx);
|
||||
|
||||
td->extra = node;
|
||||
}
|
||||
|
||||
static bool is_node_parent_select(bNode *node)
|
||||
{
|
||||
while ((node = node->parent)) {
|
||||
if (node->flag & NODE_TRANSFORM) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void createTransNodeData(bContext *UNUSED(C), TransInfo *t)
|
||||
{
|
||||
const float dpi_fac = UI_DPI_FAC;
|
||||
TransData *td;
|
||||
TransData2D *td2d;
|
||||
SpaceNode *snode = t->sa->spacedata.first;
|
||||
bNode *node;
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
tc->data_len = 0;
|
||||
|
||||
if (!snode->edittree) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* nodes dont support PET and probably never will */
|
||||
t->flag &= ~T_PROP_EDIT_ALL;
|
||||
|
||||
/* set transform flags on nodes */
|
||||
for (node = snode->edittree->nodes.first; node; node = node->next) {
|
||||
if (node->flag & NODE_SELECT && is_node_parent_select(node) == false) {
|
||||
node->flag |= NODE_TRANSFORM;
|
||||
tc->data_len++;
|
||||
}
|
||||
else {
|
||||
node->flag &= ~NODE_TRANSFORM;
|
||||
}
|
||||
}
|
||||
|
||||
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransNode TransData");
|
||||
td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransNode TransData2D");
|
||||
|
||||
for (node = snode->edittree->nodes.first; node; node = node->next) {
|
||||
if (node->flag & NODE_TRANSFORM) {
|
||||
NodeToTransData(td++, td2d++, node, dpi_fac);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Node Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void flushTransNodes(TransInfo *t)
|
||||
{
|
||||
const float dpi_fac = UI_DPI_FAC;
|
||||
|
||||
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
||||
int a;
|
||||
TransData *td;
|
||||
TransData2D *td2d;
|
||||
|
||||
applyGridAbsolute(t);
|
||||
|
||||
/* flush to 2d vector from internally used 3d vector */
|
||||
for (a = 0, td = tc->data, td2d = tc->data_2d; a < tc->data_len; a++, td++, td2d++) {
|
||||
bNode *node = td->extra;
|
||||
float locx, locy;
|
||||
|
||||
/* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */
|
||||
#ifdef USE_NODE_CENTER
|
||||
locx = (td2d->loc[0] - (BLI_rctf_size_x(&node->totr)) * +0.5f) / dpi_fac;
|
||||
locy = (td2d->loc[1] - (BLI_rctf_size_y(&node->totr)) * -0.5f) / dpi_fac;
|
||||
#else
|
||||
locx = td2d->loc[0] / dpi_fac;
|
||||
locy = td2d->loc[1] / dpi_fac;
|
||||
#endif
|
||||
|
||||
/* account for parents (nested nodes) */
|
||||
if (node->parent) {
|
||||
nodeFromView(node->parent, locx, locy, &node->locx, &node->locy);
|
||||
}
|
||||
else {
|
||||
node->locx = locx;
|
||||
node->locy = locy;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle intersection with noodles */
|
||||
if (tc->data_len == 1) {
|
||||
ED_node_link_intersect_test(t->sa, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -0,0 +1,936 @@
|
|||
/*
|
||||
* 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_mesh_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_compiler_compat.h"
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_layer.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_rigidbody.h"
|
||||
#include "BKE_scene.h"
|
||||
|
||||
#include "ED_object.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Object Mode Custom Data
|
||||
* \{ */
|
||||
|
||||
typedef struct TransDataObject {
|
||||
|
||||
/**
|
||||
* Object to object data transform table.
|
||||
* Don't add these to transform data because we may want to include child objects
|
||||
* which aren't being transformed.
|
||||
* - The key is object data #ID.
|
||||
* - The value is #XFormObjectData_Extra.
|
||||
*/
|
||||
struct GHash *obdata_in_obmode_map;
|
||||
|
||||
/**
|
||||
* Transform
|
||||
* - The key is object data #Object.
|
||||
* - The value is #XFormObjectSkipChild.
|
||||
*/
|
||||
struct GHash *obchild_in_obmode_map;
|
||||
|
||||
} TransDataObject;
|
||||
|
||||
static void trans_obdata_in_obmode_free_all(TransDataObject *tdo);
|
||||
static void trans_obchild_in_obmode_free_all(TransDataObject *tdo);
|
||||
|
||||
static void freeTransObjectCustomData(TransInfo *t,
|
||||
TransDataContainer *UNUSED(tc),
|
||||
TransCustomData *custom_data)
|
||||
{
|
||||
TransDataObject *tdo = custom_data->data;
|
||||
custom_data->data = NULL;
|
||||
|
||||
if (t->options & CTX_OBMODE_XFORM_OBDATA) {
|
||||
trans_obdata_in_obmode_free_all(tdo);
|
||||
}
|
||||
|
||||
if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) {
|
||||
trans_obchild_in_obmode_free_all(tdo);
|
||||
}
|
||||
MEM_freeN(tdo);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Object Data in Object Mode
|
||||
*
|
||||
* Use to implement 'Affect Only Origins' feature.
|
||||
* We need this to be detached from transform data because,
|
||||
* unlike transforming regular objects, we need to transform the children.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
struct XFormObjectData_Extra {
|
||||
Object *ob;
|
||||
float obmat_orig[4][4];
|
||||
struct XFormObjectData *xod;
|
||||
};
|
||||
|
||||
static void trans_obdata_in_obmode_ensure_object(TransDataObject *tdo, Object *ob)
|
||||
{
|
||||
if (tdo->obdata_in_obmode_map == NULL) {
|
||||
tdo->obdata_in_obmode_map = BLI_ghash_ptr_new(__func__);
|
||||
}
|
||||
|
||||
void **xf_p;
|
||||
if (!BLI_ghash_ensure_p(tdo->obdata_in_obmode_map, ob->data, &xf_p)) {
|
||||
struct XFormObjectData_Extra *xf = MEM_mallocN(sizeof(*xf), __func__);
|
||||
copy_m4_m4(xf->obmat_orig, ob->obmat);
|
||||
xf->ob = ob;
|
||||
/* Result may be NULL, that's OK. */
|
||||
xf->xod = ED_object_data_xform_create(ob->data);
|
||||
*xf_p = xf;
|
||||
}
|
||||
}
|
||||
|
||||
void trans_obdata_in_obmode_update_all(TransInfo *t)
|
||||
{
|
||||
TransDataObject *tdo = t->custom.type.data;
|
||||
if (tdo->obdata_in_obmode_map == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct Main *bmain = CTX_data_main(t->context);
|
||||
BKE_scene_graph_evaluated_ensure(t->depsgraph, bmain);
|
||||
|
||||
GHashIterator gh_iter;
|
||||
GHASH_ITER (gh_iter, tdo->obdata_in_obmode_map) {
|
||||
ID *id = BLI_ghashIterator_getKey(&gh_iter);
|
||||
struct XFormObjectData_Extra *xf = BLI_ghashIterator_getValue(&gh_iter);
|
||||
if (xf->xod == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object *ob_eval = DEG_get_evaluated_object(t->depsgraph, xf->ob);
|
||||
float imat[4][4], dmat[4][4];
|
||||
invert_m4_m4(imat, xf->obmat_orig);
|
||||
mul_m4_m4m4(dmat, imat, ob_eval->obmat);
|
||||
invert_m4(dmat);
|
||||
|
||||
ED_object_data_xform_by_mat4(xf->xod, dmat);
|
||||
DEG_id_tag_update(id, ID_RECALC_GEOMETRY);
|
||||
}
|
||||
}
|
||||
|
||||
/** Callback for #GHash free. */
|
||||
static void trans_obdata_in_obmode_free_elem(void *xf_p)
|
||||
{
|
||||
struct XFormObjectData_Extra *xf = xf_p;
|
||||
if (xf->xod) {
|
||||
ED_object_data_xform_destroy(xf->xod);
|
||||
}
|
||||
MEM_freeN(xf);
|
||||
}
|
||||
|
||||
static void trans_obdata_in_obmode_free_all(TransDataObject *tdo)
|
||||
{
|
||||
if (tdo->obdata_in_obmode_map != NULL) {
|
||||
BLI_ghash_free(tdo->obdata_in_obmode_map, NULL, trans_obdata_in_obmode_free_elem);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Object Child Skip
|
||||
*
|
||||
* Don't transform unselected children, this is done using the parent inverse matrix.
|
||||
*
|
||||
* \note The complex logic here is caused by mixed selection within a single selection chain,
|
||||
* otherwise we only need #OB_SKIP_CHILD_PARENT_IS_XFORM for single objects.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
enum {
|
||||
/**
|
||||
* The parent is transformed, this is held in place.
|
||||
*/
|
||||
OB_SKIP_CHILD_PARENT_IS_XFORM = 1,
|
||||
/**
|
||||
* The same as #OB_SKIP_CHILD_PARENT_IS_XFORM,
|
||||
* however this objects parent isn't transformed directly.
|
||||
*/
|
||||
OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT = 3,
|
||||
/**
|
||||
* Use the parent invert matrix to apply transformation,
|
||||
* this is needed, because breaks in the selection chain prevents this from being transformed.
|
||||
* This is used to add the transform which would have been added
|
||||
* if there weren't breaks in the parent/child chain.
|
||||
*/
|
||||
OB_SKIP_CHILD_PARENT_APPLY_TRANSFORM = 2,
|
||||
};
|
||||
|
||||
struct XFormObjectSkipChild {
|
||||
float obmat_orig[4][4];
|
||||
float parent_obmat_orig[4][4];
|
||||
float parent_obmat_inv_orig[4][4];
|
||||
float parent_recurse_obmat_orig[4][4];
|
||||
float parentinv_orig[4][4];
|
||||
Object *ob_parent_recurse;
|
||||
int mode;
|
||||
};
|
||||
|
||||
static void trans_obchild_in_obmode_ensure_object(TransDataObject *tdo,
|
||||
Object *ob,
|
||||
Object *ob_parent_recurse,
|
||||
int mode)
|
||||
{
|
||||
if (tdo->obchild_in_obmode_map == NULL) {
|
||||
tdo->obchild_in_obmode_map = BLI_ghash_ptr_new(__func__);
|
||||
}
|
||||
|
||||
void **xf_p;
|
||||
if (!BLI_ghash_ensure_p(tdo->obchild_in_obmode_map, ob, &xf_p)) {
|
||||
struct XFormObjectSkipChild *xf = MEM_mallocN(sizeof(*xf), __func__);
|
||||
copy_m4_m4(xf->parentinv_orig, ob->parentinv);
|
||||
copy_m4_m4(xf->obmat_orig, ob->obmat);
|
||||
copy_m4_m4(xf->parent_obmat_orig, ob->parent->obmat);
|
||||
invert_m4_m4(xf->parent_obmat_inv_orig, ob->parent->obmat);
|
||||
if (ob_parent_recurse) {
|
||||
copy_m4_m4(xf->parent_recurse_obmat_orig, ob_parent_recurse->obmat);
|
||||
}
|
||||
xf->mode = mode;
|
||||
xf->ob_parent_recurse = ob_parent_recurse;
|
||||
*xf_p = xf;
|
||||
}
|
||||
}
|
||||
|
||||
void trans_obchild_in_obmode_update_all(TransInfo *t)
|
||||
{
|
||||
TransDataObject *tdo = t->custom.type.data;
|
||||
if (tdo->obchild_in_obmode_map == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct Main *bmain = CTX_data_main(t->context);
|
||||
BKE_scene_graph_evaluated_ensure(t->depsgraph, bmain);
|
||||
|
||||
GHashIterator gh_iter;
|
||||
GHASH_ITER (gh_iter, tdo->obchild_in_obmode_map) {
|
||||
Object *ob = BLI_ghashIterator_getKey(&gh_iter);
|
||||
struct XFormObjectSkipChild *xf = BLI_ghashIterator_getValue(&gh_iter);
|
||||
|
||||
/* The following blocks below assign 'dmat'. */
|
||||
float dmat[4][4];
|
||||
|
||||
if (xf->mode == OB_SKIP_CHILD_PARENT_IS_XFORM) {
|
||||
/* Parent is transformed, this isn't so compensate. */
|
||||
Object *ob_parent_eval = DEG_get_evaluated_object(t->depsgraph, ob->parent);
|
||||
mul_m4_m4m4(dmat, xf->parent_obmat_inv_orig, ob_parent_eval->obmat);
|
||||
invert_m4(dmat);
|
||||
}
|
||||
else if (xf->mode == OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT) {
|
||||
/* Calculate parent matrix (from the root transform). */
|
||||
Object *ob_parent_recurse_eval = DEG_get_evaluated_object(t->depsgraph,
|
||||
xf->ob_parent_recurse);
|
||||
float parent_recurse_obmat_inv[4][4];
|
||||
invert_m4_m4(parent_recurse_obmat_inv, ob_parent_recurse_eval->obmat);
|
||||
mul_m4_m4m4(dmat, xf->parent_recurse_obmat_orig, parent_recurse_obmat_inv);
|
||||
invert_m4(dmat);
|
||||
float parent_obmat_calc[4][4];
|
||||
mul_m4_m4m4(parent_obmat_calc, dmat, xf->parent_obmat_orig);
|
||||
|
||||
/* Apply to the parent inverse matrix. */
|
||||
mul_m4_m4m4(dmat, xf->parent_obmat_inv_orig, parent_obmat_calc);
|
||||
invert_m4(dmat);
|
||||
}
|
||||
else {
|
||||
BLI_assert(xf->mode == OB_SKIP_CHILD_PARENT_APPLY_TRANSFORM);
|
||||
/* Transform this - without transform data. */
|
||||
Object *ob_parent_recurse_eval = DEG_get_evaluated_object(t->depsgraph,
|
||||
xf->ob_parent_recurse);
|
||||
float parent_recurse_obmat_inv[4][4];
|
||||
invert_m4_m4(parent_recurse_obmat_inv, ob_parent_recurse_eval->obmat);
|
||||
mul_m4_m4m4(dmat, xf->parent_recurse_obmat_orig, parent_recurse_obmat_inv);
|
||||
invert_m4(dmat);
|
||||
float obmat_calc[4][4];
|
||||
mul_m4_m4m4(obmat_calc, dmat, xf->obmat_orig);
|
||||
/* obmat_calc is just obmat. */
|
||||
|
||||
/* Get the matrices relative to the parent. */
|
||||
float obmat_parent_relative_orig[4][4];
|
||||
float obmat_parent_relative_calc[4][4];
|
||||
float obmat_parent_relative_inv_orig[4][4];
|
||||
|
||||
mul_m4_m4m4(obmat_parent_relative_orig, xf->parent_obmat_inv_orig, xf->obmat_orig);
|
||||
mul_m4_m4m4(obmat_parent_relative_calc, xf->parent_obmat_inv_orig, obmat_calc);
|
||||
invert_m4_m4(obmat_parent_relative_inv_orig, obmat_parent_relative_orig);
|
||||
|
||||
/* Apply to the parent inverse matrix. */
|
||||
mul_m4_m4m4(dmat, obmat_parent_relative_calc, obmat_parent_relative_inv_orig);
|
||||
}
|
||||
|
||||
mul_m4_m4m4(ob->parentinv, dmat, xf->parentinv_orig);
|
||||
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
|
||||
}
|
||||
}
|
||||
|
||||
static void trans_obchild_in_obmode_free_all(TransDataObject *tdo)
|
||||
{
|
||||
if (tdo->obchild_in_obmode_map != NULL) {
|
||||
BLI_ghash_free(tdo->obchild_in_obmode_map, NULL, MEM_freeN);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Object Transform Creation
|
||||
*
|
||||
* Instead of transforming the selection, move the 2D/3D cursor.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/* *********************** Object Transform data ******************* */
|
||||
|
||||
/* transcribe given object into TransData for Transforming */
|
||||
static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob)
|
||||
{
|
||||
Scene *scene = t->scene;
|
||||
bool constinv;
|
||||
bool skip_invert = false;
|
||||
|
||||
if (t->mode != TFM_DUMMY && ob->rigidbody_object) {
|
||||
float rot[3][3], scale[3];
|
||||
float ctime = BKE_scene_frame_get(scene);
|
||||
|
||||
/* only use rigid body transform if simulation is running,
|
||||
* avoids problems with initial setup of rigid bodies */
|
||||
if (BKE_rigidbody_check_sim_running(scene->rigidbody_world, ctime)) {
|
||||
|
||||
/* save original object transform */
|
||||
copy_v3_v3(td->ext->oloc, ob->loc);
|
||||
|
||||
if (ob->rotmode > 0) {
|
||||
copy_v3_v3(td->ext->orot, ob->rot);
|
||||
}
|
||||
else if (ob->rotmode == ROT_MODE_AXISANGLE) {
|
||||
td->ext->orotAngle = ob->rotAngle;
|
||||
copy_v3_v3(td->ext->orotAxis, ob->rotAxis);
|
||||
}
|
||||
else {
|
||||
copy_qt_qt(td->ext->oquat, ob->quat);
|
||||
}
|
||||
/* update object's loc/rot to get current rigid body transform */
|
||||
mat4_to_loc_rot_size(ob->loc, rot, scale, ob->obmat);
|
||||
sub_v3_v3(ob->loc, ob->dloc);
|
||||
BKE_object_mat3_to_rot(ob, rot, false); /* drot is already corrected here */
|
||||
}
|
||||
}
|
||||
|
||||
/* axismtx has the real orientation */
|
||||
copy_m3_m4(td->axismtx, ob->obmat);
|
||||
normalize_m3(td->axismtx);
|
||||
|
||||
td->con = ob->constraints.first;
|
||||
|
||||
/* hack: temporarily disable tracking and/or constraints when getting
|
||||
* object matrix, if tracking is on, or if constraints don't need
|
||||
* inverse correction to stop it from screwing up space conversion
|
||||
* matrix later
|
||||
*/
|
||||
constinv = constraints_list_needinv(t, &ob->constraints);
|
||||
|
||||
/* disable constraints inversion for dummy pass */
|
||||
if (t->mode == TFM_DUMMY) {
|
||||
skip_invert = true;
|
||||
}
|
||||
|
||||
/* NOTE: This is not really following copy-on-write design and we should not
|
||||
* be re-evaluating the evaluated object. But as the comment above mentioned
|
||||
* this is part of a hack.
|
||||
* More proper solution would be to make a shallow copy of the object and
|
||||
* evaluate that, and access matrix of that evaluated copy of the object.
|
||||
* Might be more tricky than it sounds, if some logic later on accesses the
|
||||
* object matrix via td->ob->obmat. */
|
||||
Object *object_eval = DEG_get_evaluated_object(t->depsgraph, ob);
|
||||
if (skip_invert == false && constinv == false) {
|
||||
object_eval->transflag |= OB_NO_CONSTRAINTS; /* BKE_object_where_is_calc checks this */
|
||||
/* It is possible to have transform data initialization prior to a
|
||||
* complete dependency graph evaluated. Happens, for example, when
|
||||
* changing transformation mode. */
|
||||
BKE_object_tfm_copy(object_eval, ob);
|
||||
BKE_object_where_is_calc(t->depsgraph, t->scene, object_eval);
|
||||
object_eval->transflag &= ~OB_NO_CONSTRAINTS;
|
||||
}
|
||||
else {
|
||||
BKE_object_where_is_calc(t->depsgraph, t->scene, object_eval);
|
||||
}
|
||||
/* Copy newly evaluated fields to the original object, similar to how
|
||||
* active dependency graph will do it. */
|
||||
copy_m4_m4(ob->obmat, object_eval->obmat);
|
||||
/* Only copy negative scale flag, this is the only flag which is modified by
|
||||
* the BKE_object_where_is_calc(). The rest of the flags we need to keep,
|
||||
* otherwise we might loose dupli flags (see T61787). */
|
||||
ob->transflag &= ~OB_NEG_SCALE;
|
||||
ob->transflag |= (object_eval->transflag & OB_NEG_SCALE);
|
||||
|
||||
td->ob = ob;
|
||||
|
||||
td->loc = ob->loc;
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
|
||||
if (ob->rotmode > 0) {
|
||||
td->ext->rot = ob->rot;
|
||||
td->ext->rotAxis = NULL;
|
||||
td->ext->rotAngle = NULL;
|
||||
td->ext->quat = NULL;
|
||||
|
||||
copy_v3_v3(td->ext->irot, ob->rot);
|
||||
copy_v3_v3(td->ext->drot, ob->drot);
|
||||
}
|
||||
else if (ob->rotmode == ROT_MODE_AXISANGLE) {
|
||||
td->ext->rot = NULL;
|
||||
td->ext->rotAxis = ob->rotAxis;
|
||||
td->ext->rotAngle = &ob->rotAngle;
|
||||
td->ext->quat = NULL;
|
||||
|
||||
td->ext->irotAngle = ob->rotAngle;
|
||||
copy_v3_v3(td->ext->irotAxis, ob->rotAxis);
|
||||
// td->ext->drotAngle = ob->drotAngle; // XXX, not implemented
|
||||
// copy_v3_v3(td->ext->drotAxis, ob->drotAxis); // XXX, not implemented
|
||||
}
|
||||
else {
|
||||
td->ext->rot = NULL;
|
||||
td->ext->rotAxis = NULL;
|
||||
td->ext->rotAngle = NULL;
|
||||
td->ext->quat = ob->quat;
|
||||
|
||||
copy_qt_qt(td->ext->iquat, ob->quat);
|
||||
copy_qt_qt(td->ext->dquat, ob->dquat);
|
||||
}
|
||||
td->ext->rotOrder = ob->rotmode;
|
||||
|
||||
td->ext->size = ob->scale;
|
||||
copy_v3_v3(td->ext->isize, ob->scale);
|
||||
copy_v3_v3(td->ext->dscale, ob->dscale);
|
||||
|
||||
copy_v3_v3(td->center, ob->obmat[3]);
|
||||
|
||||
copy_m4_m4(td->ext->obmat, ob->obmat);
|
||||
|
||||
/* is there a need to set the global<->data space conversion matrices? */
|
||||
if (ob->parent || constinv) {
|
||||
float obmtx[3][3], totmat[3][3], obinv[3][3];
|
||||
|
||||
/* Get the effect of parenting, and/or certain constraints.
|
||||
* NOTE: some Constraints, and also Tracking should never get this
|
||||
* done, as it doesn't work well.
|
||||
*/
|
||||
BKE_object_to_mat3(ob, obmtx);
|
||||
copy_m3_m4(totmat, ob->obmat);
|
||||
invert_m3_m3(obinv, totmat);
|
||||
mul_m3_m3m3(td->smtx, obmtx, obinv);
|
||||
invert_m3_m3(td->mtx, td->smtx);
|
||||
}
|
||||
else {
|
||||
/* no conversion to/from dataspace */
|
||||
unit_m3(td->smtx);
|
||||
unit_m3(td->mtx);
|
||||
}
|
||||
}
|
||||
|
||||
static void trans_object_base_deps_flag_prepare(ViewLayer *view_layer)
|
||||
{
|
||||
for (Base *base = view_layer->object_bases.first; base; base = base->next) {
|
||||
base->object->id.tag &= ~LIB_TAG_DOIT;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_trans_object_base_deps_flag_cb(ID *id,
|
||||
eDepsObjectComponentType component,
|
||||
void *UNUSED(user_data))
|
||||
{
|
||||
/* Here we only handle object IDs. */
|
||||
if (GS(id->name) != ID_OB) {
|
||||
return;
|
||||
}
|
||||
if (!ELEM(component, DEG_OB_COMP_TRANSFORM, DEG_OB_COMP_GEOMETRY)) {
|
||||
return;
|
||||
}
|
||||
id->tag |= LIB_TAG_DOIT;
|
||||
}
|
||||
|
||||
static void flush_trans_object_base_deps_flag(Depsgraph *depsgraph, Object *object)
|
||||
{
|
||||
object->id.tag |= LIB_TAG_DOIT;
|
||||
DEG_foreach_dependent_ID_component(
|
||||
depsgraph, &object->id, DEG_OB_COMP_TRANSFORM, set_trans_object_base_deps_flag_cb, NULL);
|
||||
}
|
||||
|
||||
static void trans_object_base_deps_flag_finish(const TransInfo *t, ViewLayer *view_layer)
|
||||
{
|
||||
|
||||
if ((t->options & CTX_OBMODE_XFORM_OBDATA) == 0) {
|
||||
for (Base *base = view_layer->object_bases.first; base; base = base->next) {
|
||||
if (base->object->id.tag & LIB_TAG_DOIT) {
|
||||
base->flag_legacy |= BA_SNAP_FIX_DEPS_FIASCO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* sets flags in Bases to define whether they take part in transform */
|
||||
/* it deselects Bases, so we have to call the clear function always after */
|
||||
static void set_trans_object_base_flags(TransInfo *t)
|
||||
{
|
||||
Main *bmain = CTX_data_main(t->context);
|
||||
ViewLayer *view_layer = t->view_layer;
|
||||
View3D *v3d = t->view;
|
||||
Scene *scene = t->scene;
|
||||
Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true);
|
||||
/* NOTE: if Base selected and has parent selected:
|
||||
* base->flag_legacy = BA_WAS_SEL
|
||||
*/
|
||||
/* Don't do it if we're not actually going to recalculate anything. */
|
||||
if (t->mode == TFM_DUMMY) {
|
||||
return;
|
||||
}
|
||||
/* Makes sure base flags and object flags are identical. */
|
||||
BKE_scene_base_flag_to_objects(t->view_layer);
|
||||
/* Make sure depsgraph is here. */
|
||||
DEG_graph_relations_update(depsgraph, bmain, scene, view_layer);
|
||||
/* Clear all flags we need. It will be used to detect dependencies. */
|
||||
trans_object_base_deps_flag_prepare(view_layer);
|
||||
/* Traverse all bases and set all possible flags. */
|
||||
for (Base *base = view_layer->object_bases.first; base; base = base->next) {
|
||||
base->flag_legacy &= ~(BA_WAS_SEL | BA_TRANSFORM_LOCKED_IN_PLACE);
|
||||
if (BASE_SELECTED_EDITABLE(v3d, base)) {
|
||||
Object *ob = base->object;
|
||||
Object *parsel = ob->parent;
|
||||
/* If parent selected, deselect. */
|
||||
while (parsel != NULL) {
|
||||
if (parsel->base_flag & BASE_SELECTED) {
|
||||
Base *parbase = BKE_view_layer_base_find(view_layer, parsel);
|
||||
if (parbase != NULL) { /* in rare cases this can fail */
|
||||
if (BASE_SELECTED_EDITABLE(v3d, parbase)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
parsel = parsel->parent;
|
||||
}
|
||||
if (parsel != NULL) {
|
||||
/* Rotation around local centers are allowed to propagate. */
|
||||
if ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
|
||||
(t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL)) {
|
||||
base->flag_legacy |= BA_TRANSFORM_CHILD;
|
||||
}
|
||||
else {
|
||||
base->flag &= ~BASE_SELECTED;
|
||||
base->flag_legacy |= BA_WAS_SEL;
|
||||
}
|
||||
}
|
||||
flush_trans_object_base_deps_flag(depsgraph, ob);
|
||||
}
|
||||
}
|
||||
/* Store temporary bits in base indicating that base is being modified
|
||||
* (directly or indirectly) by transforming objects.
|
||||
*/
|
||||
trans_object_base_deps_flag_finish(t, view_layer);
|
||||
}
|
||||
|
||||
static bool mark_children(Object *ob)
|
||||
{
|
||||
if (ob->flag & (SELECT | BA_TRANSFORM_CHILD)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ob->parent) {
|
||||
if (mark_children(ob->parent)) {
|
||||
ob->flag |= BA_TRANSFORM_CHILD;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int count_proportional_objects(TransInfo *t)
|
||||
{
|
||||
int total = 0;
|
||||
ViewLayer *view_layer = t->view_layer;
|
||||
View3D *v3d = t->view;
|
||||
Scene *scene = t->scene;
|
||||
Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true);
|
||||
/* Clear all flags we need. It will be used to detect dependencies. */
|
||||
trans_object_base_deps_flag_prepare(view_layer);
|
||||
/* Rotations around local centers are allowed to propagate, so we take all objects. */
|
||||
if (!((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
|
||||
(t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL))) {
|
||||
/* Mark all parents. */
|
||||
for (Base *base = view_layer->object_bases.first; base; base = base->next) {
|
||||
if (BASE_SELECTED_EDITABLE(v3d, base) && BASE_SELECTABLE(v3d, base)) {
|
||||
Object *parent = base->object->parent;
|
||||
/* flag all parents */
|
||||
while (parent != NULL) {
|
||||
parent->flag |= BA_TRANSFORM_PARENT;
|
||||
parent = parent->parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Mark all children. */
|
||||
for (Base *base = view_layer->object_bases.first; base; base = base->next) {
|
||||
/* all base not already selected or marked that is editable */
|
||||
if ((base->object->flag & (BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT)) == 0 &&
|
||||
(base->flag & BASE_SELECTED) == 0 &&
|
||||
(BASE_EDITABLE(v3d, base) && BASE_SELECTABLE(v3d, base))) {
|
||||
mark_children(base->object);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Flush changed flags to all dependencies. */
|
||||
for (Base *base = view_layer->object_bases.first; base; base = base->next) {
|
||||
Object *ob = base->object;
|
||||
/* If base is not selected, not a parent of selection or not a child of
|
||||
* selection and it is editable and selectable.
|
||||
*/
|
||||
if ((ob->flag & (BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT)) == 0 &&
|
||||
(base->flag & BASE_SELECTED) == 0 &&
|
||||
(BASE_EDITABLE(v3d, base) && BASE_SELECTABLE(v3d, base))) {
|
||||
flush_trans_object_base_deps_flag(depsgraph, ob);
|
||||
total += 1;
|
||||
}
|
||||
}
|
||||
/* Store temporary bits in base indicating that base is being modified
|
||||
* (directly or indirectly) by transforming objects.
|
||||
*/
|
||||
trans_object_base_deps_flag_finish(t, view_layer);
|
||||
return total;
|
||||
}
|
||||
|
||||
void clear_trans_object_base_flags(TransInfo *t)
|
||||
{
|
||||
ViewLayer *view_layer = t->view_layer;
|
||||
Base *base;
|
||||
|
||||
for (base = view_layer->object_bases.first; base; base = base->next) {
|
||||
if (base->flag_legacy & BA_WAS_SEL) {
|
||||
ED_object_base_select(base, BA_SELECT);
|
||||
}
|
||||
|
||||
base->flag_legacy &= ~(BA_WAS_SEL | BA_SNAP_FIX_DEPS_FIASCO | BA_TEMP_TAG |
|
||||
BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT |
|
||||
BA_TRANSFORM_LOCKED_IN_PLACE);
|
||||
}
|
||||
}
|
||||
|
||||
void createTransObject(bContext *C, TransInfo *t)
|
||||
{
|
||||
TransData *td = NULL;
|
||||
TransDataExtension *tx;
|
||||
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
|
||||
|
||||
set_trans_object_base_flags(t);
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
/* count */
|
||||
tc->data_len = CTX_DATA_COUNT(C, selected_bases);
|
||||
|
||||
if (!tc->data_len) {
|
||||
/* clear here, main transform function escapes too */
|
||||
clear_trans_object_base_flags(t);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_prop_edit) {
|
||||
tc->data_len += count_proportional_objects(t);
|
||||
}
|
||||
|
||||
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransOb");
|
||||
tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), "TransObExtension");
|
||||
|
||||
TransDataObject *tdo = MEM_callocN(sizeof(*tdo), __func__);
|
||||
t->custom.type.data = tdo;
|
||||
t->custom.type.free_cb = freeTransObjectCustomData;
|
||||
|
||||
CTX_DATA_BEGIN (C, Base *, base, selected_bases) {
|
||||
Object *ob = base->object;
|
||||
|
||||
td->flag = TD_SELECTED;
|
||||
td->protectflag = ob->protectflag;
|
||||
td->ext = tx;
|
||||
td->ext->rotOrder = ob->rotmode;
|
||||
|
||||
if (base->flag & BA_TRANSFORM_CHILD) {
|
||||
td->flag |= TD_NOCENTER;
|
||||
td->flag |= TD_NO_LOC;
|
||||
}
|
||||
|
||||
/* select linked objects, but skip them later */
|
||||
if (ID_IS_LINKED(ob)) {
|
||||
td->flag |= TD_SKIP;
|
||||
}
|
||||
|
||||
if (t->options & CTX_OBMODE_XFORM_OBDATA) {
|
||||
ID *id = ob->data;
|
||||
if (!id || id->lib) {
|
||||
td->flag |= TD_SKIP;
|
||||
}
|
||||
else if (BKE_object_is_in_editmode(ob)) {
|
||||
/* The object could have edit-mode data from another view-layer,
|
||||
* it's such a corner-case it can be skipped for now - Campbell. */
|
||||
td->flag |= TD_SKIP;
|
||||
}
|
||||
}
|
||||
|
||||
if (t->options & CTX_OBMODE_XFORM_OBDATA) {
|
||||
if ((td->flag & TD_SKIP) == 0) {
|
||||
trans_obdata_in_obmode_ensure_object(tdo, ob);
|
||||
}
|
||||
}
|
||||
|
||||
ObjectToTransData(t, td, ob);
|
||||
td->val = NULL;
|
||||
td++;
|
||||
tx++;
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
if (is_prop_edit) {
|
||||
ViewLayer *view_layer = t->view_layer;
|
||||
View3D *v3d = t->view;
|
||||
Base *base;
|
||||
|
||||
for (base = view_layer->object_bases.first; base; base = base->next) {
|
||||
Object *ob = base->object;
|
||||
|
||||
/* if base is not selected, not a parent of selection
|
||||
* or not a child of selection and it is editable and selectable */
|
||||
if ((ob->flag & (BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT)) == 0 &&
|
||||
(base->flag & BASE_SELECTED) == 0 && BASE_EDITABLE(v3d, base) &&
|
||||
BASE_SELECTABLE(v3d, base)) {
|
||||
td->protectflag = ob->protectflag;
|
||||
td->ext = tx;
|
||||
td->ext->rotOrder = ob->rotmode;
|
||||
|
||||
ObjectToTransData(t, td, ob);
|
||||
td->val = NULL;
|
||||
td++;
|
||||
tx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (t->options & CTX_OBMODE_XFORM_OBDATA) {
|
||||
GSet *objects_in_transdata = BLI_gset_ptr_new_ex(__func__, tc->data_len);
|
||||
td = tc->data;
|
||||
for (int i = 0; i < tc->data_len; i++, td++) {
|
||||
if ((td->flag & TD_SKIP) == 0) {
|
||||
BLI_gset_add(objects_in_transdata, td->ob);
|
||||
}
|
||||
}
|
||||
|
||||
ViewLayer *view_layer = t->view_layer;
|
||||
View3D *v3d = t->view;
|
||||
|
||||
for (Base *base = view_layer->object_bases.first; base; base = base->next) {
|
||||
Object *ob = base->object;
|
||||
|
||||
/* if base is not selected, not a parent of selection
|
||||
* or not a child of selection and it is editable and selectable */
|
||||
if ((base->flag_legacy & BA_WAS_SEL) && (base->flag & BASE_SELECTED) == 0 &&
|
||||
BASE_EDITABLE(v3d, base) && BASE_SELECTABLE(v3d, base)) {
|
||||
|
||||
Object *ob_parent = ob->parent;
|
||||
if (ob_parent != NULL) {
|
||||
if (!BLI_gset_haskey(objects_in_transdata, ob)) {
|
||||
bool parent_in_transdata = false;
|
||||
while (ob_parent != NULL) {
|
||||
if (BLI_gset_haskey(objects_in_transdata, ob_parent)) {
|
||||
parent_in_transdata = true;
|
||||
break;
|
||||
}
|
||||
ob_parent = ob_parent->parent;
|
||||
}
|
||||
if (parent_in_transdata) {
|
||||
trans_obdata_in_obmode_ensure_object(tdo, ob);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BLI_gset_free(objects_in_transdata, NULL);
|
||||
}
|
||||
|
||||
if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) {
|
||||
|
||||
#define BASE_XFORM_INDIRECT(base) \
|
||||
((base->flag_legacy & BA_WAS_SEL) && (base->flag & BASE_SELECTED) == 0)
|
||||
|
||||
GSet *objects_in_transdata = BLI_gset_ptr_new_ex(__func__, tc->data_len);
|
||||
GHash *objects_parent_root = BLI_ghash_ptr_new_ex(__func__, tc->data_len);
|
||||
td = tc->data;
|
||||
for (int i = 0; i < tc->data_len; i++, td++) {
|
||||
if ((td->flag & TD_SKIP) == 0) {
|
||||
BLI_gset_add(objects_in_transdata, td->ob);
|
||||
}
|
||||
}
|
||||
|
||||
ViewLayer *view_layer = t->view_layer;
|
||||
|
||||
for (Base *base = view_layer->object_bases.first; base; base = base->next) {
|
||||
Object *ob = base->object;
|
||||
if (ob->parent != NULL) {
|
||||
if (ob->parent && !BLI_gset_haskey(objects_in_transdata, ob->parent) &&
|
||||
!BLI_gset_haskey(objects_in_transdata, ob)) {
|
||||
if (((base->flag_legacy & BA_WAS_SEL) && (base->flag & BASE_SELECTED) == 0)) {
|
||||
Base *base_parent = BKE_view_layer_base_find(view_layer, ob->parent);
|
||||
if (base_parent && !BASE_XFORM_INDIRECT(base_parent)) {
|
||||
Object *ob_parent_recurse = ob->parent;
|
||||
if (ob_parent_recurse != NULL) {
|
||||
while (ob_parent_recurse != NULL) {
|
||||
if (BLI_gset_haskey(objects_in_transdata, ob_parent_recurse)) {
|
||||
break;
|
||||
}
|
||||
ob_parent_recurse = ob_parent_recurse->parent;
|
||||
}
|
||||
|
||||
if (ob_parent_recurse) {
|
||||
trans_obchild_in_obmode_ensure_object(
|
||||
tdo, ob, ob_parent_recurse, OB_SKIP_CHILD_PARENT_APPLY_TRANSFORM);
|
||||
BLI_ghash_insert(objects_parent_root, ob, ob_parent_recurse);
|
||||
base->flag_legacy |= BA_TRANSFORM_LOCKED_IN_PLACE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Base *base = view_layer->object_bases.first; base; base = base->next) {
|
||||
Object *ob = base->object;
|
||||
|
||||
if (BASE_XFORM_INDIRECT(base) || BLI_gset_haskey(objects_in_transdata, ob)) {
|
||||
/* pass. */
|
||||
}
|
||||
else if (ob->parent != NULL) {
|
||||
Base *base_parent = BKE_view_layer_base_find(view_layer, ob->parent);
|
||||
if (base_parent) {
|
||||
if (BASE_XFORM_INDIRECT(base_parent) ||
|
||||
BLI_gset_haskey(objects_in_transdata, ob->parent)) {
|
||||
trans_obchild_in_obmode_ensure_object(tdo, ob, NULL, OB_SKIP_CHILD_PARENT_IS_XFORM);
|
||||
base->flag_legacy |= BA_TRANSFORM_LOCKED_IN_PLACE;
|
||||
}
|
||||
else {
|
||||
Object *ob_parent_recurse = BLI_ghash_lookup(objects_parent_root, ob->parent);
|
||||
if (ob_parent_recurse) {
|
||||
trans_obchild_in_obmode_ensure_object(
|
||||
tdo, ob, ob_parent_recurse, OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BLI_gset_free(objects_in_transdata, NULL);
|
||||
BLI_ghash_free(objects_parent_root, NULL, NULL);
|
||||
|
||||
#undef BASE_XFORM_INDIRECT
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Texture Space Transform Creation
|
||||
*
|
||||
* Instead of transforming the selection, move the 2D/3D cursor.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void createTransTexspace(TransInfo *t)
|
||||
{
|
||||
ViewLayer *view_layer = t->view_layer;
|
||||
TransData *td;
|
||||
Object *ob;
|
||||
ID *id;
|
||||
short *texflag;
|
||||
|
||||
ob = OBACT(view_layer);
|
||||
|
||||
if (ob == NULL) { // Shouldn't logically happen, but still...
|
||||
return;
|
||||
}
|
||||
|
||||
id = ob->data;
|
||||
if (id == NULL || !ELEM(GS(id->name), ID_ME, ID_CU, ID_MB)) {
|
||||
BKE_report(t->reports, RPT_ERROR, "Unsupported object type for text-space transform");
|
||||
return;
|
||||
}
|
||||
|
||||
if (BKE_object_obdata_is_libdata(ob)) {
|
||||
BKE_report(t->reports, RPT_ERROR, "Linked data can't text-space transform");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
BLI_assert(t->data_container_len == 1);
|
||||
TransDataContainer *tc = t->data_container;
|
||||
tc->data_len = 1;
|
||||
td = tc->data = MEM_callocN(sizeof(TransData), "TransTexspace");
|
||||
td->ext = tc->data_ext = MEM_callocN(sizeof(TransDataExtension), "TransTexspace");
|
||||
}
|
||||
|
||||
td->flag = TD_SELECTED;
|
||||
copy_v3_v3(td->center, ob->obmat[3]);
|
||||
td->ob = ob;
|
||||
|
||||
copy_m3_m4(td->mtx, ob->obmat);
|
||||
copy_m3_m4(td->axismtx, ob->obmat);
|
||||
normalize_m3(td->axismtx);
|
||||
pseudoinverse_m3_m3(td->smtx, td->mtx, PSEUDOINVERSE_EPSILON);
|
||||
|
||||
if (BKE_object_obdata_texspace_get(ob, &texflag, &td->loc, &td->ext->size, &td->ext->rot)) {
|
||||
ob->dtx |= OB_TEXSPACE;
|
||||
*texflag &= ~ME_AUTOSPACE;
|
||||
}
|
||||
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
copy_v3_v3(td->ext->irot, td->ext->rot);
|
||||
copy_v3_v3(td->ext->isize, td->ext->size);
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* 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_brush_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_paint.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
typedef struct TransDataPaintCurve {
|
||||
struct PaintCurvePoint *pcp; /* initial curve point */
|
||||
char id;
|
||||
} TransDataPaintCurve;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Paint Curve Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
#define PC_IS_ANY_SEL(pc) (((pc)->bez.f1 | (pc)->bez.f2 | (pc)->bez.f3) & SELECT)
|
||||
|
||||
static void PaintCurveConvertHandle(
|
||||
PaintCurvePoint *pcp, int id, TransData2D *td2d, TransDataPaintCurve *tdpc, TransData *td)
|
||||
{
|
||||
BezTriple *bezt = &pcp->bez;
|
||||
copy_v2_v2(td2d->loc, bezt->vec[id]);
|
||||
td2d->loc[2] = 0.0f;
|
||||
td2d->loc2d = bezt->vec[id];
|
||||
|
||||
td->flag = 0;
|
||||
td->loc = td2d->loc;
|
||||
copy_v3_v3(td->center, bezt->vec[1]);
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
|
||||
memset(td->axismtx, 0, sizeof(td->axismtx));
|
||||
td->axismtx[2][2] = 1.0f;
|
||||
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
td->flag |= TD_SELECTED;
|
||||
td->dist = 0.0;
|
||||
|
||||
unit_m3(td->mtx);
|
||||
unit_m3(td->smtx);
|
||||
|
||||
tdpc->id = id;
|
||||
tdpc->pcp = pcp;
|
||||
}
|
||||
|
||||
static void PaintCurvePointToTransData(PaintCurvePoint *pcp,
|
||||
TransData *td,
|
||||
TransData2D *td2d,
|
||||
TransDataPaintCurve *tdpc)
|
||||
{
|
||||
BezTriple *bezt = &pcp->bez;
|
||||
|
||||
if (pcp->bez.f2 == SELECT) {
|
||||
int i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
copy_v2_v2(td2d->loc, bezt->vec[i]);
|
||||
td2d->loc[2] = 0.0f;
|
||||
td2d->loc2d = bezt->vec[i];
|
||||
|
||||
td->flag = 0;
|
||||
td->loc = td2d->loc;
|
||||
copy_v3_v3(td->center, bezt->vec[1]);
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
|
||||
memset(td->axismtx, 0, sizeof(td->axismtx));
|
||||
td->axismtx[2][2] = 1.0f;
|
||||
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
td->flag |= TD_SELECTED;
|
||||
td->dist = 0.0;
|
||||
|
||||
unit_m3(td->mtx);
|
||||
unit_m3(td->smtx);
|
||||
|
||||
tdpc->id = i;
|
||||
tdpc->pcp = pcp;
|
||||
|
||||
td++;
|
||||
td2d++;
|
||||
tdpc++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (bezt->f3 & SELECT) {
|
||||
PaintCurveConvertHandle(pcp, 2, td2d, tdpc, td);
|
||||
td2d++;
|
||||
tdpc++;
|
||||
td++;
|
||||
}
|
||||
|
||||
if (bezt->f1 & SELECT) {
|
||||
PaintCurveConvertHandle(pcp, 0, td2d, tdpc, td);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void createTransPaintCurveVerts(bContext *C, TransInfo *t)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
PaintCurve *pc;
|
||||
PaintCurvePoint *pcp;
|
||||
Brush *br;
|
||||
TransData *td = NULL;
|
||||
TransData2D *td2d = NULL;
|
||||
TransDataPaintCurve *tdpc = NULL;
|
||||
int i;
|
||||
int total = 0;
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
tc->data_len = 0;
|
||||
|
||||
if (!paint || !paint->brush || !paint->brush->paint_curve) {
|
||||
return;
|
||||
}
|
||||
|
||||
br = paint->brush;
|
||||
pc = br->paint_curve;
|
||||
|
||||
for (pcp = pc->points, i = 0; i < pc->tot_points; i++, pcp++) {
|
||||
if (PC_IS_ANY_SEL(pcp)) {
|
||||
if (pcp->bez.f2 & SELECT) {
|
||||
total += 3;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
if (pcp->bez.f1 & SELECT) {
|
||||
total++;
|
||||
}
|
||||
if (pcp->bez.f3 & SELECT) {
|
||||
total++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!total) {
|
||||
return;
|
||||
}
|
||||
|
||||
tc->data_len = total;
|
||||
td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransData2D");
|
||||
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData");
|
||||
tc->custom.type.data = tdpc = MEM_callocN(tc->data_len * sizeof(TransDataPaintCurve),
|
||||
"TransDataPaintCurve");
|
||||
tc->custom.type.use_free = true;
|
||||
|
||||
for (pcp = pc->points, i = 0; i < pc->tot_points; i++, pcp++) {
|
||||
if (PC_IS_ANY_SEL(pcp)) {
|
||||
PaintCurvePointToTransData(pcp, td, td2d, tdpc);
|
||||
|
||||
if (pcp->bez.f2 & SELECT) {
|
||||
td += 3;
|
||||
td2d += 3;
|
||||
tdpc += 3;
|
||||
}
|
||||
else {
|
||||
if (pcp->bez.f1 & SELECT) {
|
||||
td++;
|
||||
td2d++;
|
||||
tdpc++;
|
||||
}
|
||||
if (pcp->bez.f3 & SELECT) {
|
||||
td++;
|
||||
td2d++;
|
||||
tdpc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Paint Curve Transform Flush
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void flushTransPaintCurve(TransInfo *t)
|
||||
{
|
||||
int i;
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
TransData2D *td2d = tc->data_2d;
|
||||
TransDataPaintCurve *tdpc = tc->custom.type.data;
|
||||
|
||||
for (i = 0; i < tc->data_len; i++, tdpc++, td2d++) {
|
||||
PaintCurvePoint *pcp = tdpc->pcp;
|
||||
copy_v2_v2(pcp->bez.vec[tdpc->id], td2d->loc);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* 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_particle_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_particle.h"
|
||||
#include "BKE_pointcache.h"
|
||||
|
||||
#include "ED_particle.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Particle Edit Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void createTransParticleVerts(bContext *C, TransInfo *t)
|
||||
{
|
||||
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
||||
|
||||
TransData *td = NULL;
|
||||
TransDataExtension *tx;
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
ParticleEditSettings *pset = PE_settings(t->scene);
|
||||
PTCacheEdit *edit = PE_get_current(t->scene, ob);
|
||||
ParticleSystem *psys = NULL;
|
||||
PTCacheEditPoint *point;
|
||||
PTCacheEditKey *key;
|
||||
float mat[4][4];
|
||||
int i, k, transformparticle;
|
||||
int count = 0, hasselected = 0;
|
||||
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
|
||||
|
||||
if (edit == NULL || t->settings->particle.selectmode == SCE_SELECT_PATH) {
|
||||
return;
|
||||
}
|
||||
|
||||
psys = edit->psys;
|
||||
|
||||
for (i = 0, point = edit->points; i < edit->totpoint; i++, point++) {
|
||||
point->flag &= ~PEP_TRANSFORM;
|
||||
transformparticle = 0;
|
||||
|
||||
if ((point->flag & PEP_HIDE) == 0) {
|
||||
for (k = 0, key = point->keys; k < point->totkey; k++, key++) {
|
||||
if ((key->flag & PEK_HIDE) == 0) {
|
||||
if (key->flag & PEK_SELECT) {
|
||||
hasselected = 1;
|
||||
transformparticle = 1;
|
||||
}
|
||||
else if (is_prop_edit) {
|
||||
transformparticle = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (transformparticle) {
|
||||
count += point->totkey;
|
||||
point->flag |= PEP_TRANSFORM;
|
||||
}
|
||||
}
|
||||
|
||||
/* note: in prop mode we need at least 1 selected */
|
||||
if (hasselected == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
tc->data_len = count;
|
||||
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Particle Mode)");
|
||||
|
||||
if (t->mode == TFM_BAKE_TIME) {
|
||||
tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension),
|
||||
"Particle_TransExtension");
|
||||
}
|
||||
else {
|
||||
tx = tc->data_ext = NULL;
|
||||
}
|
||||
|
||||
unit_m4(mat);
|
||||
|
||||
invert_m4_m4(ob->imat, ob->obmat);
|
||||
|
||||
for (i = 0, point = edit->points; i < edit->totpoint; i++, point++) {
|
||||
TransData *head, *tail;
|
||||
head = tail = td;
|
||||
|
||||
if (!(point->flag & PEP_TRANSFORM)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
|
||||
ParticleSystemModifierData *psmd_eval = edit->psmd_eval;
|
||||
psys_mat_hair_to_global(
|
||||
ob, psmd_eval->mesh_final, psys->part->from, psys->particles + i, mat);
|
||||
}
|
||||
|
||||
for (k = 0, key = point->keys; k < point->totkey; k++, key++) {
|
||||
if (key->flag & PEK_USE_WCO) {
|
||||
copy_v3_v3(key->world_co, key->co);
|
||||
mul_m4_v3(mat, key->world_co);
|
||||
td->loc = key->world_co;
|
||||
}
|
||||
else {
|
||||
td->loc = key->co;
|
||||
}
|
||||
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
copy_v3_v3(td->center, td->loc);
|
||||
|
||||
if (key->flag & PEK_SELECT) {
|
||||
td->flag |= TD_SELECTED;
|
||||
}
|
||||
else if (!is_prop_edit) {
|
||||
td->flag |= TD_SKIP;
|
||||
}
|
||||
|
||||
unit_m3(td->mtx);
|
||||
unit_m3(td->smtx);
|
||||
|
||||
/* don't allow moving roots */
|
||||
if (k == 0 && pset->flag & PE_LOCK_FIRST && (!psys || !(psys->flag & PSYS_GLOBAL_HAIR))) {
|
||||
td->protectflag |= OB_LOCK_LOC;
|
||||
}
|
||||
|
||||
td->ob = ob;
|
||||
td->ext = tx;
|
||||
if (t->mode == TFM_BAKE_TIME) {
|
||||
td->val = key->time;
|
||||
td->ival = *(key->time);
|
||||
/* abuse size and quat for min/max values */
|
||||
td->flag |= TD_NO_EXT;
|
||||
if (k == 0) {
|
||||
tx->size = NULL;
|
||||
}
|
||||
else {
|
||||
tx->size = (key - 1)->time;
|
||||
}
|
||||
|
||||
if (k == point->totkey - 1) {
|
||||
tx->quat = NULL;
|
||||
}
|
||||
else {
|
||||
tx->quat = (key + 1)->time;
|
||||
}
|
||||
}
|
||||
|
||||
td++;
|
||||
if (tx) {
|
||||
tx++;
|
||||
}
|
||||
tail++;
|
||||
}
|
||||
if (is_prop_edit && head != tail) {
|
||||
calc_distanceCurveVerts(head, tail - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Node Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void flushTransParticles(TransInfo *t)
|
||||
{
|
||||
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
||||
Scene *scene = t->scene;
|
||||
ViewLayer *view_layer = t->view_layer;
|
||||
Object *ob = OBACT(view_layer);
|
||||
PTCacheEdit *edit = PE_get_current(scene, ob);
|
||||
ParticleSystem *psys = edit->psys;
|
||||
PTCacheEditPoint *point;
|
||||
PTCacheEditKey *key;
|
||||
TransData *td;
|
||||
float mat[4][4], imat[4][4], co[3];
|
||||
int i, k;
|
||||
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
|
||||
|
||||
/* we do transform in world space, so flush world space position
|
||||
* back to particle local space (only for hair particles) */
|
||||
td = tc->data;
|
||||
for (i = 0, point = edit->points; i < edit->totpoint; i++, point++, td++) {
|
||||
if (!(point->flag & PEP_TRANSFORM)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
|
||||
ParticleSystemModifierData *psmd_eval = edit->psmd_eval;
|
||||
psys_mat_hair_to_global(
|
||||
ob, psmd_eval->mesh_final, psys->part->from, psys->particles + i, mat);
|
||||
invert_m4_m4(imat, mat);
|
||||
|
||||
for (k = 0, key = point->keys; k < point->totkey; k++, key++) {
|
||||
copy_v3_v3(co, key->world_co);
|
||||
mul_m4_v3(imat, co);
|
||||
|
||||
/* optimization for proportional edit */
|
||||
if (!is_prop_edit || !compare_v3v3(key->co, co, 0.0001f)) {
|
||||
copy_v3_v3(key->co, co);
|
||||
point->flag |= PEP_EDIT_RECALC;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
point->flag |= PEP_EDIT_RECALC;
|
||||
}
|
||||
}
|
||||
|
||||
PE_update_object(t->depsgraph, scene, OBACT(view_layer), 1);
|
||||
BKE_particle_batch_cache_dirty_tag(psys, BKE_PARTICLE_BATCH_DIRTY_ALL);
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_PSYS_REDO);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -0,0 +1,769 @@
|
|||
/*
|
||||
* 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_space_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_sequencer.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "UI_view2d.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Sequencer Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/* This function applies the rules for transforming a strip so duplicate
|
||||
* checks don't need to be added in multiple places.
|
||||
*
|
||||
* recursive, count and flag MUST be set.
|
||||
*
|
||||
* seq->depth must be set before running this function so we know if the strips
|
||||
* are root level or not
|
||||
*/
|
||||
static void SeqTransInfo(TransInfo *t, Sequence *seq, int *recursive, int *count, int *flag)
|
||||
{
|
||||
/* for extend we need to do some tricks */
|
||||
if (t->mode == TFM_TIME_EXTEND) {
|
||||
|
||||
/* *** Extend Transform *** */
|
||||
|
||||
Scene *scene = t->scene;
|
||||
int cfra = CFRA;
|
||||
int left = BKE_sequence_tx_get_final_left(seq, true);
|
||||
int right = BKE_sequence_tx_get_final_right(seq, true);
|
||||
|
||||
if (seq->depth == 0 && ((seq->flag & SELECT) == 0 || (seq->flag & SEQ_LOCK))) {
|
||||
*recursive = false;
|
||||
*count = 0;
|
||||
*flag = 0;
|
||||
}
|
||||
else if (seq->type == SEQ_TYPE_META) {
|
||||
|
||||
/* for meta's we only ever need to extend their children, no matter what depth
|
||||
* just check the meta's are in the bounds */
|
||||
if (t->frame_side == 'R' && right <= cfra) {
|
||||
*recursive = false;
|
||||
}
|
||||
else if (t->frame_side == 'L' && left >= cfra) {
|
||||
*recursive = false;
|
||||
}
|
||||
else {
|
||||
*recursive = true;
|
||||
}
|
||||
|
||||
*count = 1;
|
||||
*flag = (seq->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL);
|
||||
}
|
||||
else {
|
||||
|
||||
*recursive = false; /* not a meta, so no thinking here */
|
||||
*count = 1; /* unless its set to 0, extend will never set 2 handles at once */
|
||||
*flag = (seq->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL);
|
||||
|
||||
if (t->frame_side == 'R') {
|
||||
if (right <= cfra) {
|
||||
*count = *flag = 0;
|
||||
} /* ignore */
|
||||
else if (left > cfra) {
|
||||
} /* keep the selection */
|
||||
else {
|
||||
*flag |= SEQ_RIGHTSEL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (left >= cfra) {
|
||||
*count = *flag = 0;
|
||||
} /* ignore */
|
||||
else if (right < cfra) {
|
||||
} /* keep the selection */
|
||||
else {
|
||||
*flag |= SEQ_LEFTSEL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
t->frame_side = 'B';
|
||||
|
||||
/* *** Normal Transform *** */
|
||||
|
||||
if (seq->depth == 0) {
|
||||
|
||||
/* Count */
|
||||
|
||||
/* Non nested strips (resect selection and handles) */
|
||||
if ((seq->flag & SELECT) == 0 || (seq->flag & SEQ_LOCK)) {
|
||||
*recursive = false;
|
||||
*count = 0;
|
||||
*flag = 0;
|
||||
}
|
||||
else {
|
||||
if ((seq->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) == (SEQ_LEFTSEL | SEQ_RIGHTSEL)) {
|
||||
*flag = seq->flag;
|
||||
*count = 2; /* we need 2 transdata's */
|
||||
}
|
||||
else {
|
||||
*flag = seq->flag;
|
||||
*count = 1; /* selected or with a handle selected */
|
||||
}
|
||||
|
||||
/* Recursive */
|
||||
|
||||
if ((seq->type == SEQ_TYPE_META) && ((seq->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) == 0)) {
|
||||
/* if any handles are selected, don't recurse */
|
||||
*recursive = true;
|
||||
}
|
||||
else {
|
||||
*recursive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Nested, different rules apply */
|
||||
|
||||
#ifdef SEQ_TX_NESTED_METAS
|
||||
*flag = (seq->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL);
|
||||
*count = 1; /* ignore the selection for nested */
|
||||
*recursive = (seq->type == SEQ_TYPE_META);
|
||||
#else
|
||||
if (seq->type == SEQ_TYPE_META) {
|
||||
/* Meta's can only directly be moved between channels since they
|
||||
* don't have their start and length set directly (children affect that)
|
||||
* since this Meta is nested we don't need any of its data in fact.
|
||||
* BKE_sequence_calc() will update its settings when run on the toplevel meta */
|
||||
*flag = 0;
|
||||
*count = 0;
|
||||
*recursive = true;
|
||||
}
|
||||
else {
|
||||
*flag = (seq->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL);
|
||||
*count = 1; /* ignore the selection for nested */
|
||||
*recursive = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int SeqTransCount(TransInfo *t, Sequence *parent, ListBase *seqbase, int depth)
|
||||
{
|
||||
Sequence *seq;
|
||||
int tot = 0, recursive, count, flag;
|
||||
|
||||
for (seq = seqbase->first; seq; seq = seq->next) {
|
||||
seq->depth = depth;
|
||||
|
||||
/* 'seq->tmp' is used by seq_tx_get_final_{left, right}
|
||||
* to check sequence's range and clamp to it if needed.
|
||||
* It's first place where digging into sequences tree, so store link to parent here. */
|
||||
seq->tmp = parent;
|
||||
|
||||
SeqTransInfo(t, seq, &recursive, &count, &flag); /* ignore the flag */
|
||||
tot += count;
|
||||
|
||||
if (recursive) {
|
||||
tot += SeqTransCount(t, seq, &seq->seqbase, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
static TransData *SeqToTransData(
|
||||
TransData *td, TransData2D *td2d, TransDataSeq *tdsq, Sequence *seq, int flag, int sel_flag)
|
||||
{
|
||||
int start_left;
|
||||
|
||||
switch (sel_flag) {
|
||||
case SELECT:
|
||||
/* Use seq_tx_get_final_left() and an offset here
|
||||
* so transform has the left hand location of the strip.
|
||||
* tdsq->start_offset is used when flushing the tx data back */
|
||||
start_left = BKE_sequence_tx_get_final_left(seq, false);
|
||||
td2d->loc[0] = start_left;
|
||||
tdsq->start_offset = start_left - seq->start; /* use to apply the original location */
|
||||
break;
|
||||
case SEQ_LEFTSEL:
|
||||
start_left = BKE_sequence_tx_get_final_left(seq, false);
|
||||
td2d->loc[0] = start_left;
|
||||
break;
|
||||
case SEQ_RIGHTSEL:
|
||||
td2d->loc[0] = BKE_sequence_tx_get_final_right(seq, false);
|
||||
break;
|
||||
}
|
||||
|
||||
td2d->loc[1] = seq->machine; /* channel - Y location */
|
||||
td2d->loc[2] = 0.0f;
|
||||
td2d->loc2d = NULL;
|
||||
|
||||
tdsq->seq = seq;
|
||||
|
||||
/* Use instead of seq->flag for nested strips and other
|
||||
* cases where the selection may need to be modified */
|
||||
tdsq->flag = flag;
|
||||
tdsq->sel_flag = sel_flag;
|
||||
|
||||
td->extra = (void *)tdsq; /* allow us to update the strip from here */
|
||||
|
||||
td->flag = 0;
|
||||
td->loc = td2d->loc;
|
||||
copy_v3_v3(td->center, td->loc);
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
|
||||
memset(td->axismtx, 0, sizeof(td->axismtx));
|
||||
td->axismtx[2][2] = 1.0f;
|
||||
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
|
||||
td->flag |= TD_SELECTED;
|
||||
td->dist = 0.0;
|
||||
|
||||
unit_m3(td->mtx);
|
||||
unit_m3(td->smtx);
|
||||
|
||||
/* Time Transform (extend) */
|
||||
td->val = td2d->loc;
|
||||
td->ival = td2d->loc[0];
|
||||
|
||||
return td;
|
||||
}
|
||||
|
||||
static int SeqToTransData_Recursive(
|
||||
TransInfo *t, ListBase *seqbase, TransData *td, TransData2D *td2d, TransDataSeq *tdsq)
|
||||
{
|
||||
Sequence *seq;
|
||||
int recursive, count, flag;
|
||||
int tot = 0;
|
||||
|
||||
for (seq = seqbase->first; seq; seq = seq->next) {
|
||||
|
||||
SeqTransInfo(t, seq, &recursive, &count, &flag);
|
||||
|
||||
/* add children first so recalculating metastrips does nested strips first */
|
||||
if (recursive) {
|
||||
int tot_children = SeqToTransData_Recursive(t, &seq->seqbase, td, td2d, tdsq);
|
||||
|
||||
td = td + tot_children;
|
||||
td2d = td2d + tot_children;
|
||||
tdsq = tdsq + tot_children;
|
||||
|
||||
tot += tot_children;
|
||||
}
|
||||
|
||||
/* use 'flag' which is derived from seq->flag but modified for special cases */
|
||||
if (flag & SELECT) {
|
||||
if (flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) {
|
||||
if (flag & SEQ_LEFTSEL) {
|
||||
SeqToTransData(td++, td2d++, tdsq++, seq, flag, SEQ_LEFTSEL);
|
||||
tot++;
|
||||
}
|
||||
if (flag & SEQ_RIGHTSEL) {
|
||||
SeqToTransData(td++, td2d++, tdsq++, seq, flag, SEQ_RIGHTSEL);
|
||||
tot++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
SeqToTransData(td++, td2d++, tdsq++, seq, flag, SELECT);
|
||||
tot++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return tot;
|
||||
}
|
||||
|
||||
static void SeqTransDataBounds(TransInfo *t, ListBase *seqbase, TransSeq *ts)
|
||||
{
|
||||
Sequence *seq;
|
||||
int recursive, count, flag;
|
||||
int max = INT32_MIN, min = INT32_MAX;
|
||||
|
||||
for (seq = seqbase->first; seq; seq = seq->next) {
|
||||
|
||||
/* just to get the flag since there are corner cases where this isn't totally obvious */
|
||||
SeqTransInfo(t, seq, &recursive, &count, &flag);
|
||||
|
||||
/* use 'flag' which is derived from seq->flag but modified for special cases */
|
||||
if (flag & SELECT) {
|
||||
if (flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) {
|
||||
if (flag & SEQ_LEFTSEL) {
|
||||
min = min_ii(seq->startdisp, min);
|
||||
max = max_ii(seq->startdisp, max);
|
||||
}
|
||||
if (flag & SEQ_RIGHTSEL) {
|
||||
min = min_ii(seq->enddisp, min);
|
||||
max = max_ii(seq->enddisp, max);
|
||||
}
|
||||
}
|
||||
else {
|
||||
min = min_ii(seq->startdisp, min);
|
||||
max = max_ii(seq->enddisp, max);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ts) {
|
||||
ts->max = max;
|
||||
ts->min = min;
|
||||
}
|
||||
}
|
||||
|
||||
static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data)
|
||||
{
|
||||
Editing *ed = BKE_sequencer_editing_get(t->scene, false);
|
||||
|
||||
if (ed != NULL) {
|
||||
|
||||
ListBase *seqbasep = ed->seqbasep;
|
||||
TransData *td = tc->data;
|
||||
int a;
|
||||
|
||||
/* prevent updating the same seq twice
|
||||
* if the transdata order is changed this will mess up
|
||||
* but so will TransDataSeq */
|
||||
Sequence *seq_prev = NULL;
|
||||
Sequence *seq;
|
||||
|
||||
if (!(t->state == TRANS_CANCEL)) {
|
||||
|
||||
#if 0 // default 2.4 behavior
|
||||
|
||||
/* flush to 2d vector from internally used 3d vector */
|
||||
for (a = 0; a < t->total; a++, td++) {
|
||||
if ((seq != seq_prev) && (seq->depth == 0) && (seq->flag & SEQ_OVERLAP)) {
|
||||
seq = ((TransDataSeq *)td->extra)->seq;
|
||||
BKE_sequence_base_shuffle(seqbasep, seq, t->scene);
|
||||
}
|
||||
|
||||
seq_prev = seq;
|
||||
}
|
||||
|
||||
#else // durian hack
|
||||
{
|
||||
int overlap = 0;
|
||||
|
||||
for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) {
|
||||
seq = ((TransDataSeq *)td->extra)->seq;
|
||||
if ((seq != seq_prev) && (seq->depth == 0) && (seq->flag & SEQ_OVERLAP)) {
|
||||
overlap = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (overlap) {
|
||||
bool has_effect_root = false, has_effect_any = false;
|
||||
for (seq = seqbasep->first; seq; seq = seq->next) {
|
||||
seq->tmp = NULL;
|
||||
}
|
||||
|
||||
td = tc->data;
|
||||
for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) {
|
||||
seq = ((TransDataSeq *)td->extra)->seq;
|
||||
if ((seq != seq_prev)) {
|
||||
/* check effects strips, we cant change their time */
|
||||
if ((seq->type & SEQ_TYPE_EFFECT) && seq->seq1) {
|
||||
has_effect_any = true;
|
||||
if (seq->depth == 0) {
|
||||
has_effect_root = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Tag seq with a non zero value, used by
|
||||
* BKE_sequence_base_shuffle_time to identify the ones to shuffle */
|
||||
if (seq->depth == 0) {
|
||||
seq->tmp = (void *)1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (t->flag & T_ALT_TRANSFORM) {
|
||||
int minframe = MAXFRAME;
|
||||
td = tc->data;
|
||||
for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) {
|
||||
seq = ((TransDataSeq *)td->extra)->seq;
|
||||
if ((seq != seq_prev) && (seq->depth == 0)) {
|
||||
minframe = min_ii(minframe, seq->startdisp);
|
||||
}
|
||||
}
|
||||
|
||||
for (seq = seqbasep->first; seq; seq = seq->next) {
|
||||
if (!(seq->flag & SELECT)) {
|
||||
if (seq->startdisp >= minframe) {
|
||||
seq->machine += MAXSEQ * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BKE_sequence_base_shuffle_time(seqbasep, t->scene);
|
||||
|
||||
for (seq = seqbasep->first; seq; seq = seq->next) {
|
||||
if (seq->machine >= MAXSEQ * 2) {
|
||||
seq->machine -= MAXSEQ * 2;
|
||||
seq->tmp = (void *)1;
|
||||
}
|
||||
else {
|
||||
seq->tmp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BKE_sequence_base_shuffle_time(seqbasep, t->scene);
|
||||
}
|
||||
else {
|
||||
BKE_sequence_base_shuffle_time(seqbasep, t->scene);
|
||||
}
|
||||
|
||||
if (has_effect_any) {
|
||||
/* update effects strips based on strips just moved in time */
|
||||
td = tc->data;
|
||||
for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) {
|
||||
seq = ((TransDataSeq *)td->extra)->seq;
|
||||
if ((seq != seq_prev)) {
|
||||
if ((seq->type & SEQ_TYPE_EFFECT) && seq->seq1) {
|
||||
BKE_sequence_calc(t->scene, seq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (has_effect_root) {
|
||||
/* now if any effects _still_ overlap, we need to move them up */
|
||||
td = tc->data;
|
||||
for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) {
|
||||
seq = ((TransDataSeq *)td->extra)->seq;
|
||||
if ((seq != seq_prev) && (seq->depth == 0)) {
|
||||
if ((seq->type & SEQ_TYPE_EFFECT) && seq->seq1) {
|
||||
if (BKE_sequence_test_overlap(seqbasep, seq)) {
|
||||
BKE_sequence_base_shuffle(seqbasep, seq, t->scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* done with effects */
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (seq = seqbasep->first; seq; seq = seq->next) {
|
||||
/* We might want to build a list of effects that need to be updated during transform */
|
||||
if (seq->type & SEQ_TYPE_EFFECT) {
|
||||
if (seq->seq1 && seq->seq1->flag & SELECT) {
|
||||
BKE_sequence_calc(t->scene, seq);
|
||||
}
|
||||
else if (seq->seq2 && seq->seq2->flag & SELECT) {
|
||||
BKE_sequence_calc(t->scene, seq);
|
||||
}
|
||||
else if (seq->seq3 && seq->seq3->flag & SELECT) {
|
||||
BKE_sequence_calc(t->scene, seq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BKE_sequencer_sort(t->scene);
|
||||
}
|
||||
else {
|
||||
/* Canceled, need to update the strips display */
|
||||
for (a = 0; a < tc->data_len; a++, td++) {
|
||||
seq = ((TransDataSeq *)td->extra)->seq;
|
||||
if ((seq != seq_prev) && (seq->depth == 0)) {
|
||||
if (seq->flag & SEQ_OVERLAP) {
|
||||
BKE_sequence_base_shuffle(seqbasep, seq, t->scene);
|
||||
}
|
||||
|
||||
BKE_sequence_calc_disp(t->scene, seq);
|
||||
}
|
||||
seq_prev = seq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((custom_data->data != NULL) && custom_data->use_free) {
|
||||
TransSeq *ts = custom_data->data;
|
||||
MEM_freeN(ts->tdseq);
|
||||
MEM_freeN(custom_data->data);
|
||||
custom_data->data = NULL;
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&t->scene->id, ID_RECALC_SEQUENCER_STRIPS);
|
||||
}
|
||||
|
||||
void createTransSeqData(bContext *C, TransInfo *t)
|
||||
{
|
||||
#define XXX_DURIAN_ANIM_TX_HACK
|
||||
|
||||
View2D *v2d = UI_view2d_fromcontext(C);
|
||||
Scene *scene = t->scene;
|
||||
Editing *ed = BKE_sequencer_editing_get(t->scene, false);
|
||||
TransData *td = NULL;
|
||||
TransData2D *td2d = NULL;
|
||||
TransDataSeq *tdsq = NULL;
|
||||
TransSeq *ts = NULL;
|
||||
int xmouse;
|
||||
|
||||
int count = 0;
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
if (ed == NULL) {
|
||||
tc->data_len = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
tc->custom.type.free_cb = freeSeqData;
|
||||
|
||||
xmouse = (int)UI_view2d_region_to_view_x(v2d, t->mouse.imval[0]);
|
||||
|
||||
/* which side of the current frame should be allowed */
|
||||
if (t->mode == TFM_TIME_EXTEND) {
|
||||
/* only side on which mouse is gets transformed */
|
||||
t->frame_side = (xmouse > CFRA) ? 'R' : 'L';
|
||||
}
|
||||
else {
|
||||
/* normal transform - both sides of current frame are considered */
|
||||
t->frame_side = 'B';
|
||||
}
|
||||
|
||||
#ifdef XXX_DURIAN_ANIM_TX_HACK
|
||||
{
|
||||
Sequence *seq;
|
||||
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
|
||||
/* hack */
|
||||
if ((seq->flag & SELECT) == 0 && seq->type & SEQ_TYPE_EFFECT) {
|
||||
Sequence *seq_user;
|
||||
int i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
seq_user = *((&seq->seq1) + i);
|
||||
if (seq_user && (seq_user->flag & SELECT) && !(seq_user->flag & SEQ_LOCK) &&
|
||||
!(seq_user->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL))) {
|
||||
seq->flag |= SELECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
count = SeqTransCount(t, NULL, ed->seqbasep, 0);
|
||||
|
||||
/* allocate memory for data */
|
||||
tc->data_len = count;
|
||||
|
||||
/* stop if trying to build list if nothing selected */
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
tc->custom.type.data = ts = MEM_callocN(sizeof(TransSeq), "transseq");
|
||||
tc->custom.type.use_free = true;
|
||||
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransSeq TransData");
|
||||
td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransSeq TransData2D");
|
||||
ts->tdseq = tdsq = MEM_callocN(tc->data_len * sizeof(TransDataSeq), "TransSeq TransDataSeq");
|
||||
|
||||
/* loop 2: build transdata array */
|
||||
SeqToTransData_Recursive(t, ed->seqbasep, td, td2d, tdsq);
|
||||
SeqTransDataBounds(t, ed->seqbasep, ts);
|
||||
|
||||
/* set the snap mode based on how close the mouse is at the end/start points */
|
||||
if (abs(xmouse - ts->max) > abs(xmouse - ts->min)) {
|
||||
ts->snap_left = true;
|
||||
}
|
||||
|
||||
#undef XXX_DURIAN_ANIM_TX_HACK
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UVs Transform Flush
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/* commented _only_ because the meta may have animation data which
|
||||
* needs moving too [#28158] */
|
||||
|
||||
#define SEQ_TX_NESTED_METAS
|
||||
|
||||
BLI_INLINE void trans_update_seq(Scene *sce, Sequence *seq, int old_start, int sel_flag)
|
||||
{
|
||||
if (seq->depth == 0) {
|
||||
/* Calculate this strip and all nested strips.
|
||||
* Children are ALWAYS transformed first so we don't need to do this in another loop.
|
||||
*/
|
||||
BKE_sequence_calc(sce, seq);
|
||||
}
|
||||
else {
|
||||
BKE_sequence_calc_disp(sce, seq);
|
||||
}
|
||||
|
||||
if (sel_flag == SELECT) {
|
||||
BKE_sequencer_offset_animdata(sce, seq, seq->start - old_start);
|
||||
}
|
||||
}
|
||||
|
||||
void flushTransSeq(TransInfo *t)
|
||||
{
|
||||
/* Editing null check already done */
|
||||
ListBase *seqbasep = BKE_sequencer_editing_get(t->scene, false)->seqbasep;
|
||||
|
||||
int a, new_frame;
|
||||
TransData *td = NULL;
|
||||
TransData2D *td2d = NULL;
|
||||
TransDataSeq *tdsq = NULL;
|
||||
Sequence *seq;
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
/* prevent updating the same seq twice
|
||||
* if the transdata order is changed this will mess up
|
||||
* but so will TransDataSeq */
|
||||
Sequence *seq_prev = NULL;
|
||||
int old_start_prev = 0, sel_flag_prev = 0;
|
||||
|
||||
/* flush to 2d vector from internally used 3d vector */
|
||||
for (a = 0, td = tc->data, td2d = tc->data_2d; a < tc->data_len; a++, td++, td2d++) {
|
||||
int old_start;
|
||||
tdsq = (TransDataSeq *)td->extra;
|
||||
seq = tdsq->seq;
|
||||
old_start = seq->start;
|
||||
new_frame = round_fl_to_int(td2d->loc[0]);
|
||||
|
||||
switch (tdsq->sel_flag) {
|
||||
case SELECT:
|
||||
#ifdef SEQ_TX_NESTED_METAS
|
||||
if ((seq->depth != 0 || BKE_sequence_tx_test(seq))) {
|
||||
/* for meta's, their children move */
|
||||
seq->start = new_frame - tdsq->start_offset;
|
||||
}
|
||||
#else
|
||||
if (seq->type != SEQ_TYPE_META && (seq->depth != 0 || seq_tx_test(seq))) {
|
||||
/* for meta's, their children move */
|
||||
seq->start = new_frame - tdsq->start_offset;
|
||||
}
|
||||
#endif
|
||||
if (seq->depth == 0) {
|
||||
seq->machine = round_fl_to_int(td2d->loc[1]);
|
||||
CLAMP(seq->machine, 1, MAXSEQ);
|
||||
}
|
||||
break;
|
||||
case SEQ_LEFTSEL: /* no vertical transform */
|
||||
BKE_sequence_tx_set_final_left(seq, new_frame);
|
||||
BKE_sequence_tx_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL);
|
||||
|
||||
/* todo - move this into aftertrans update? - old seq tx needed it anyway */
|
||||
BKE_sequence_single_fix(seq);
|
||||
break;
|
||||
case SEQ_RIGHTSEL: /* no vertical transform */
|
||||
BKE_sequence_tx_set_final_right(seq, new_frame);
|
||||
BKE_sequence_tx_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL);
|
||||
|
||||
/* todo - move this into aftertrans update? - old seq tx needed it anyway */
|
||||
BKE_sequence_single_fix(seq);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update *previous* seq! Else, we would update a seq after its first transform,
|
||||
* and if it has more than one (like e.g. SEQ_LEFTSEL and SEQ_RIGHTSEL),
|
||||
* the others are not updated! See T38469.
|
||||
*/
|
||||
if (seq != seq_prev) {
|
||||
if (seq_prev) {
|
||||
trans_update_seq(t->scene, seq_prev, old_start_prev, sel_flag_prev);
|
||||
}
|
||||
|
||||
seq_prev = seq;
|
||||
old_start_prev = old_start;
|
||||
sel_flag_prev = tdsq->sel_flag;
|
||||
}
|
||||
else {
|
||||
/* We want to accumulate *all* sel_flags for this seq! */
|
||||
sel_flag_prev |= tdsq->sel_flag;
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't forget to update the last seq! */
|
||||
if (seq_prev) {
|
||||
trans_update_seq(t->scene, seq_prev, old_start_prev, sel_flag_prev);
|
||||
}
|
||||
|
||||
/* originally TFM_TIME_EXTEND, transform changes */
|
||||
if (ELEM(t->mode, TFM_SEQ_SLIDE, TFM_TIME_TRANSLATE)) {
|
||||
/* Special annoying case here, need to calc metas with TFM_TIME_EXTEND only */
|
||||
|
||||
/* calc all meta's then effects [#27953] */
|
||||
for (seq = seqbasep->first; seq; seq = seq->next) {
|
||||
if (seq->type == SEQ_TYPE_META && seq->flag & SELECT) {
|
||||
BKE_sequence_calc(t->scene, seq);
|
||||
}
|
||||
}
|
||||
for (seq = seqbasep->first; seq; seq = seq->next) {
|
||||
if (seq->seq1 || seq->seq2 || seq->seq3) {
|
||||
BKE_sequence_calc(t->scene, seq);
|
||||
}
|
||||
}
|
||||
|
||||
/* update effects inside meta's */
|
||||
for (a = 0, seq_prev = NULL, td = tc->data, td2d = tc->data_2d; a < tc->data_len;
|
||||
a++, td++, td2d++, seq_prev = seq) {
|
||||
tdsq = (TransDataSeq *)td->extra;
|
||||
seq = tdsq->seq;
|
||||
if ((seq != seq_prev) && (seq->depth != 0)) {
|
||||
if (seq->seq1 || seq->seq2 || seq->seq3) {
|
||||
BKE_sequence_calc(t->scene, seq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* need to do the overlap check in a new loop otherwise adjacent strips
|
||||
* will not be updated and we'll get false positives */
|
||||
seq_prev = NULL;
|
||||
for (a = 0, td = tc->data, td2d = tc->data_2d; a < tc->data_len; a++, td++, td2d++) {
|
||||
|
||||
tdsq = (TransDataSeq *)td->extra;
|
||||
seq = tdsq->seq;
|
||||
|
||||
if (seq != seq_prev) {
|
||||
if (seq->depth == 0) {
|
||||
/* test overlap, displays red outline */
|
||||
seq->flag &= ~SEQ_OVERLAP;
|
||||
if (BKE_sequence_test_overlap(seqbasep, seq)) {
|
||||
seq->flag |= SEQ_OVERLAP;
|
||||
}
|
||||
}
|
||||
}
|
||||
seq_prev = seq;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -0,0 +1,692 @@
|
|||
/*
|
||||
* 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_space_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_movieclip.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_tracking.h"
|
||||
|
||||
#include "ED_clip.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
typedef struct TransDataTracking {
|
||||
int mode, flag;
|
||||
|
||||
/* tracks transformation from main window */
|
||||
int area;
|
||||
const float *relative, *loc;
|
||||
float soffset[2], srelative[2];
|
||||
float offset[2];
|
||||
|
||||
float (*smarkers)[2];
|
||||
int markersnr;
|
||||
MovieTrackingMarker *markers;
|
||||
|
||||
/* marker transformation from curves editor */
|
||||
float *prev_pos, scale;
|
||||
short coord;
|
||||
|
||||
MovieTrackingTrack *track;
|
||||
MovieTrackingPlaneTrack *plane_track;
|
||||
} TransDataTracking;
|
||||
|
||||
enum transDataTracking_Mode {
|
||||
transDataTracking_ModeTracks = 0,
|
||||
transDataTracking_ModeCurves = 1,
|
||||
transDataTracking_ModePlaneTracks = 2,
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Clip Editor Motion Tracking Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
static void markerToTransDataInit(TransData *td,
|
||||
TransData2D *td2d,
|
||||
TransDataTracking *tdt,
|
||||
MovieTrackingTrack *track,
|
||||
MovieTrackingMarker *marker,
|
||||
int area,
|
||||
float loc[2],
|
||||
float rel[2],
|
||||
const float off[2],
|
||||
const float aspect[2])
|
||||
{
|
||||
int anchor = area == TRACK_AREA_POINT && off;
|
||||
|
||||
tdt->mode = transDataTracking_ModeTracks;
|
||||
|
||||
if (anchor) {
|
||||
td2d->loc[0] = rel[0] * aspect[0]; /* hold original location */
|
||||
td2d->loc[1] = rel[1] * aspect[1];
|
||||
|
||||
tdt->loc = loc;
|
||||
td2d->loc2d = loc; /* current location */
|
||||
}
|
||||
else {
|
||||
td2d->loc[0] = loc[0] * aspect[0]; /* hold original location */
|
||||
td2d->loc[1] = loc[1] * aspect[1];
|
||||
|
||||
td2d->loc2d = loc; /* current location */
|
||||
}
|
||||
td2d->loc[2] = 0.0f;
|
||||
|
||||
tdt->relative = rel;
|
||||
tdt->area = area;
|
||||
|
||||
tdt->markersnr = track->markersnr;
|
||||
tdt->markers = track->markers;
|
||||
tdt->track = track;
|
||||
|
||||
if (rel) {
|
||||
if (!anchor) {
|
||||
td2d->loc[0] += rel[0] * aspect[0];
|
||||
td2d->loc[1] += rel[1] * aspect[1];
|
||||
}
|
||||
|
||||
copy_v2_v2(tdt->srelative, rel);
|
||||
}
|
||||
|
||||
if (off) {
|
||||
copy_v2_v2(tdt->soffset, off);
|
||||
}
|
||||
|
||||
td->flag = 0;
|
||||
td->loc = td2d->loc;
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
|
||||
// copy_v3_v3(td->center, td->loc);
|
||||
td->flag |= TD_INDIVIDUAL_SCALE;
|
||||
td->center[0] = marker->pos[0] * aspect[0];
|
||||
td->center[1] = marker->pos[1] * aspect[1];
|
||||
|
||||
memset(td->axismtx, 0, sizeof(td->axismtx));
|
||||
td->axismtx[2][2] = 1.0f;
|
||||
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
|
||||
td->flag |= TD_SELECTED;
|
||||
td->dist = 0.0;
|
||||
|
||||
unit_m3(td->mtx);
|
||||
unit_m3(td->smtx);
|
||||
}
|
||||
|
||||
static void trackToTransData(const int framenr,
|
||||
TransData *td,
|
||||
TransData2D *td2d,
|
||||
TransDataTracking *tdt,
|
||||
MovieTrackingTrack *track,
|
||||
const float aspect[2])
|
||||
{
|
||||
MovieTrackingMarker *marker = BKE_tracking_marker_ensure(track, framenr);
|
||||
|
||||
tdt->flag = marker->flag;
|
||||
marker->flag &= ~(MARKER_DISABLED | MARKER_TRACKED);
|
||||
|
||||
markerToTransDataInit(td++,
|
||||
td2d++,
|
||||
tdt++,
|
||||
track,
|
||||
marker,
|
||||
TRACK_AREA_POINT,
|
||||
track->offset,
|
||||
marker->pos,
|
||||
track->offset,
|
||||
aspect);
|
||||
|
||||
if (track->flag & SELECT) {
|
||||
markerToTransDataInit(
|
||||
td++, td2d++, tdt++, track, marker, TRACK_AREA_POINT, marker->pos, NULL, NULL, aspect);
|
||||
}
|
||||
|
||||
if (track->pat_flag & SELECT) {
|
||||
int a;
|
||||
|
||||
for (a = 0; a < 4; a++) {
|
||||
markerToTransDataInit(td++,
|
||||
td2d++,
|
||||
tdt++,
|
||||
track,
|
||||
marker,
|
||||
TRACK_AREA_PAT,
|
||||
marker->pattern_corners[a],
|
||||
marker->pos,
|
||||
NULL,
|
||||
aspect);
|
||||
}
|
||||
}
|
||||
|
||||
if (track->search_flag & SELECT) {
|
||||
markerToTransDataInit(td++,
|
||||
td2d++,
|
||||
tdt++,
|
||||
track,
|
||||
marker,
|
||||
TRACK_AREA_SEARCH,
|
||||
marker->search_min,
|
||||
marker->pos,
|
||||
NULL,
|
||||
aspect);
|
||||
|
||||
markerToTransDataInit(td++,
|
||||
td2d++,
|
||||
tdt++,
|
||||
track,
|
||||
marker,
|
||||
TRACK_AREA_SEARCH,
|
||||
marker->search_max,
|
||||
marker->pos,
|
||||
NULL,
|
||||
aspect);
|
||||
}
|
||||
}
|
||||
|
||||
static void planeMarkerToTransDataInit(TransData *td,
|
||||
TransData2D *td2d,
|
||||
TransDataTracking *tdt,
|
||||
MovieTrackingPlaneTrack *plane_track,
|
||||
float corner[2],
|
||||
const float aspect[2])
|
||||
{
|
||||
tdt->mode = transDataTracking_ModePlaneTracks;
|
||||
tdt->plane_track = plane_track;
|
||||
|
||||
td2d->loc[0] = corner[0] * aspect[0]; /* hold original location */
|
||||
td2d->loc[1] = corner[1] * aspect[1];
|
||||
|
||||
td2d->loc2d = corner; /* current location */
|
||||
td2d->loc[2] = 0.0f;
|
||||
|
||||
td->flag = 0;
|
||||
td->loc = td2d->loc;
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
copy_v3_v3(td->center, td->loc);
|
||||
|
||||
memset(td->axismtx, 0, sizeof(td->axismtx));
|
||||
td->axismtx[2][2] = 1.0f;
|
||||
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
|
||||
td->flag |= TD_SELECTED;
|
||||
td->dist = 0.0;
|
||||
|
||||
unit_m3(td->mtx);
|
||||
unit_m3(td->smtx);
|
||||
}
|
||||
|
||||
static void planeTrackToTransData(const int framenr,
|
||||
TransData *td,
|
||||
TransData2D *td2d,
|
||||
TransDataTracking *tdt,
|
||||
MovieTrackingPlaneTrack *plane_track,
|
||||
const float aspect[2])
|
||||
{
|
||||
MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_ensure(plane_track, framenr);
|
||||
int i;
|
||||
|
||||
tdt->flag = plane_marker->flag;
|
||||
plane_marker->flag &= ~PLANE_MARKER_TRACKED;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
planeMarkerToTransDataInit(td++, td2d++, tdt++, plane_track, plane_marker->corners[i], aspect);
|
||||
}
|
||||
}
|
||||
|
||||
static void transDataTrackingFree(TransInfo *UNUSED(t),
|
||||
TransDataContainer *UNUSED(tc),
|
||||
TransCustomData *custom_data)
|
||||
{
|
||||
if (custom_data->data) {
|
||||
TransDataTracking *tdt = custom_data->data;
|
||||
if (tdt->smarkers) {
|
||||
MEM_freeN(tdt->smarkers);
|
||||
}
|
||||
|
||||
MEM_freeN(tdt);
|
||||
custom_data->data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void createTransTrackingTracksData(bContext *C, TransInfo *t)
|
||||
{
|
||||
TransData *td;
|
||||
TransData2D *td2d;
|
||||
SpaceClip *sc = CTX_wm_space_clip(C);
|
||||
MovieClip *clip = ED_space_clip_get_clip(sc);
|
||||
ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
|
||||
ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(&clip->tracking);
|
||||
MovieTrackingTrack *track;
|
||||
MovieTrackingPlaneTrack *plane_track;
|
||||
TransDataTracking *tdt;
|
||||
int framenr = ED_space_clip_get_clip_frame_number(sc);
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
/* count */
|
||||
tc->data_len = 0;
|
||||
|
||||
track = tracksbase->first;
|
||||
while (track) {
|
||||
if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
|
||||
tc->data_len++; /* offset */
|
||||
|
||||
if (track->flag & SELECT) {
|
||||
tc->data_len++;
|
||||
}
|
||||
|
||||
if (track->pat_flag & SELECT) {
|
||||
tc->data_len += 4;
|
||||
}
|
||||
|
||||
if (track->search_flag & SELECT) {
|
||||
tc->data_len += 2;
|
||||
}
|
||||
}
|
||||
|
||||
track = track->next;
|
||||
}
|
||||
|
||||
for (plane_track = plane_tracks_base->first; plane_track; plane_track = plane_track->next) {
|
||||
if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
|
||||
tc->data_len += 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (tc->data_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransTracking TransData");
|
||||
td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D),
|
||||
"TransTracking TransData2D");
|
||||
tdt = tc->custom.type.data = MEM_callocN(tc->data_len * sizeof(TransDataTracking),
|
||||
"TransTracking TransDataTracking");
|
||||
|
||||
tc->custom.type.free_cb = transDataTrackingFree;
|
||||
|
||||
/* create actual data */
|
||||
track = tracksbase->first;
|
||||
while (track) {
|
||||
if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
|
||||
trackToTransData(framenr, td, td2d, tdt, track, t->aspect);
|
||||
|
||||
/* offset */
|
||||
td++;
|
||||
td2d++;
|
||||
tdt++;
|
||||
|
||||
if (track->flag & SELECT) {
|
||||
td++;
|
||||
td2d++;
|
||||
tdt++;
|
||||
}
|
||||
|
||||
if (track->pat_flag & SELECT) {
|
||||
td += 4;
|
||||
td2d += 4;
|
||||
tdt += 4;
|
||||
}
|
||||
|
||||
if (track->search_flag & SELECT) {
|
||||
td += 2;
|
||||
td2d += 2;
|
||||
tdt += 2;
|
||||
}
|
||||
}
|
||||
|
||||
track = track->next;
|
||||
}
|
||||
|
||||
for (plane_track = plane_tracks_base->first; plane_track; plane_track = plane_track->next) {
|
||||
if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
|
||||
planeTrackToTransData(framenr, td, td2d, tdt, plane_track, t->aspect);
|
||||
td += 4;
|
||||
td2d += 4;
|
||||
tdt += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void markerToTransCurveDataInit(TransData *td,
|
||||
TransData2D *td2d,
|
||||
TransDataTracking *tdt,
|
||||
MovieTrackingTrack *track,
|
||||
MovieTrackingMarker *marker,
|
||||
MovieTrackingMarker *prev_marker,
|
||||
short coord,
|
||||
float size)
|
||||
{
|
||||
float frames_delta = (marker->framenr - prev_marker->framenr);
|
||||
|
||||
tdt->flag = marker->flag;
|
||||
marker->flag &= ~MARKER_TRACKED;
|
||||
|
||||
tdt->mode = transDataTracking_ModeCurves;
|
||||
tdt->coord = coord;
|
||||
tdt->scale = 1.0f / size * frames_delta;
|
||||
tdt->prev_pos = prev_marker->pos;
|
||||
tdt->track = track;
|
||||
|
||||
/* calculate values depending on marker's speed */
|
||||
td2d->loc[0] = marker->framenr;
|
||||
td2d->loc[1] = (marker->pos[coord] - prev_marker->pos[coord]) * size / frames_delta;
|
||||
td2d->loc[2] = 0.0f;
|
||||
|
||||
td2d->loc2d = marker->pos; /* current location */
|
||||
|
||||
td->flag = 0;
|
||||
td->loc = td2d->loc;
|
||||
copy_v3_v3(td->center, td->loc);
|
||||
copy_v3_v3(td->iloc, td->loc);
|
||||
|
||||
memset(td->axismtx, 0, sizeof(td->axismtx));
|
||||
td->axismtx[2][2] = 1.0f;
|
||||
|
||||
td->ext = NULL;
|
||||
td->val = NULL;
|
||||
|
||||
td->flag |= TD_SELECTED;
|
||||
td->dist = 0.0;
|
||||
|
||||
unit_m3(td->mtx);
|
||||
unit_m3(td->smtx);
|
||||
}
|
||||
|
||||
static void createTransTrackingCurvesData(bContext *C, TransInfo *t)
|
||||
{
|
||||
TransData *td;
|
||||
TransData2D *td2d;
|
||||
SpaceClip *sc = CTX_wm_space_clip(C);
|
||||
MovieClip *clip = ED_space_clip_get_clip(sc);
|
||||
ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
|
||||
MovieTrackingTrack *track;
|
||||
MovieTrackingMarker *marker, *prev_marker;
|
||||
TransDataTracking *tdt;
|
||||
int i, width, height;
|
||||
|
||||
BKE_movieclip_get_size(clip, &sc->user, &width, &height);
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
/* count */
|
||||
tc->data_len = 0;
|
||||
|
||||
if ((sc->flag & SC_SHOW_GRAPH_TRACKS_MOTION) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
track = tracksbase->first;
|
||||
while (track) {
|
||||
if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
|
||||
for (i = 1; i < track->markersnr; i++) {
|
||||
marker = &track->markers[i];
|
||||
prev_marker = &track->markers[i - 1];
|
||||
|
||||
if ((marker->flag & MARKER_DISABLED) || (prev_marker->flag & MARKER_DISABLED)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (marker->flag & MARKER_GRAPH_SEL_X) {
|
||||
tc->data_len += 1;
|
||||
}
|
||||
|
||||
if (marker->flag & MARKER_GRAPH_SEL_Y) {
|
||||
tc->data_len += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
track = track->next;
|
||||
}
|
||||
|
||||
if (tc->data_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransTracking TransData");
|
||||
td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D),
|
||||
"TransTracking TransData2D");
|
||||
tc->custom.type.data = tdt = MEM_callocN(tc->data_len * sizeof(TransDataTracking),
|
||||
"TransTracking TransDataTracking");
|
||||
tc->custom.type.free_cb = transDataTrackingFree;
|
||||
|
||||
/* create actual data */
|
||||
track = tracksbase->first;
|
||||
while (track) {
|
||||
if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
|
||||
for (i = 1; i < track->markersnr; i++) {
|
||||
marker = &track->markers[i];
|
||||
prev_marker = &track->markers[i - 1];
|
||||
|
||||
if ((marker->flag & MARKER_DISABLED) || (prev_marker->flag & MARKER_DISABLED)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (marker->flag & MARKER_GRAPH_SEL_X) {
|
||||
markerToTransCurveDataInit(
|
||||
td, td2d, tdt, track, marker, &track->markers[i - 1], 0, width);
|
||||
td += 1;
|
||||
td2d += 1;
|
||||
tdt += 1;
|
||||
}
|
||||
|
||||
if (marker->flag & MARKER_GRAPH_SEL_Y) {
|
||||
markerToTransCurveDataInit(
|
||||
td, td2d, tdt, track, marker, &track->markers[i - 1], 1, height);
|
||||
|
||||
td += 1;
|
||||
td2d += 1;
|
||||
tdt += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
track = track->next;
|
||||
}
|
||||
}
|
||||
|
||||
void createTransTrackingData(bContext *C, TransInfo *t)
|
||||
{
|
||||
ARegion *ar = CTX_wm_region(C);
|
||||
SpaceClip *sc = CTX_wm_space_clip(C);
|
||||
MovieClip *clip = ED_space_clip_get_clip(sc);
|
||||
int width, height;
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
tc->data_len = 0;
|
||||
|
||||
if (!clip) {
|
||||
return;
|
||||
}
|
||||
|
||||
BKE_movieclip_get_size(clip, &sc->user, &width, &height);
|
||||
|
||||
if (width == 0 || height == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ar->regiontype == RGN_TYPE_PREVIEW) {
|
||||
/* transformation was called from graph editor */
|
||||
createTransTrackingCurvesData(C, t);
|
||||
}
|
||||
else {
|
||||
createTransTrackingTracksData(C, t);
|
||||
}
|
||||
}
|
||||
|
||||
void cancelTransTracking(TransInfo *t)
|
||||
{
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
SpaceClip *sc = t->sa->spacedata.first;
|
||||
int i, framenr = ED_space_clip_get_clip_frame_number(sc);
|
||||
TransDataTracking *tdt_array = tc->custom.type.data;
|
||||
|
||||
i = 0;
|
||||
while (i < tc->data_len) {
|
||||
TransDataTracking *tdt = &tdt_array[i];
|
||||
|
||||
if (tdt->mode == transDataTracking_ModeTracks) {
|
||||
MovieTrackingTrack *track = tdt->track;
|
||||
MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
|
||||
|
||||
marker->flag = tdt->flag;
|
||||
|
||||
if (track->flag & SELECT) {
|
||||
i++;
|
||||
}
|
||||
|
||||
if (track->pat_flag & SELECT) {
|
||||
i += 4;
|
||||
}
|
||||
|
||||
if (track->search_flag & SELECT) {
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
else if (tdt->mode == transDataTracking_ModeCurves) {
|
||||
MovieTrackingTrack *track = tdt->track;
|
||||
MovieTrackingMarker *marker, *prev_marker;
|
||||
int a;
|
||||
|
||||
for (a = 1; a < track->markersnr; a++) {
|
||||
marker = &track->markers[a];
|
||||
prev_marker = &track->markers[a - 1];
|
||||
|
||||
if ((marker->flag & MARKER_DISABLED) || (prev_marker->flag & MARKER_DISABLED)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (marker->flag & (MARKER_GRAPH_SEL_X | MARKER_GRAPH_SEL_Y)) {
|
||||
marker->flag = tdt->flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (tdt->mode == transDataTracking_ModePlaneTracks) {
|
||||
MovieTrackingPlaneTrack *plane_track = tdt->plane_track;
|
||||
MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(plane_track, framenr);
|
||||
|
||||
plane_marker->flag = tdt->flag;
|
||||
i += 3;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Clip Editor Motion Tracking Transform Creation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void flushTransTracking(TransInfo *t)
|
||||
{
|
||||
TransData *td;
|
||||
TransData2D *td2d;
|
||||
TransDataTracking *tdt;
|
||||
int a;
|
||||
|
||||
if (t->state == TRANS_CANCEL) {
|
||||
cancelTransTracking(t);
|
||||
}
|
||||
|
||||
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
||||
|
||||
/* flush to 2d vector from internally used 3d vector */
|
||||
for (a = 0, td = tc->data, td2d = tc->data_2d, tdt = tc->custom.type.data; a < tc->data_len;
|
||||
a++, td2d++, td++, tdt++) {
|
||||
if (tdt->mode == transDataTracking_ModeTracks) {
|
||||
float loc2d[2];
|
||||
|
||||
if (t->mode == TFM_ROTATION && tdt->area == TRACK_AREA_SEARCH) {
|
||||
continue;
|
||||
}
|
||||
|
||||
loc2d[0] = td2d->loc[0] / t->aspect[0];
|
||||
loc2d[1] = td2d->loc[1] / t->aspect[1];
|
||||
|
||||
if (t->flag & T_ALT_TRANSFORM) {
|
||||
if (t->mode == TFM_RESIZE) {
|
||||
if (tdt->area != TRACK_AREA_PAT) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (t->mode == TFM_TRANSLATION) {
|
||||
if (tdt->area == TRACK_AREA_POINT && tdt->relative) {
|
||||
float d[2], d2[2];
|
||||
|
||||
if (!tdt->smarkers) {
|
||||
tdt->smarkers = MEM_callocN(sizeof(*tdt->smarkers) * tdt->markersnr,
|
||||
"flushTransTracking markers");
|
||||
for (a = 0; a < tdt->markersnr; a++) {
|
||||
copy_v2_v2(tdt->smarkers[a], tdt->markers[a].pos);
|
||||
}
|
||||
}
|
||||
|
||||
sub_v2_v2v2(d, loc2d, tdt->soffset);
|
||||
sub_v2_v2(d, tdt->srelative);
|
||||
|
||||
sub_v2_v2v2(d2, loc2d, tdt->srelative);
|
||||
|
||||
for (a = 0; a < tdt->markersnr; a++) {
|
||||
add_v2_v2v2(tdt->markers[a].pos, tdt->smarkers[a], d2);
|
||||
}
|
||||
|
||||
negate_v2_v2(td2d->loc2d, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tdt->area != TRACK_AREA_POINT || tdt->relative == NULL) {
|
||||
td2d->loc2d[0] = loc2d[0];
|
||||
td2d->loc2d[1] = loc2d[1];
|
||||
|
||||
if (tdt->relative) {
|
||||
sub_v2_v2(td2d->loc2d, tdt->relative);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (tdt->mode == transDataTracking_ModeCurves) {
|
||||
td2d->loc2d[tdt->coord] = tdt->prev_pos[tdt->coord] + td2d->loc[1] * tdt->scale;
|
||||
}
|
||||
else if (tdt->mode == transDataTracking_ModePlaneTracks) {
|
||||
td2d->loc2d[0] = td2d->loc[0] / t->aspect[0];
|
||||
td2d->loc2d[1] = td2d->loc[1] / t->aspect[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -109,6 +109,7 @@
|
|||
#include "UI_view2d.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
/* ************************** Functions *************************** */
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@
|
|||
|
||||
/* local module include */
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#include "ED_mesh.h"
|
||||
|
||||
#include "transform.h"
|
||||
#include "transform_convert.h"
|
||||
|
||||
typedef struct TransformModeItem {
|
||||
const char *idname;
|
||||
|
|
Loading…
Reference in New Issue