2011-02-23 11:52:22 +01:00
/*
2009-09-19 02:18:42 +02:00
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software Foundation ,
2010-02-12 14:34:04 +01:00
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
2009-09-19 02:18:42 +02:00
*
* The Original Code is Copyright ( C ) 2009 , Blender Foundation , Joshua Leung
* This is a new part of Blender
*/
2011-02-27 21:29:51 +01:00
2019-02-17 22:08:12 +01:00
/** \file
* \ ingroup edarmature
2011-02-27 21:29:51 +01:00
*/
2009-09-19 02:18:42 +02:00
# include "MEM_guardedalloc.h"
2009-11-10 21:43:45 +01:00
# include "BLI_math.h"
2009-09-19 02:18:42 +02:00
# include "BLI_blenlib.h"
# include "BLI_dlrbTree.h"
# include "DNA_anim_types.h"
# include "DNA_armature_types.h"
# include "DNA_object_types.h"
# include "DNA_scene_types.h"
# include "BKE_fcurve.h"
2017-09-21 07:38:30 +02:00
# include "BKE_nla.h"
2009-09-19 02:18:42 +02:00
# include "BKE_context.h"
2018-10-20 00:08:12 +02:00
# include "BKE_layer.h"
2011-09-14 03:48:55 +02:00
# include "BKE_object.h"
2009-09-19 02:18:42 +02:00
# include "BKE_report.h"
2015-05-17 09:45:59 +02:00
# include "BKE_unit.h"
2009-09-19 02:18:42 +02:00
# include "RNA_access.h"
# include "RNA_define.h"
# include "WM_api.h"
# include "WM_types.h"
2016-05-14 10:00:52 +02:00
# include "UI_interface.h"
2009-09-19 02:18:42 +02:00
# include "ED_armature.h"
# include "ED_keyframes_draw.h"
2011-03-31 03:37:42 +02:00
# include "ED_markers.h"
2015-05-17 09:45:59 +02:00
# include "ED_numinput.h"
2009-09-19 02:18:42 +02:00
# include "ED_screen.h"
# include "armature_intern.h"
/* **************************************************** */
2018-06-04 09:31:30 +02:00
/* == POSE 'SLIDING' TOOLS ==
2009-09-19 02:18:42 +02:00
*
* A ) Push & Relax , Breakdowner
* These tools provide the animator with various capabilities
* for interactively controlling the spacing of poses , but also
* for ' pushing ' and / or ' relaxing ' extremes as they see fit .
*
2011-03-24 04:02:34 +01:00
* B ) Propagate
* This tool copies elements of the selected pose to successive
* keyframes , allowing the animator to go back and modify the poses
* for some " static " pose controls , without having to repeatedly
* doing a " next paste " dance .
*
* C ) Pose Sculpting
2009-09-19 02:18:42 +02:00
* This is yet to be implemented , but the idea here is to use
* sculpting techniques to make it easier to pose rigs by allowing
2018-06-01 18:19:39 +02:00
* rigs to be manipulated using a familiar paint - based interface .
2009-09-19 02:18:42 +02:00
*/
/* **************************************************** */
/* A) Push & Relax, Breakdowner */
/* Temporary data shared between these operators */
typedef struct tPoseSlideOp {
2019-01-15 13:24:20 +01:00
/** current scene */
Scene * scene ;
/** area that we're operating in (needed for modal()) */
ScrArea * sa ;
/** region that we're operating in (needed for modal()) */
ARegion * ar ;
/** len of the PoseSlideObject array. */
uint objects_len ;
/** links between posechannels and f-curves for all the pose objects. */
ListBase pfLinks ;
/** binary tree for quicker searching for keyframes (when applicable) */
DLRBT_Tree keys ;
/** current frame number - global time */
int cframe ;
/** frame before current frame (blend-from) - global time */
int prevFrame ;
/** frame after current frame (blend-to) - global time */
int nextFrame ;
/** sliding mode (ePoseSlide_Modes) */
short mode ;
/** unused for now, but can later get used for storing runtime settings.... */
short flag ;
/** which transforms/channels are affected (ePoseSlide_Channels) */
short channels ;
/** axis-limits for transforms (ePoseSlide_AxisLock) */
short axislock ;
/** 0-1 value for determining the influence of whatever is relevant */
float percentage ;
/** numeric input */
NumInput num ;
2018-10-20 00:08:12 +02:00
struct tPoseSlideObject * ob_data_array ;
2009-09-19 02:18:42 +02:00
} tPoseSlideOp ;
2018-10-20 00:08:12 +02:00
typedef struct tPoseSlideObject {
Object * ob ; /* active object that Pose Info comes from */
float prevFrameF ; /* prevFrame, but in local action time (for F-Curve lookups to work) */
float nextFrameF ; /* nextFrame, but in local action time (for F-Curve lookups to work) */
bool valid ;
} tPoseSlideObject ;
2009-09-19 02:18:42 +02:00
/* Pose Sliding Modes */
typedef enum ePoseSlide_Modes {
2012-05-08 22:18:33 +02:00
POSESLIDE_PUSH = 0 , /* exaggerate the pose... */
POSESLIDE_RELAX , /* soften the pose... */
POSESLIDE_BREAKDOWN , /* slide between the endpoint poses, finding a 'soft' spot */
2009-09-19 02:18:42 +02:00
} ePoseSlide_Modes ;
2017-03-10 23:52:58 +01:00
/* Transforms/Channels to Affect */
typedef enum ePoseSlide_Channels {
PS_TFM_ALL = 0 , /* All transforms and properties */
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
PS_TFM_LOC , /* Loc/Rot/Scale */
PS_TFM_ROT ,
PS_TFM_SIZE ,
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
PS_TFM_BBONE_SHAPE , /* Bendy Bones */
2018-06-04 09:31:30 +02:00
2019-04-16 16:40:47 +02:00
PS_TFM_PROPS , /* Custom Properties */
2017-03-10 23:52:58 +01:00
} ePoseSlide_Channels ;
/* Property enum for ePoseSlide_Channels */
2017-10-18 06:07:26 +02:00
static const EnumPropertyItem prop_channels_types [ ] = {
2018-06-04 09:31:30 +02:00
{ PS_TFM_ALL , " ALL " , 0 , " All Properties " ,
2017-03-10 23:52:58 +01:00
" All properties, including transforms, bendy bone shape, and custom properties " } ,
{ PS_TFM_LOC , " LOC " , 0 , " Location " , " Location only " } ,
{ PS_TFM_ROT , " ROT " , 0 , " Rotation " , " Rotation only " } ,
{ PS_TFM_SIZE , " SIZE " , 0 , " Scale " , " Scale only " } ,
{ PS_TFM_BBONE_SHAPE , " BBONE " , 0 , " Bendy Bone " , " Bendy Bone shape properties " } ,
{ PS_TFM_PROPS , " CUSTOM " , 0 , " Custom Properties " , " Custom properties " } ,
2019-02-03 04:01:45 +01:00
{ 0 , NULL , 0 , NULL , NULL } ,
2017-03-10 23:52:58 +01:00
} ;
/* Axis Locks */
typedef enum ePoseSlide_AxisLock {
PS_LOCK_X = ( 1 < < 0 ) ,
PS_LOCK_Y = ( 1 < < 1 ) ,
2019-01-15 13:57:49 +01:00
PS_LOCK_Z = ( 1 < < 2 ) ,
2017-03-10 23:52:58 +01:00
} ePoseSlide_AxisLock ;
/* Property enum for ePoseSlide_AxisLock */
2017-10-18 06:07:26 +02:00
static const EnumPropertyItem prop_axis_lock_types [ ] = {
2017-03-10 23:52:58 +01:00
{ 0 , " FREE " , 0 , " Free " , " All axes are affected " } ,
{ PS_LOCK_X , " X " , 0 , " X " , " Only X-axis transforms are affected " } ,
{ PS_LOCK_Y , " Y " , 0 , " Y " , " Only Y-axis transforms are affected " } ,
{ PS_LOCK_Z , " Z " , 0 , " Z " , " Only Z-axis transforms are affected " } ,
/* TODO: Combinations? */
2019-02-03 04:01:45 +01:00
{ 0 , NULL , 0 , NULL , NULL } ,
2017-03-10 23:52:58 +01:00
} ;
2009-09-19 02:18:42 +02:00
/* ------------------------------------ */
/* operator init */
2018-10-20 00:08:12 +02:00
static int pose_slide_init ( bContext * C , wmOperator * op , ePoseSlide_Modes mode )
2009-09-19 02:18:42 +02:00
{
tPoseSlideOp * pso ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* init slide-op data */
2012-05-08 22:18:33 +02:00
pso = op - > customdata = MEM_callocN ( sizeof ( tPoseSlideOp ) , " tPoseSlideOp " ) ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* get info from context */
2012-05-08 22:18:33 +02:00
pso - > scene = CTX_data_scene ( C ) ;
pso - > sa = CTX_wm_area ( C ) ; /* only really needed when doing modal() */
pso - > ar = CTX_wm_region ( C ) ; /* only really needed when doing modal() */
2018-06-04 09:31:30 +02:00
2012-05-08 22:18:33 +02:00
pso - > cframe = pso - > scene - > r . cfra ;
pso - > mode = mode ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* set range info from property values - these may get overridden for the invoke() */
2012-05-08 22:18:33 +02:00
pso - > percentage = RNA_float_get ( op - > ptr , " percentage " ) ;
pso - > prevFrame = RNA_int_get ( op - > ptr , " prev_frame " ) ;
pso - > nextFrame = RNA_int_get ( op - > ptr , " next_frame " ) ;
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
/* get the set of properties/axes that can be operated on */
pso - > channels = RNA_enum_get ( op - > ptr , " channels " ) ;
pso - > axislock = RNA_enum_get ( op - > ptr , " axis_lock " ) ;
2018-06-04 09:31:30 +02:00
2018-10-20 00:08:12 +02:00
/* for each Pose-Channel which gets affected, get the F-Curves for that channel
2019-02-11 00:51:25 +01:00
* and set the relevant transform flags . . . */
2018-10-20 00:08:12 +02:00
poseAnim_mapping_get ( C , & pso - > pfLinks ) ;
2018-06-04 09:31:30 +02:00
2018-10-20 00:08:12 +02:00
Object * * objects = BKE_view_layer_array_from_objects_in_mode_unique_data ( CTX_data_view_layer ( C ) ,
2018-11-25 12:50:34 +01:00
CTX_wm_view3d ( C ) ,
2018-10-20 00:08:12 +02:00
& pso - > objects_len ,
OB_MODE_POSE ) ;
pso - > ob_data_array = MEM_callocN ( pso - > objects_len * sizeof ( tPoseSlideObject ) , " pose slide objects data " ) ;
2018-06-04 09:31:30 +02:00
2018-10-20 00:08:12 +02:00
for ( uint ob_index = 0 ; ob_index < pso - > objects_len ; ob_index + + ) {
tPoseSlideObject * ob_data = & pso - > ob_data_array [ ob_index ] ;
Object * ob_iter = poseAnim_object_get ( objects [ ob_index ] ) ;
2018-06-04 09:31:30 +02:00
2018-10-20 00:08:12 +02:00
/* Ensure validity of the settings from the context. */
if ( ob_iter = = NULL ) {
continue ;
}
ob_data - > ob = ob_iter ;
ob_data - > valid = true ;
2018-06-04 09:31:30 +02:00
2018-10-20 00:08:12 +02:00
/* apply NLA mapping corrections so the frame lookups work */
ob_data - > prevFrameF = BKE_nla_tweakedit_remap ( ob_data - > ob - > adt , pso - > prevFrame , NLATIME_CONVERT_UNMAP ) ;
ob_data - > nextFrameF = BKE_nla_tweakedit_remap ( ob_data - > ob - > adt , pso - > nextFrame , NLATIME_CONVERT_UNMAP ) ;
/* set depsgraph flags */
/* make sure the lock is set OK, unlock can be accidentally saved? */
ob_data - > ob - > pose - > flag | = POSE_LOCKED ;
ob_data - > ob - > pose - > flag & = ~ POSE_DO_UNLOCK ;
}
MEM_freeN ( objects ) ;
2018-06-04 09:31:30 +02:00
/* do basic initialize of RB-BST used for finding keyframes, but leave the filling of it up
2009-09-19 02:18:42 +02:00
* to the caller of this ( usually only invoke ( ) will do it , to make things more efficient ) .
*/
BLI_dlrbTree_init ( & pso - > keys ) ;
2018-06-04 09:31:30 +02:00
2015-05-17 09:45:59 +02:00
/* initialise numeric input */
initNumInput ( & pso - > num ) ;
pso - > num . idx_max = 0 ; /* one axis */
pso - > num . val_flag [ 0 ] | = NUM_NO_NEGATIVE ;
pso - > num . unit_type [ 0 ] = B_UNIT_NONE ; /* percentages don't have any units... */
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* return status is whether we've got all the data we were requested to get */
return 1 ;
}
/* exiting the operator - free data */
2010-10-16 04:40:31 +02:00
static void pose_slide_exit ( wmOperator * op )
2009-09-19 02:18:42 +02:00
{
2012-05-08 22:18:33 +02:00
tPoseSlideOp * pso = op - > customdata ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* if data exists, clear its data and exit */
if ( pso ) {
/* free the temp pchan links and their data */
2010-02-19 12:42:21 +01:00
poseAnim_mapping_free ( & pso - > pfLinks ) ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* free RB-BST for keyframes (if it contained data) */
BLI_dlrbTree_free ( & pso - > keys ) ;
2018-06-04 09:31:30 +02:00
2018-10-20 00:08:12 +02:00
if ( pso - > ob_data_array ! = NULL ) {
MEM_freeN ( pso - > ob_data_array ) ;
}
2009-09-19 02:18:42 +02:00
/* free data itself */
MEM_freeN ( pso ) ;
}
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* cleanup */
2012-05-08 22:18:33 +02:00
op - > customdata = NULL ;
2009-09-19 02:18:42 +02:00
}
/* ------------------------------------ */
2009-09-20 07:05:16 +02:00
/* helper for apply() / reset() - refresh the data */
2012-05-08 22:18:33 +02:00
static void pose_slide_refresh ( bContext * C , tPoseSlideOp * pso )
2009-09-20 07:05:16 +02:00
{
2010-02-19 12:42:21 +01:00
/* wrapper around the generic version, allowing us to add some custom stuff later still */
2018-10-20 00:08:12 +02:00
for ( uint ob_index = 0 ; ob_index < pso - > objects_len ; ob_index + + ) {
tPoseSlideObject * ob_data = & pso - > ob_data_array [ ob_index ] ;
if ( ob_data - > valid ) {
poseAnim_mapping_refresh ( C , pso - > scene , ob_data - > ob ) ;
}
}
}
2019-03-19 05:17:46 +01:00
/**
* Although this lookup is not ideal , we won ' t be dealing with a lot of objects at a given time .
2019-04-09 16:06:53 +02:00
* But if it comes to that we can instead store prev / next frame in the # tPChanFCurveLink .
2019-03-19 05:17:46 +01:00
*/
2018-10-20 00:08:12 +02:00
static bool pose_frame_range_from_object_get ( tPoseSlideOp * pso , Object * ob , float * prevFrameF , float * nextFrameF )
{
for ( uint ob_index = 0 ; ob_index < pso - > objects_len ; ob_index + + ) {
tPoseSlideObject * ob_data = & pso - > ob_data_array [ ob_index ] ;
Object * ob_iter = ob_data - > ob ;
if ( ob_iter = = ob ) {
* prevFrameF = ob_data - > prevFrameF ;
* nextFrameF = ob_data - > nextFrameF ;
return true ;
}
}
2018-10-24 04:47:04 +02:00
* prevFrameF = * nextFrameF = 0.0f ;
2018-10-20 00:08:12 +02:00
return false ;
2009-09-19 02:18:42 +02:00
}
2011-03-13 13:22:57 +01:00
/* helper for apply() - perform sliding for some value */
2018-10-20 00:08:12 +02:00
static void pose_slide_apply_val (
tPoseSlideOp * pso ,
FCurve * fcu ,
Object * ob ,
float * val )
2011-03-13 13:22:57 +01:00
{
2018-10-20 00:08:12 +02:00
float prevFrameF , nextFrameF ;
2011-03-13 13:22:57 +01:00
float cframe = ( float ) pso - > cframe ;
float sVal , eVal ;
float w1 , w2 ;
2018-06-04 09:31:30 +02:00
2018-10-20 00:08:12 +02:00
pose_frame_range_from_object_get ( pso , ob , & prevFrameF , & nextFrameF ) ;
2011-03-13 13:22:57 +01:00
/* get keyframe values for endpoint poses to blend with */
2012-05-08 22:18:33 +02:00
/* previous/start */
2018-10-20 00:08:12 +02:00
sVal = evaluate_fcurve ( fcu , prevFrameF ) ;
2012-05-08 22:18:33 +02:00
/* next/end */
2018-10-20 00:08:12 +02:00
eVal = evaluate_fcurve ( fcu , nextFrameF ) ;
2018-06-04 09:31:30 +02:00
2015-04-08 04:17:36 +02:00
/* if both values are equal, don't do anything */
2015-04-08 08:13:00 +02:00
if ( IS_EQF ( sVal , eVal ) ) {
2015-04-08 04:17:36 +02:00
( * val ) = sVal ;
return ;
}
2018-06-04 09:31:30 +02:00
2011-03-13 13:22:57 +01:00
/* calculate the relative weights of the endpoints */
if ( pso - > mode = = POSESLIDE_BREAKDOWN ) {
/* get weights from the percentage control */
2012-05-08 22:18:33 +02:00
w1 = pso - > percentage ; /* this must come second */
w2 = 1.0f - w1 ; /* this must come first */
2011-03-13 13:22:57 +01:00
}
else {
2018-11-14 02:53:15 +01:00
/* - these weights are derived from the relative distance of these
* poses from the current frame
* - they then get normalized so that they only sum up to 1
2011-03-13 13:22:57 +01:00
*/
2018-06-04 09:31:30 +02:00
float wtot ;
2011-03-13 13:22:57 +01:00
w1 = cframe - ( float ) pso - > prevFrame ;
w2 = ( float ) pso - > nextFrame - cframe ;
2018-06-04 09:31:30 +02:00
2011-03-13 13:22:57 +01:00
wtot = w1 + w2 ;
2012-05-08 22:18:33 +02:00
w1 = ( w1 / wtot ) ;
w2 = ( w2 / wtot ) ;
2011-03-13 13:22:57 +01:00
}
2018-06-04 09:31:30 +02:00
2011-03-13 13:22:57 +01:00
/* depending on the mode, calculate the new value
2018-11-14 02:53:15 +01:00
* - in all of these , the start + end values are multiplied by w2 and w1 ( respectively ) ,
* since multiplication in another order would decrease the value the current frame is closer to
2011-03-13 13:22:57 +01:00
*/
switch ( pso - > mode ) {
case POSESLIDE_PUSH : /* make the current pose more pronounced */
{
2012-03-08 05:12:11 +01:00
/* perform a weighted average here, favoring the middle pose
2018-11-14 02:53:15 +01:00
* - numerator should be larger than denominator to ' expand ' the result
* - perform this weighting a number of times given by the percentage . . .
2011-03-13 13:22:57 +01:00
*/
2019-01-15 13:24:20 +01:00
/* TODO: maybe a sensitivity ctrl on top of this is needed */
int iters = ( int ) ceil ( 10.0f * pso - > percentage ) ;
2018-06-04 09:31:30 +02:00
2011-03-13 13:22:57 +01:00
while ( iters - - > 0 ) {
2012-05-08 22:18:33 +02:00
( * val ) = ( - ( ( sVal * w2 ) + ( eVal * w1 ) ) + ( ( * val ) * 6.0f ) ) / 5.0f ;
2011-03-13 13:22:57 +01:00
}
2013-07-19 17:23:42 +02:00
break ;
2011-03-13 13:22:57 +01:00
}
case POSESLIDE_RELAX : /* make the current pose more like its surrounding ones */
{
2012-03-08 05:12:11 +01:00
/* perform a weighted average here, favoring the middle pose
2018-11-14 02:53:15 +01:00
* - numerator should be smaller than denominator to ' relax ' the result
* - perform this weighting a number of times given by the percentage . . .
2011-03-13 13:22:57 +01:00
*/
2019-01-15 13:24:20 +01:00
/* TODO: maybe a sensitivity ctrl on top of this is needed */
int iters = ( int ) ceil ( 10.0f * pso - > percentage ) ;
2018-06-04 09:31:30 +02:00
2011-03-13 13:22:57 +01:00
while ( iters - - > 0 ) {
2012-05-08 22:18:33 +02:00
( * val ) = ( ( ( sVal * w2 ) + ( eVal * w1 ) ) + ( ( * val ) * 5.0f ) ) / 6.0f ;
2011-03-13 13:22:57 +01:00
}
2013-07-19 17:23:42 +02:00
break ;
2011-03-13 13:22:57 +01:00
}
case POSESLIDE_BREAKDOWN : /* make the current pose slide around between the endpoints */
{
/* perform simple linear interpolation - coefficient for start must come from pso->percentage... */
2012-07-07 01:56:59 +02:00
/* TODO: make this use some kind of spline interpolation instead? */
2012-05-08 22:18:33 +02:00
( * val ) = ( ( sVal * w2 ) + ( eVal * w1 ) ) ;
2013-07-19 17:23:42 +02:00
break ;
2011-03-13 13:22:57 +01:00
}
}
}
2009-09-19 02:18:42 +02:00
/* helper for apply() - perform sliding for some 3-element vector */
2012-05-08 22:18:33 +02:00
static void pose_slide_apply_vec3 ( tPoseSlideOp * pso , tPChanFCurveLink * pfl , float vec [ 3 ] , const char propName [ ] )
2009-09-19 02:18:42 +02:00
{
2012-05-08 22:18:33 +02:00
LinkData * ld = NULL ;
char * path = NULL ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* get the path to use... */
2012-05-08 22:18:33 +02:00
path = BLI_sprintfN ( " %s.%s " , pfl - > pchan_path , propName ) ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* using this path, find each matching F-Curve for the variables we're interested in */
2012-05-08 22:18:33 +02:00
while ( ( ld = poseAnim_mapping_getNextFCurve ( & pfl - > fcurves , ld , path ) ) ) {
FCurve * fcu = ( FCurve * ) ld - > data ;
2017-03-10 23:52:58 +01:00
const int idx = fcu - > array_index ;
const int lock = pso - > axislock ;
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
/* check if this F-Curve is ok given the current axis locks */
2013-03-26 08:29:01 +01:00
BLI_assert ( fcu - > array_index < 3 ) ;
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
if ( ( lock = = 0 ) | |
( ( lock & PS_LOCK_X ) & & ( idx = = 0 ) ) | |
( ( lock & PS_LOCK_Y ) & & ( idx = = 1 ) ) | |
( ( lock & PS_LOCK_Z ) & & ( idx = = 2 ) ) )
{
/* just work on these channels one by one... there's no interaction between values */
2018-10-20 00:08:12 +02:00
pose_slide_apply_val ( pso , fcu , pfl - > ob , & vec [ fcu - > array_index ] ) ;
2017-03-10 23:52:58 +01:00
}
2011-03-13 13:22:57 +01:00
}
2018-06-04 09:31:30 +02:00
2011-03-13 13:22:57 +01:00
/* free the temp path we got */
MEM_freeN ( path ) ;
}
2016-05-17 17:19:06 +02:00
/* helper for apply() - perform sliding for custom properties or bbone properties */
static void pose_slide_apply_props ( tPoseSlideOp * pso , tPChanFCurveLink * pfl , const char prop_prefix [ ] )
2011-03-13 13:22:57 +01:00
{
PointerRNA ptr = { { NULL } } ;
LinkData * ld ;
int len = strlen ( pfl - > pchan_path ) ;
2018-06-04 09:31:30 +02:00
2011-03-13 13:22:57 +01:00
/* setup pointer RNA for resolving paths */
RNA_pointer_create ( NULL , & RNA_PoseBone , pfl - > pchan , & ptr ) ;
2018-06-04 09:31:30 +02:00
/* - custom properties are just denoted using ["..."][etc.] after the end of the base path,
2016-05-17 17:19:06 +02:00
* so just check for opening pair after the end of the path
* - bbone properties are similar , but they always start with a prefix " bbone_* " ,
* so a similar method should work here for those too
2011-03-13 13:22:57 +01:00
*/
for ( ld = pfl - > fcurves . first ; ld ; ld = ld - > next ) {
FCurve * fcu = ( FCurve * ) ld - > data ;
2014-04-26 16:22:49 +02:00
const char * bPtr , * pPtr ;
2018-06-04 09:31:30 +02:00
2011-03-13 13:22:57 +01:00
if ( fcu - > rna_path = = NULL )
continue ;
2018-06-04 09:31:30 +02:00
/* do we have a match?
2018-11-14 02:53:15 +01:00
* - bPtr is the RNA Path with the standard part chopped off
* - pPtr is the chunk of the path which is left over
2011-03-13 13:22:57 +01:00
*/
bPtr = strstr ( fcu - > rna_path , pfl - > pchan_path ) + len ;
2016-05-17 17:19:06 +02:00
pPtr = strstr ( bPtr , prop_prefix ) ;
2018-06-04 09:31:30 +02:00
2011-03-13 13:22:57 +01:00
if ( pPtr ) {
/* use RNA to try and get a handle on this property, then, assuming that it is just
* numerical , try and grab the value as a float for temp editing before setting back
2009-09-19 13:59:23 +02:00
*/
2011-03-13 13:22:57 +01:00
PropertyRNA * prop = RNA_struct_find_property ( & ptr , pPtr ) ;
2018-06-04 09:31:30 +02:00
2011-03-13 13:22:57 +01:00
if ( prop ) {
2011-03-13 13:26:53 +01:00
switch ( RNA_property_type ( prop ) ) {
2015-04-08 04:17:36 +02:00
/* continuous values that can be smoothly interpolated... */
2011-03-13 13:26:53 +01:00
case PROP_FLOAT :
{
float tval = RNA_property_float_get ( & ptr , prop ) ;
2018-10-20 00:08:12 +02:00
pose_slide_apply_val ( pso , fcu , pfl - > ob , & tval ) ;
2011-03-13 13:26:53 +01:00
RNA_property_float_set ( & ptr , prop , tval ) ;
2013-07-19 17:23:42 +02:00
break ;
2011-03-13 13:26:53 +01:00
}
case PROP_INT :
{
float tval = ( float ) RNA_property_int_get ( & ptr , prop ) ;
2018-10-20 00:08:12 +02:00
pose_slide_apply_val ( pso , fcu , pfl - > ob , & tval ) ;
2011-03-13 13:26:53 +01:00
RNA_property_int_set ( & ptr , prop , ( int ) tval ) ;
2013-07-19 17:23:42 +02:00
break ;
2011-03-13 13:26:53 +01:00
}
2018-06-04 09:31:30 +02:00
2015-04-08 04:17:36 +02:00
/* values which can only take discrete values */
case PROP_BOOLEAN :
{
float tval = ( float ) RNA_property_boolean_get ( & ptr , prop ) ;
2018-10-20 00:08:12 +02:00
pose_slide_apply_val ( pso , fcu , pfl - > ob , & tval ) ;
2015-04-08 04:17:36 +02:00
RNA_property_boolean_set ( & ptr , prop , ( int ) tval ) ; // XXX: do we need threshold clamping here?
break ;
}
case PROP_ENUM :
{
/* don't handle this case - these don't usually represent interchangeable
* set of values which should be interpolated between
*/
break ;
}
2018-06-04 09:31:30 +02:00
2011-03-13 13:26:53 +01:00
default :
/* cannot handle */
//printf("Cannot Pose Slide non-numerical property\n");
break ;
}
2009-09-20 07:05:16 +02:00
}
2009-09-19 02:18:42 +02:00
}
}
}
/* helper for apply() - perform sliding for quaternion rotations (using quat blending) */
2012-05-08 22:18:33 +02:00
static void pose_slide_apply_quat ( tPoseSlideOp * pso , tPChanFCurveLink * pfl )
2009-09-19 02:18:42 +02:00
{
2012-05-08 22:18:33 +02:00
FCurve * fcu_w = NULL , * fcu_x = NULL , * fcu_y = NULL , * fcu_z = NULL ;
bPoseChannel * pchan = pfl - > pchan ;
LinkData * ld = NULL ;
char * path = NULL ;
2009-09-20 14:54:30 +02:00
float cframe ;
2018-10-20 00:08:12 +02:00
float prevFrameF , nextFrameF ;
if ( ! pose_frame_range_from_object_get ( pso , pfl - > ob , & prevFrameF , & nextFrameF ) ) {
BLI_assert ( ! " Invalid pfl data " ) ;
return ;
}
2018-06-04 09:31:30 +02:00
2009-09-20 14:54:30 +02:00
/* get the path to use - this should be quaternion rotations only (needs care) */
2012-05-08 22:18:33 +02:00
path = BLI_sprintfN ( " %s.%s " , pfl - > pchan_path , " rotation_quaternion " ) ;
2018-06-04 09:31:30 +02:00
2009-09-20 14:54:30 +02:00
/* get the current frame number */
2012-05-08 22:18:33 +02:00
cframe = ( float ) pso - > cframe ;
2018-06-04 09:31:30 +02:00
2009-09-20 14:54:30 +02:00
/* using this path, find each matching F-Curve for the variables we're interested in */
2012-05-08 22:18:33 +02:00
while ( ( ld = poseAnim_mapping_getNextFCurve ( & pfl - > fcurves , ld , path ) ) ) {
FCurve * fcu = ( FCurve * ) ld - > data ;
2018-06-04 09:31:30 +02:00
2009-09-20 14:54:30 +02:00
/* assign this F-Curve to one of the relevant pointers... */
switch ( fcu - > array_index ) {
case 3 : /* z */
2012-05-08 22:18:33 +02:00
fcu_z = fcu ;
2009-09-20 14:54:30 +02:00
break ;
case 2 : /* y */
2012-05-08 22:18:33 +02:00
fcu_y = fcu ;
2009-09-20 14:54:30 +02:00
break ;
case 1 : /* x */
2012-05-08 22:18:33 +02:00
fcu_x = fcu ;
2009-09-20 14:54:30 +02:00
break ;
case 0 : /* w */
2012-05-08 22:18:33 +02:00
fcu_w = fcu ;
2009-09-20 14:54:30 +02:00
break ;
}
}
2018-06-04 09:31:30 +02:00
2009-09-20 14:54:30 +02:00
/* only if all channels exist, proceed */
if ( fcu_w & & fcu_x & & fcu_y & & fcu_z ) {
2019-03-19 07:17:34 +01:00
float quat_prev [ 4 ] , quat_prev_orig [ 4 ] ;
float quat_next [ 4 ] , quat_next_orig [ 4 ] ;
float quat_curr [ 4 ] , quat_curr_orig [ 4 ] ;
float quat_final [ 4 ] ;
copy_qt_qt ( quat_curr_orig , pchan - > quat ) ;
2018-06-04 09:31:30 +02:00
2009-09-20 14:54:30 +02:00
/* get 2 quats */
2019-03-19 07:17:34 +01:00
quat_prev_orig [ 0 ] = evaluate_fcurve ( fcu_w , prevFrameF ) ;
quat_prev_orig [ 1 ] = evaluate_fcurve ( fcu_x , prevFrameF ) ;
quat_prev_orig [ 2 ] = evaluate_fcurve ( fcu_y , prevFrameF ) ;
quat_prev_orig [ 3 ] = evaluate_fcurve ( fcu_z , prevFrameF ) ;
quat_next_orig [ 0 ] = evaluate_fcurve ( fcu_w , nextFrameF ) ;
quat_next_orig [ 1 ] = evaluate_fcurve ( fcu_x , nextFrameF ) ;
quat_next_orig [ 2 ] = evaluate_fcurve ( fcu_y , nextFrameF ) ;
quat_next_orig [ 3 ] = evaluate_fcurve ( fcu_z , nextFrameF ) ;
2018-06-04 09:31:30 +02:00
2019-03-19 07:17:34 +01:00
normalize_qt_qt ( quat_prev , quat_prev_orig ) ;
normalize_qt_qt ( quat_next , quat_next_orig ) ;
normalize_qt_qt ( quat_curr , quat_curr_orig ) ;
2018-06-04 09:31:30 +02:00
2009-09-20 14:54:30 +02:00
/* perform blending */
if ( pso - > mode = = POSESLIDE_BREAKDOWN ) {
/* just perform the interpol between quat_prev and quat_next using pso->percentage as a guide */
2019-03-19 07:17:34 +01:00
interp_qt_qtqt ( quat_final , quat_prev , quat_next , pso - > percentage ) ;
2009-09-20 14:54:30 +02:00
}
2009-12-30 00:25:46 +01:00
else if ( pso - > mode = = POSESLIDE_PUSH ) {
2019-03-19 07:17:34 +01:00
float quat_diff [ 4 ] ;
2018-06-04 09:31:30 +02:00
2009-12-30 00:25:46 +01:00
/* calculate the delta transform from the previous to the current */
2012-07-07 01:56:59 +02:00
/* TODO: investigate ways to favour one transform more? */
2019-03-19 07:17:34 +01:00
sub_qt_qtqt ( quat_diff , quat_curr , quat_prev ) ;
2018-06-04 09:31:30 +02:00
2009-12-30 00:25:46 +01:00
/* increase the original by the delta transform, by an amount determined by percentage */
2019-03-19 07:17:34 +01:00
add_qt_qtqt ( quat_final , quat_curr , quat_diff , pso - > percentage ) ;
normalize_qt ( quat_final ) ;
2009-12-30 00:25:46 +01:00
}
2009-09-20 14:54:30 +02:00
else {
2019-03-19 07:17:34 +01:00
BLI_assert ( pso - > mode = = POSESLIDE_RELAX ) ;
float quat_interp [ 4 ] , quat_final_prev [ 4 ] ;
2019-01-15 13:24:20 +01:00
/* TODO: maybe a sensitivity ctrl on top of this is needed */
int iters = ( int ) ceil ( 10.0f * pso - > percentage ) ;
2018-06-04 09:31:30 +02:00
2019-03-19 07:17:34 +01:00
copy_qt_qt ( quat_final , quat_curr ) ;
2009-09-20 14:54:30 +02:00
/* perform this blending several times until a satisfactory result is reached */
while ( iters - - > 0 ) {
/* calculate the interpolation between the endpoints */
2012-05-08 22:18:33 +02:00
interp_qt_qtqt ( quat_interp , quat_prev , quat_next , ( cframe - pso - > prevFrame ) / ( pso - > nextFrame - pso - > prevFrame ) ) ;
2018-06-04 09:31:30 +02:00
2019-03-19 07:17:34 +01:00
normalize_qt_qt ( quat_final_prev , quat_final ) ;
2018-06-04 09:31:30 +02:00
2009-12-30 00:25:46 +01:00
/* tricky interpolations - blending between original and new */
2019-03-19 07:17:34 +01:00
interp_qt_qtqt ( quat_final , quat_final_prev , quat_interp , 1.0f / 6.0f ) ;
2009-09-20 14:54:30 +02:00
}
}
2019-03-19 07:17:34 +01:00
/* Apply final to the pose bone, keeping compatible for similar keyframe positions. */
quat_to_compatible_quat ( pchan - > quat , quat_final , quat_curr_orig ) ;
2009-09-20 14:54:30 +02:00
}
2018-06-04 09:31:30 +02:00
2009-09-20 14:54:30 +02:00
/* free the path now */
MEM_freeN ( path ) ;
2009-09-19 02:18:42 +02:00
}
/* apply() - perform the pose sliding based on weighting various poses */
2010-10-16 04:40:31 +02:00
static void pose_slide_apply ( bContext * C , tPoseSlideOp * pso )
2009-09-19 02:18:42 +02:00
{
tPChanFCurveLink * pfl ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* sanitise the frame ranges */
if ( pso - > prevFrame = = pso - > nextFrame ) {
/* move out one step either side */
pso - > prevFrame - - ;
pso - > nextFrame + + ;
2018-06-04 09:31:30 +02:00
2018-10-20 00:08:12 +02:00
for ( uint ob_index = 0 ; ob_index < pso - > objects_len ; ob_index + + ) {
tPoseSlideObject * ob_data = & pso - > ob_data_array [ ob_index ] ;
if ( ! ob_data - > valid ) {
continue ;
}
/* apply NLA mapping corrections so the frame lookups work */
ob_data - > prevFrameF = BKE_nla_tweakedit_remap ( ob_data - > ob - > adt ,
2018-10-29 14:07:47 +01:00
pso - > prevFrame ,
2018-10-20 00:08:12 +02:00
NLATIME_CONVERT_UNMAP ) ;
ob_data - > nextFrameF = BKE_nla_tweakedit_remap ( ob_data - > ob - > adt ,
2018-10-29 14:07:47 +01:00
pso - > nextFrame ,
2018-10-20 00:08:12 +02:00
NLATIME_CONVERT_UNMAP ) ;
}
2009-09-19 02:18:42 +02:00
}
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* for each link, handle each set of transforms */
2012-05-08 22:18:33 +02:00
for ( pfl = pso - > pfLinks . first ; pfl ; pfl = pfl - > next ) {
2018-06-04 09:31:30 +02:00
/* valid transforms for each PoseChannel should have been noted already
2018-11-14 02:53:15 +01:00
* - sliding the pose should be a straightforward exercise for location + rotation ,
* but rotations get more complicated since we may want to use quaternion blending
* for quaternions instead . . .
2009-09-19 02:18:42 +02:00
*/
2012-05-08 22:18:33 +02:00
bPoseChannel * pchan = pfl - > pchan ;
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
if ( ELEM ( pso - > channels , PS_TFM_ALL , PS_TFM_LOC ) & & ( pchan - > flag & POSE_LOC ) ) {
2009-09-19 02:18:42 +02:00
/* calculate these for the 'location' vector, and use location curves */
pose_slide_apply_vec3 ( pso , pfl , pchan - > loc , " location " ) ;
}
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
if ( ELEM ( pso - > channels , PS_TFM_ALL , PS_TFM_SIZE ) & & ( pchan - > flag & POSE_SIZE ) ) {
2009-09-19 02:18:42 +02:00
/* calculate these for the 'scale' vector, and use scale curves */
pose_slide_apply_vec3 ( pso , pfl , pchan - > size , " scale " ) ;
}
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
if ( ELEM ( pso - > channels , PS_TFM_ALL , PS_TFM_ROT ) & & ( pchan - > flag & POSE_ROT ) ) {
2009-09-19 02:18:42 +02:00
/* everything depends on the rotation mode */
if ( pchan - > rotmode > 0 ) {
/* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */
2009-09-28 12:19:20 +02:00
pose_slide_apply_vec3 ( pso , pfl , pchan - > eul , " rotation_euler " ) ;
2009-09-19 02:18:42 +02:00
}
2009-09-28 12:19:20 +02:00
else if ( pchan - > rotmode = = ROT_MODE_AXISANGLE ) {
2012-10-20 22:20:02 +02:00
/* TODO: need to figure out how to do this! */
2009-09-19 02:18:42 +02:00
}
else {
/* quaternions - use quaternion blending */
pose_slide_apply_quat ( pso , pfl ) ;
}
}
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
if ( ELEM ( pso - > channels , PS_TFM_ALL , PS_TFM_BBONE_SHAPE ) & & ( pchan - > flag & POSE_BBONE_SHAPE ) ) {
2016-05-17 17:19:06 +02:00
/* bbone properties - they all start a "bbone_" prefix */
2018-06-04 09:31:30 +02:00
pose_slide_apply_props ( pso , pfl , " bbone_ " ) ;
2016-05-17 17:19:06 +02:00
}
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
if ( ELEM ( pso - > channels , PS_TFM_ALL , PS_TFM_PROPS ) & & ( pfl - > oldprops ) ) {
2016-05-17 17:19:06 +02:00
/* not strictly a transform, but custom properties contribute to the pose produced in many rigs
* ( e . g . the facial rigs used in Sintel )
*/
pose_slide_apply_props ( pso , pfl , " [ \" " ) ; /* dummy " for texteditor bugs */
2011-03-13 13:22:57 +01:00
}
2009-09-19 02:18:42 +02:00
}
2018-06-04 09:31:30 +02:00
2009-09-20 07:05:16 +02:00
/* depsgraph updates + redraws */
pose_slide_refresh ( C , pso ) ;
}
2019-04-10 08:40:49 +02:00
/* perform auto-key-framing after changes were made + confirmed */
2012-05-08 22:18:33 +02:00
static void pose_slide_autoKeyframe ( bContext * C , tPoseSlideOp * pso )
2009-09-20 07:05:16 +02:00
{
2010-02-19 12:42:21 +01:00
/* wrapper around the generic call */
2018-10-20 00:08:12 +02:00
poseAnim_mapping_autoKeyframe ( C , pso - > scene , & pso - > pfLinks , ( float ) pso - > cframe ) ;
2009-09-20 07:05:16 +02:00
}
/* reset changes made to current pose */
2012-05-08 22:18:33 +02:00
static void pose_slide_reset ( tPoseSlideOp * pso )
2009-09-20 07:05:16 +02:00
{
2010-02-19 12:42:21 +01:00
/* wrapper around the generic call, so that custom stuff can be added later */
poseAnim_mapping_reset ( & pso - > pfLinks ) ;
2009-09-19 02:18:42 +02:00
}
/* ------------------------------------ */
2011-08-10 02:46:20 +02:00
/* draw percentage indicator in header */
2017-03-10 23:52:58 +01:00
// TODO: Include hints about locks here...
2018-06-28 12:06:00 +02:00
static void pose_slide_draw_status ( tPoseSlideOp * pso )
2011-08-10 02:46:20 +02:00
{
2016-05-14 10:00:52 +02:00
char status_str [ UI_MAX_DRAW_STR ] ;
2017-03-10 23:52:58 +01:00
char limits_str [ UI_MAX_DRAW_STR ] ;
char axis_str [ 50 ] ;
2012-01-11 10:33:44 +01:00
char mode_str [ 32 ] ;
2018-06-04 09:31:30 +02:00
2011-08-10 02:46:20 +02:00
switch ( pso - > mode ) {
case POSESLIDE_PUSH :
2012-01-11 10:33:44 +01:00
strcpy ( mode_str , " Push Pose " ) ;
2011-08-10 02:46:20 +02:00
break ;
case POSESLIDE_RELAX :
2012-01-11 10:33:44 +01:00
strcpy ( mode_str , " Relax Pose " ) ;
2011-08-10 02:46:20 +02:00
break ;
case POSESLIDE_BREAKDOWN :
2012-01-11 10:33:44 +01:00
strcpy ( mode_str , " Breakdown " ) ;
2011-08-10 02:46:20 +02:00
break ;
2018-06-04 09:31:30 +02:00
2011-08-10 02:46:20 +02:00
default :
2012-10-20 22:20:02 +02:00
/* unknown */
2012-01-11 10:33:44 +01:00
strcpy ( mode_str , " Sliding-Tool " ) ;
2011-08-10 02:46:20 +02:00
break ;
}
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
switch ( pso - > axislock ) {
case PS_LOCK_X :
BLI_strncpy ( axis_str , " [X]/Y/Z axis only (X to clear) " , sizeof ( axis_str ) ) ;
break ;
case PS_LOCK_Y :
BLI_strncpy ( axis_str , " X/[Y]/Z axis only (Y to clear) " , sizeof ( axis_str ) ) ;
break ;
case PS_LOCK_Z :
BLI_strncpy ( axis_str , " X/Y/[Z] axis only (Z to clear) " , sizeof ( axis_str ) ) ;
break ;
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
default :
if ( ELEM ( pso - > channels , PS_TFM_LOC , PS_TFM_ROT , PS_TFM_SIZE ) ) {
BLI_strncpy ( axis_str , " X/Y/Z = Axis Constraint " , sizeof ( axis_str ) ) ;
}
else {
axis_str [ 0 ] = ' \0 ' ;
}
break ;
}
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
switch ( pso - > channels ) {
case PS_TFM_LOC :
BLI_snprintf ( limits_str , sizeof ( limits_str ) , " [G]/R/S/B/C - Location only (G to clear) | %s " , axis_str ) ;
break ;
case PS_TFM_ROT :
BLI_snprintf ( limits_str , sizeof ( limits_str ) , " G/[R]/S/B/C - Rotation only (R to clear) | %s " , axis_str ) ;
break ;
case PS_TFM_SIZE :
BLI_snprintf ( limits_str , sizeof ( limits_str ) , " G/R/[S]/B/C - Scale only (S to clear) | %s " , axis_str ) ;
break ;
case PS_TFM_BBONE_SHAPE :
BLI_strncpy ( limits_str , " G/R/S/[B]/C - Bendy Bone properties only (B to clear) | %s " , sizeof ( limits_str ) ) ;
break ;
case PS_TFM_PROPS :
BLI_strncpy ( limits_str , " G/R/S/B/[C] - Custom Properties only (C to clear) | %s " , sizeof ( limits_str ) ) ;
break ;
default :
BLI_strncpy ( limits_str , " G/R/S/B/C - Limit to Transform/Property Set " , sizeof ( limits_str ) ) ;
break ;
}
2018-06-04 09:31:30 +02:00
2015-05-17 09:45:59 +02:00
if ( hasNumInput ( & pso - > num ) ) {
Scene * scene = pso - > scene ;
char str_offs [ NUM_STR_REP_LEN ] ;
2018-06-04 09:31:30 +02:00
2015-05-17 09:45:59 +02:00
outputNumInput ( & pso - > num , str_offs , & scene - > unit ) ;
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
BLI_snprintf ( status_str , sizeof ( status_str ) , " %s: %s | %s " , mode_str , str_offs , limits_str ) ;
2015-05-17 09:45:59 +02:00
}
else {
2017-03-10 23:52:58 +01:00
BLI_snprintf ( status_str , sizeof ( status_str ) , " %s: %d %% | %s " , mode_str , ( int ) ( pso - > percentage * 100.0f ) , limits_str ) ;
2015-05-17 09:45:59 +02:00
}
2018-06-04 09:31:30 +02:00
2018-06-28 12:06:00 +02:00
ED_area_status_text ( pso - > sa , status_str ) ;
2011-08-10 02:46:20 +02:00
}
2009-09-19 02:18:42 +02:00
/* common code for invoke() methods */
2012-05-08 22:18:33 +02:00
static int pose_slide_invoke_common ( bContext * C , wmOperator * op , tPoseSlideOp * pso )
2009-09-19 02:18:42 +02:00
{
tPChanFCurveLink * pfl ;
2012-05-08 22:18:33 +02:00
wmWindow * win = CTX_wm_window ( C ) ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* for each link, add all its keyframes to the search tree */
2012-05-08 22:18:33 +02:00
for ( pfl = pso - > pfLinks . first ; pfl ; pfl = pfl - > next ) {
2009-09-19 02:18:42 +02:00
LinkData * ld ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* do this for each F-Curve */
2012-05-08 22:18:33 +02:00
for ( ld = pfl - > fcurves . first ; ld ; ld = ld - > next ) {
FCurve * fcu = ( FCurve * ) ld - > data ;
Dope Sheet: new option to display keyframe interpolation mode and extremes.
With the new automatic handle algorithm, it is possible to do a lot
of the animation via keyframes without touching the curves. It is
however necessary to change the keyframe interpolation and handle
types in certain cases. Currently the dopesheet/action editor
allows changing the types, but does not show them in any way.
To fix, add a new menu option to display this information. For handle
type, it is represented using the shape of the key icons: diamond for
Free, clipped diamond for Aligned, square for Vector, circle for Auto
Clamp, and cirle with dot for Automatic.
Non-bezier interpolation is a property of intervals between keys,
so it is marked by drawing lines, similar to holds. In this initial
version, only the fact of non-bezier interpolation is displayed,
without distinguishing types. For summaries, the line is drawn at
half alpha if not all curves in the group are non-bezier.
In addition, it is sometimes helpful to know the general direction
of change of the curve, and which keys are extremes. This commit
also adds an option to highlight extremes, based on comparing the
keyed values with adjacent keys. Half-intensity display is used
for overshot bezier extremes, or non-uniform summaries.
Reviewers: brecht, aligorith, billreynish
Differential Revision: https://developer.blender.org/D3788
2018-10-19 17:55:19 +02:00
fcurve_to_keylist ( pfl - > ob - > adt , fcu , & pso - > keys , 0 ) ;
2009-09-19 02:18:42 +02:00
}
}
2018-06-04 09:31:30 +02:00
2012-05-08 22:18:33 +02:00
/* cancel if no keyframes found... */
2009-09-19 02:18:42 +02:00
if ( pso - > keys . root ) {
ActKeyColumn * ak ;
2012-05-08 22:18:33 +02:00
float cframe = ( float ) pso - > cframe ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* firstly, check if the current frame is a keyframe... */
2012-05-08 22:18:33 +02:00
ak = ( ActKeyColumn * ) BLI_dlrbTree_search_exact ( & pso - > keys , compare_ak_cfraPtr , & cframe ) ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
if ( ak = = NULL ) {
/* current frame is not a keyframe, so search */
2012-05-08 22:18:33 +02:00
ActKeyColumn * pk = ( ActKeyColumn * ) BLI_dlrbTree_search_prev ( & pso - > keys , compare_ak_cfraPtr , & cframe ) ;
ActKeyColumn * nk = ( ActKeyColumn * ) BLI_dlrbTree_search_next ( & pso - > keys , compare_ak_cfraPtr , & cframe ) ;
2018-06-04 09:31:30 +02:00
2009-09-19 13:59:23 +02:00
/* new set the frames */
2012-05-08 22:18:33 +02:00
/* prev frame */
pso - > prevFrame = ( pk ) ? ( pk - > cfra ) : ( pso - > cframe - 1 ) ;
2009-09-19 02:18:42 +02:00
RNA_int_set ( op - > ptr , " prev_frame " , pso - > prevFrame ) ;
2012-05-08 22:18:33 +02:00
/* next frame */
pso - > nextFrame = ( nk ) ? ( nk - > cfra ) : ( pso - > cframe + 1 ) ;
2009-09-19 13:59:23 +02:00
RNA_int_set ( op - > ptr , " next_frame " , pso - > nextFrame ) ;
2009-09-19 02:18:42 +02:00
}
else {
/* current frame itself is a keyframe, so just take keyframes on either side */
2012-05-08 22:18:33 +02:00
/* prev frame */
pso - > prevFrame = ( ak - > prev ) ? ( ak - > prev - > cfra ) : ( pso - > cframe - 1 ) ;
2009-09-19 02:18:42 +02:00
RNA_int_set ( op - > ptr , " prev_frame " , pso - > prevFrame ) ;
2012-05-08 22:18:33 +02:00
/* next frame */
pso - > nextFrame = ( ak - > next ) ? ( ak - > next - > cfra ) : ( pso - > cframe + 1 ) ;
2009-09-19 02:18:42 +02:00
RNA_int_set ( op - > ptr , " next_frame " , pso - > nextFrame ) ;
}
2018-06-04 09:31:30 +02:00
2017-09-21 07:38:30 +02:00
/* apply NLA mapping corrections so the frame lookups work */
2018-10-20 00:08:12 +02:00
for ( uint ob_index = 0 ; ob_index < pso - > objects_len ; ob_index + + ) {
tPoseSlideObject * ob_data = & pso - > ob_data_array [ ob_index ] ;
if ( ob_data - > valid ) {
ob_data - > prevFrameF = BKE_nla_tweakedit_remap ( ob_data - > ob - > adt ,
2018-10-29 13:57:17 +01:00
pso - > prevFrame ,
2018-10-20 00:08:12 +02:00
NLATIME_CONVERT_UNMAP ) ;
ob_data - > nextFrameF = BKE_nla_tweakedit_remap ( ob_data - > ob - > adt ,
2018-10-29 13:57:17 +01:00
pso - > nextFrame ,
2018-10-20 00:08:12 +02:00
NLATIME_CONVERT_UNMAP ) ;
}
}
2009-09-19 02:18:42 +02:00
}
else {
2011-09-19 14:26:20 +02:00
BKE_report ( op - > reports , RPT_ERROR , " No keyframes to slide between " ) ;
2011-03-13 13:22:57 +01:00
pose_slide_exit ( op ) ;
2009-09-19 02:18:42 +02:00
return OPERATOR_CANCELLED ;
}
2018-06-04 09:31:30 +02:00
2009-09-20 07:05:16 +02:00
/* initial apply for operator... */
2012-10-20 22:20:02 +02:00
/* TODO: need to calculate percentage for initial round too... */
2010-10-16 04:40:31 +02:00
pose_slide_apply ( C , pso ) ;
2018-06-04 09:31:30 +02:00
2009-09-20 07:05:16 +02:00
/* depsgraph updates + redraws */
pose_slide_refresh ( C , pso ) ;
2018-06-04 09:31:30 +02:00
2009-09-20 07:05:16 +02:00
/* set cursor to indicate modal */
2013-09-07 00:34:29 +02:00
WM_cursor_modal_set ( win , BC_EW_SCROLLCURSOR ) ;
2018-06-04 09:31:30 +02:00
2011-08-10 02:46:20 +02:00
/* header print */
2018-06-28 12:06:00 +02:00
pose_slide_draw_status ( pso ) ;
2018-06-04 09:31:30 +02:00
2009-09-20 07:05:16 +02:00
/* add a modal handler for this operator */
WM_event_add_modal_handler ( C , op ) ;
return OPERATOR_RUNNING_MODAL ;
2009-09-19 13:59:23 +02:00
}
2016-01-22 01:55:19 +01:00
/* calculate percentage based on position of mouse (we only use x-axis for now.
* since this is more convenient for users to do ) , and store new percentage value
*/
static void pose_slide_mouse_update_percentage ( tPoseSlideOp * pso , wmOperator * op , const wmEvent * event )
{
pso - > percentage = ( event - > x - pso - > ar - > winrct . xmin ) / ( ( float ) pso - > ar - > winx ) ;
RNA_float_set ( op - > ptr , " percentage " , pso - > percentage ) ;
}
2017-03-10 23:52:58 +01:00
/* handle an event to toggle channels mode */
static void pose_slide_toggle_channels_mode ( wmOperator * op , tPoseSlideOp * pso , ePoseSlide_Channels channel )
{
/* Turn channel on or off? */
if ( pso - > channels = = channel ) {
/* Already limiting to transform only, so pressing this again turns it off */
pso - > channels = PS_TFM_ALL ;
}
else {
/* Only this set of channels */
pso - > channels = channel ;
}
RNA_enum_set ( op - > ptr , " channels " , pso - > channels ) ;
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
/* Reset axis limits too for good measure */
pso - > axislock = 0 ;
RNA_enum_set ( op - > ptr , " axis_lock " , pso - > axislock ) ;
}
/* handle an event to toggle axis locks - returns whether any change in state is needed */
static bool pose_slide_toggle_axis_locks ( wmOperator * op , tPoseSlideOp * pso , ePoseSlide_AxisLock axis )
{
/* Axis can only be set when a transform is set - it doesn't make sense otherwise */
if ( ELEM ( pso - > channels , PS_TFM_ALL , PS_TFM_BBONE_SHAPE , PS_TFM_PROPS ) ) {
pso - > axislock = 0 ;
RNA_enum_set ( op - > ptr , " axis_lock " , pso - > axislock ) ;
return false ;
}
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
/* Turn on or off? */
if ( pso - > axislock = = axis ) {
/* Already limiting on this axis, so turn off */
pso - > axislock = 0 ;
}
else {
/* Only this axis */
pso - > axislock = axis ;
}
RNA_enum_set ( op - > ptr , " axis_lock " , pso - > axislock ) ;
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
/* Setting changed, so pose update is needed */
return true ;
}
2009-09-19 13:59:23 +02:00
/* common code for modal() */
2013-03-13 10:03:46 +01:00
static int pose_slide_modal ( bContext * C , wmOperator * op , const wmEvent * event )
2009-09-19 13:59:23 +02:00
{
2012-05-08 22:18:33 +02:00
tPoseSlideOp * pso = op - > customdata ;
wmWindow * win = CTX_wm_window ( C ) ;
2017-03-10 23:52:58 +01:00
bool do_pose_update = false ;
2018-06-04 09:31:30 +02:00
2015-05-17 09:45:59 +02:00
const bool has_numinput = hasNumInput ( & pso - > num ) ;
2018-06-04 09:31:30 +02:00
2013-03-13 10:03:46 +01:00
switch ( event - > type ) {
2012-05-08 22:18:33 +02:00
case LEFTMOUSE : /* confirm */
2012-10-08 06:42:06 +02:00
case RETKEY :
2016-11-10 17:18:57 +01:00
case PADENTER :
2009-09-20 07:05:16 +02:00
{
2011-08-10 02:46:20 +02:00
/* return to normal cursor and header status */
2018-06-28 12:06:00 +02:00
ED_area_status_text ( pso - > sa , NULL ) ;
2013-09-07 00:34:29 +02:00
WM_cursor_modal_restore ( win ) ;
2018-06-04 09:31:30 +02:00
2009-09-20 07:05:16 +02:00
/* insert keyframes as required... */
pose_slide_autoKeyframe ( C , pso ) ;
2010-10-16 04:40:31 +02:00
pose_slide_exit ( op ) ;
2018-06-04 09:31:30 +02:00
2009-09-20 07:05:16 +02:00
/* done! */
2009-09-19 13:59:23 +02:00
return OPERATOR_FINISHED ;
2009-09-20 07:05:16 +02:00
}
2018-06-04 09:31:30 +02:00
2012-05-08 22:18:33 +02:00
case ESCKEY : /* cancel */
2018-06-04 09:31:30 +02:00
case RIGHTMOUSE :
2009-09-20 07:05:16 +02:00
{
2011-08-10 02:46:20 +02:00
/* return to normal cursor and header status */
2018-06-28 12:06:00 +02:00
ED_area_status_text ( pso - > sa , NULL ) ;
2013-09-07 00:34:29 +02:00
WM_cursor_modal_restore ( win ) ;
2018-06-04 09:31:30 +02:00
2009-09-20 07:05:16 +02:00
/* reset transforms back to original state */
2010-10-16 04:40:31 +02:00
pose_slide_reset ( pso ) ;
2018-06-04 09:31:30 +02:00
2009-09-20 07:05:16 +02:00
/* depsgraph updates + redraws */
pose_slide_refresh ( C , pso ) ;
2018-06-04 09:31:30 +02:00
2009-09-20 07:05:16 +02:00
/* clean up temp data */
2010-10-16 04:40:31 +02:00
pose_slide_exit ( op ) ;
2018-06-04 09:31:30 +02:00
2012-03-08 05:12:11 +01:00
/* canceled! */
2009-09-19 13:59:23 +02:00
return OPERATOR_CANCELLED ;
2009-09-20 07:05:16 +02:00
}
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
/* Percentage Chane... */
2009-09-19 13:59:23 +02:00
case MOUSEMOVE : /* calculate new position */
{
2015-05-17 09:45:59 +02:00
/* only handle mousemove if not doing numinput */
if ( has_numinput = = false ) {
2016-01-22 01:55:19 +01:00
/* update percentage based on position of mouse */
pose_slide_mouse_update_percentage ( pso , op , event ) ;
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
/* update pose to reflect the new values (see below) */
do_pose_update = true ;
2015-05-17 09:45:59 +02:00
}
2013-07-19 17:23:42 +02:00
break ;
2009-09-19 13:59:23 +02:00
}
2015-05-17 09:45:59 +02:00
default :
2017-05-19 14:18:54 +02:00
{
2015-05-17 09:45:59 +02:00
if ( ( event - > val = = KM_PRESS ) & & handleNumInput ( C , & pso - > num , event ) ) {
float value ;
2018-06-04 09:31:30 +02:00
/* Grab percentage from numeric input, and store this new value for redo
2015-05-17 09:45:59 +02:00
* NOTE : users see ints , while internally we use a 0 - 1 float
*/
value = pso - > percentage * 100.0f ;
applyNumInput ( & pso - > num , & value ) ;
2018-06-04 09:31:30 +02:00
2015-05-17 09:45:59 +02:00
pso - > percentage = value / 100.0f ;
CLAMP ( pso - > percentage , 0.0f , 1.0f ) ;
RNA_float_set ( op - > ptr , " percentage " , pso - > percentage ) ;
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
/* Update pose to reflect the new values (see below) */
do_pose_update = true ;
2015-05-17 09:45:59 +02:00
break ;
}
2017-03-10 23:52:58 +01:00
else if ( event - > val = = KM_PRESS ) {
switch ( event - > type ) {
/* Transform Channel Limits */
/* XXX: Replace these hardcoded hotkeys with a modalmap that can be customised */
case GKEY : /* Location */
{
pose_slide_toggle_channels_mode ( op , pso , PS_TFM_LOC ) ;
do_pose_update = true ;
break ;
}
case RKEY : /* Rotation */
{
pose_slide_toggle_channels_mode ( op , pso , PS_TFM_ROT ) ;
do_pose_update = true ;
break ;
}
case SKEY : /* Scale */
{
pose_slide_toggle_channels_mode ( op , pso , PS_TFM_SIZE ) ;
do_pose_update = true ;
break ;
}
case BKEY : /* Bendy Bones */
{
pose_slide_toggle_channels_mode ( op , pso , PS_TFM_BBONE_SHAPE ) ;
do_pose_update = true ;
break ;
}
case CKEY : /* Custom Properties */
{
pose_slide_toggle_channels_mode ( op , pso , PS_TFM_PROPS ) ;
do_pose_update = true ;
break ;
}
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
/* Axis Locks */
/* XXX: Hardcoded... */
case XKEY :
{
if ( pose_slide_toggle_axis_locks ( op , pso , PS_LOCK_X ) ) {
do_pose_update = true ;
}
break ;
}
case YKEY :
{
if ( pose_slide_toggle_axis_locks ( op , pso , PS_LOCK_Y ) ) {
do_pose_update = true ;
}
break ;
}
case ZKEY :
{
if ( pose_slide_toggle_axis_locks ( op , pso , PS_LOCK_Z ) ) {
do_pose_update = true ;
}
break ;
}
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
default : /* Some other unhandled key... */
break ;
}
}
2015-05-17 09:45:59 +02:00
else {
/* unhandled event - maybe it was some view manip? */
/* allow to pass through */
return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH ;
}
2017-05-19 14:18:54 +02:00
}
2009-09-19 13:59:23 +02:00
}
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
/* perform pose updates - in response to some user action (e.g. pressing a key or moving the mouse) */
if ( do_pose_update ) {
/* update percentage indicator in header */
2018-06-28 12:06:00 +02:00
pose_slide_draw_status ( pso ) ;
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
/* reset transforms (to avoid accumulation errors) */
pose_slide_reset ( pso ) ;
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
/* apply... */
pose_slide_apply ( C , pso ) ;
}
2018-06-04 09:31:30 +02:00
2009-09-19 13:59:23 +02:00
/* still running... */
return OPERATOR_RUNNING_MODAL ;
2009-09-19 02:18:42 +02:00
}
2009-09-19 13:59:23 +02:00
/* common code for cancel() */
2013-10-31 00:08:53 +01:00
static void pose_slide_cancel ( bContext * UNUSED ( C ) , wmOperator * op )
2009-09-19 13:59:23 +02:00
{
/* cleanup and done */
2010-10-16 04:40:31 +02:00
pose_slide_exit ( op ) ;
2009-09-19 13:59:23 +02:00
}
2009-09-19 02:18:42 +02:00
/* common code for exec() methods */
2012-05-08 22:18:33 +02:00
static int pose_slide_exec_common ( bContext * C , wmOperator * op , tPoseSlideOp * pso )
2009-09-19 02:18:42 +02:00
{
/* settings should have been set up ok for applying, so just apply! */
2010-10-16 04:40:31 +02:00
pose_slide_apply ( C , pso ) ;
2018-06-04 09:31:30 +02:00
2009-09-20 07:05:16 +02:00
/* insert keyframes if needed */
pose_slide_autoKeyframe ( C , pso ) ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* cleanup and done */
2010-10-16 04:40:31 +02:00
pose_slide_exit ( op ) ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
return OPERATOR_FINISHED ;
}
/* common code for defining RNA properties */
2017-03-10 23:52:58 +01:00
/* TODO: Skip save on these? */
2012-05-08 22:18:33 +02:00
static void pose_slide_opdef_properties ( wmOperatorType * ot )
2009-09-19 02:18:42 +02:00
{
2017-03-10 23:52:58 +01:00
RNA_def_float_percentage ( ot - > srna , " percentage " , 0.5f , 0.0f , 1.0f , " Percentage " , " Weighting factor for which keyframe is favored more " , 0.3 , 0.7 ) ;
2018-06-04 09:31:30 +02:00
2011-09-19 14:26:20 +02:00
RNA_def_int ( ot - > srna , " prev_frame " , 0 , MINAFRAME , MAXFRAME , " Previous Keyframe " , " Frame number of keyframe immediately before the current frame " , 0 , 50 ) ;
RNA_def_int ( ot - > srna , " next_frame " , 0 , MINAFRAME , MAXFRAME , " Next Keyframe " , " Frame number of keyframe immediately after the current frame " , 0 , 50 ) ;
2018-06-04 09:31:30 +02:00
2017-03-10 23:52:58 +01:00
RNA_def_enum ( ot - > srna , " channels " , prop_channels_types , PS_TFM_ALL , " Channels " , " Set of properties that are affected " ) ;
RNA_def_enum ( ot - > srna , " axis_lock " , prop_axis_lock_types , 0 , " Axis Lock " , " Transform axis to restrict effects to " ) ;
2009-09-19 02:18:42 +02:00
}
/* ------------------------------------ */
/* invoke() - for 'push' mode */
2016-01-22 01:55:19 +01:00
static int pose_slide_push_invoke ( bContext * C , wmOperator * op , const wmEvent * event )
2009-09-19 02:18:42 +02:00
{
tPoseSlideOp * pso ;
2018-06-04 09:31:30 +02:00
2012-03-02 17:05:54 +01:00
/* initialize data */
2009-09-19 02:18:42 +02:00
if ( pose_slide_init ( C , op , POSESLIDE_PUSH ) = = 0 ) {
2010-10-16 04:40:31 +02:00
pose_slide_exit ( op ) ;
2009-09-19 02:18:42 +02:00
return OPERATOR_CANCELLED ;
}
else
2012-05-08 22:18:33 +02:00
pso = op - > customdata ;
2018-06-04 09:31:30 +02:00
2016-01-22 01:55:19 +01:00
/* initialise percentage so that it won't pop on first mouse move */
pose_slide_mouse_update_percentage ( pso , op , event ) ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* do common setup work */
return pose_slide_invoke_common ( C , op , pso ) ;
}
/* exec() - for push */
2012-05-08 22:18:33 +02:00
static int pose_slide_push_exec ( bContext * C , wmOperator * op )
2009-09-19 02:18:42 +02:00
{
tPoseSlideOp * pso ;
2018-06-04 09:31:30 +02:00
2012-03-02 17:05:54 +01:00
/* initialize data (from RNA-props) */
2009-09-19 02:18:42 +02:00
if ( pose_slide_init ( C , op , POSESLIDE_PUSH ) = = 0 ) {
2010-10-16 04:40:31 +02:00
pose_slide_exit ( op ) ;
2009-09-19 02:18:42 +02:00
return OPERATOR_CANCELLED ;
}
else
2012-05-08 22:18:33 +02:00
pso = op - > customdata ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* do common exec work */
return pose_slide_exec_common ( C , op , pso ) ;
}
2012-04-29 19:11:40 +02:00
void POSE_OT_push ( wmOperatorType * ot )
2009-09-19 02:18:42 +02:00
{
/* identifiers */
2012-03-22 08:26:09 +01:00
ot - > name = " Push Pose " ;
ot - > idname = " POSE_OT_push " ;
ot - > description = " Exaggerate the current pose " ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* callbacks */
2012-03-22 08:26:09 +01:00
ot - > exec = pose_slide_push_exec ;
ot - > invoke = pose_slide_push_invoke ;
ot - > modal = pose_slide_modal ;
ot - > cancel = pose_slide_cancel ;
ot - > poll = ED_operator_posemode ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* flags */
2012-05-08 22:18:33 +02:00
ot - > flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* Properties */
pose_slide_opdef_properties ( ot ) ;
}
/* ........................ */
/* invoke() - for 'relax' mode */
2016-01-22 01:55:19 +01:00
static int pose_slide_relax_invoke ( bContext * C , wmOperator * op , const wmEvent * event )
2009-09-19 02:18:42 +02:00
{
tPoseSlideOp * pso ;
2018-06-04 09:31:30 +02:00
2012-03-02 17:05:54 +01:00
/* initialize data */
2009-09-19 02:18:42 +02:00
if ( pose_slide_init ( C , op , POSESLIDE_RELAX ) = = 0 ) {
2010-10-16 04:40:31 +02:00
pose_slide_exit ( op ) ;
2009-09-19 02:18:42 +02:00
return OPERATOR_CANCELLED ;
}
else
2012-05-08 22:18:33 +02:00
pso = op - > customdata ;
2018-06-04 09:31:30 +02:00
2016-01-22 01:55:19 +01:00
/* initialise percentage so that it won't pop on first mouse move */
pose_slide_mouse_update_percentage ( pso , op , event ) ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* do common setup work */
return pose_slide_invoke_common ( C , op , pso ) ;
}
/* exec() - for relax */
2012-05-08 22:18:33 +02:00
static int pose_slide_relax_exec ( bContext * C , wmOperator * op )
2009-09-19 02:18:42 +02:00
{
tPoseSlideOp * pso ;
2018-06-04 09:31:30 +02:00
2012-03-02 17:05:54 +01:00
/* initialize data (from RNA-props) */
2009-09-19 02:18:42 +02:00
if ( pose_slide_init ( C , op , POSESLIDE_RELAX ) = = 0 ) {
2010-10-16 04:40:31 +02:00
pose_slide_exit ( op ) ;
2009-09-19 02:18:42 +02:00
return OPERATOR_CANCELLED ;
}
else
2012-05-08 22:18:33 +02:00
pso = op - > customdata ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* do common exec work */
return pose_slide_exec_common ( C , op , pso ) ;
}
2012-04-29 19:11:40 +02:00
void POSE_OT_relax ( wmOperatorType * ot )
2009-09-19 02:18:42 +02:00
{
/* identifiers */
2012-03-22 08:26:09 +01:00
ot - > name = " Relax Pose " ;
ot - > idname = " POSE_OT_relax " ;
ot - > description = " Make the current pose more similar to its surrounding ones " ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* callbacks */
2012-03-22 08:26:09 +01:00
ot - > exec = pose_slide_relax_exec ;
ot - > invoke = pose_slide_relax_invoke ;
ot - > modal = pose_slide_modal ;
ot - > cancel = pose_slide_cancel ;
ot - > poll = ED_operator_posemode ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* flags */
2012-05-08 22:18:33 +02:00
ot - > flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* Properties */
pose_slide_opdef_properties ( ot ) ;
}
/* ........................ */
/* invoke() - for 'breakdown' mode */
2016-01-22 01:55:19 +01:00
static int pose_slide_breakdown_invoke ( bContext * C , wmOperator * op , const wmEvent * event )
2009-09-19 02:18:42 +02:00
{
tPoseSlideOp * pso ;
2018-06-04 09:31:30 +02:00
2012-03-02 17:05:54 +01:00
/* initialize data */
2009-09-19 02:18:42 +02:00
if ( pose_slide_init ( C , op , POSESLIDE_BREAKDOWN ) = = 0 ) {
2010-10-16 04:40:31 +02:00
pose_slide_exit ( op ) ;
2009-09-19 02:18:42 +02:00
return OPERATOR_CANCELLED ;
}
else
2012-05-08 22:18:33 +02:00
pso = op - > customdata ;
2018-06-04 09:31:30 +02:00
2016-01-22 01:55:19 +01:00
/* initialise percentage so that it won't pop on first mouse move */
pose_slide_mouse_update_percentage ( pso , op , event ) ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* do common setup work */
return pose_slide_invoke_common ( C , op , pso ) ;
}
/* exec() - for breakdown */
2012-05-08 22:18:33 +02:00
static int pose_slide_breakdown_exec ( bContext * C , wmOperator * op )
2009-09-19 02:18:42 +02:00
{
tPoseSlideOp * pso ;
2018-06-04 09:31:30 +02:00
2012-03-02 17:05:54 +01:00
/* initialize data (from RNA-props) */
2009-09-19 02:18:42 +02:00
if ( pose_slide_init ( C , op , POSESLIDE_BREAKDOWN ) = = 0 ) {
2010-10-16 04:40:31 +02:00
pose_slide_exit ( op ) ;
2009-09-19 02:18:42 +02:00
return OPERATOR_CANCELLED ;
}
else
2012-05-08 22:18:33 +02:00
pso = op - > customdata ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* do common exec work */
return pose_slide_exec_common ( C , op , pso ) ;
}
2012-04-29 19:11:40 +02:00
void POSE_OT_breakdown ( wmOperatorType * ot )
2009-09-19 02:18:42 +02:00
{
/* identifiers */
2012-03-22 08:26:09 +01:00
ot - > name = " Pose Breakdowner " ;
ot - > idname = " POSE_OT_breakdown " ;
ot - > description = " Create a suitable breakdown pose on the current frame " ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* callbacks */
2012-03-22 08:26:09 +01:00
ot - > exec = pose_slide_breakdown_exec ;
ot - > invoke = pose_slide_breakdown_invoke ;
ot - > modal = pose_slide_modal ;
ot - > cancel = pose_slide_cancel ;
ot - > poll = ED_operator_posemode ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* flags */
2012-05-08 22:18:33 +02:00
ot - > flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING ;
2018-06-04 09:31:30 +02:00
2009-09-19 02:18:42 +02:00
/* Properties */
pose_slide_opdef_properties ( ot ) ;
}
/* **************************************************** */
2011-03-24 04:02:34 +01:00
/* B) Pose Propagate */
/* "termination conditions" - i.e. when we stop */
typedef enum ePosePropagate_Termination {
2012-05-08 22:18:33 +02:00
/* stop after the current hold ends */
2011-03-31 02:45:52 +02:00
POSE_PROPAGATE_SMART_HOLDS = 0 ,
2012-05-08 22:18:33 +02:00
/* only do on the last keyframe */
2011-03-31 02:45:52 +02:00
POSE_PROPAGATE_LAST_KEY ,
2012-05-08 22:18:33 +02:00
/* stop after the next keyframe */
2011-03-24 04:02:34 +01:00
POSE_PROPAGATE_NEXT_KEY ,
2012-05-08 22:18:33 +02:00
/* stop after the specified frame */
2011-03-24 04:02:34 +01:00
POSE_PROPAGATE_BEFORE_FRAME ,
2012-05-08 22:18:33 +02:00
/* stop when we run out of keyframes */
2011-03-31 03:37:42 +02:00
POSE_PROPAGATE_BEFORE_END ,
2018-06-04 09:31:30 +02:00
2015-04-02 12:30:30 +02:00
/* only do on keyframes that are selected */
POSE_PROPAGATE_SELECTED_KEYS ,
2012-05-08 22:18:33 +02:00
/* only do on the frames where markers are selected */
2019-04-16 16:40:47 +02:00
POSE_PROPAGATE_SELECTED_MARKERS ,
2011-03-24 04:02:34 +01:00
} ePosePropagate_Termination ;
2011-03-31 03:37:42 +02:00
/* termination data needed for some modes - assumes only one of these entries will be needed at a time */
typedef union tPosePropagate_ModeData {
/* smart holds + before frame: frame number to stop on */
float end_frame ;
2018-06-04 09:31:30 +02:00
2011-03-31 03:37:42 +02:00
/* selected markers: listbase for CfraElem's marking these frames */
ListBase sel_markers ;
} tPosePropagate_ModeData ;
2011-03-24 04:02:34 +01:00
/* --------------------------------- */
2018-06-04 09:31:30 +02:00
/* get frame on which the "hold" for the bone ends
2011-03-24 04:02:34 +01:00
* XXX : this may not really work that well if a bone moves on some channels and not others
2012-05-08 22:18:33 +02:00
* if this happens to be a major issue , scrap this , and just make this happen
2018-11-14 02:53:15 +01:00
* independently per F - Curve
2011-03-24 04:02:34 +01:00
*/
2018-10-20 00:08:12 +02:00
static float pose_propagate_get_boneHoldEndFrame ( tPChanFCurveLink * pfl , float startFrame )
2011-03-24 04:02:34 +01:00
{
2018-10-13 19:22:44 +02:00
DLRBT_Tree keys ;
2018-06-04 09:31:30 +02:00
2018-10-20 00:08:12 +02:00
Object * ob = pfl - > ob ;
2012-05-08 22:18:33 +02:00
AnimData * adt = ob - > adt ;
2011-03-24 04:02:34 +01:00
LinkData * ld ;
float endFrame = startFrame ;
2018-06-04 09:31:30 +02:00
2012-03-04 05:35:12 +01:00
/* set up optimized data-structures for searching for relevant keyframes + holds */
2011-03-24 04:02:34 +01:00
BLI_dlrbTree_init ( & keys ) ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
for ( ld = pfl - > fcurves . first ; ld ; ld = ld - > next ) {
FCurve * fcu = ( FCurve * ) ld - > data ;
Dope Sheet: new option to display keyframe interpolation mode and extremes.
With the new automatic handle algorithm, it is possible to do a lot
of the animation via keyframes without touching the curves. It is
however necessary to change the keyframe interpolation and handle
types in certain cases. Currently the dopesheet/action editor
allows changing the types, but does not show them in any way.
To fix, add a new menu option to display this information. For handle
type, it is represented using the shape of the key icons: diamond for
Free, clipped diamond for Aligned, square for Vector, circle for Auto
Clamp, and cirle with dot for Automatic.
Non-bezier interpolation is a property of intervals between keys,
so it is marked by drawing lines, similar to holds. In this initial
version, only the fact of non-bezier interpolation is displayed,
without distinguishing types. For summaries, the line is drawn at
half alpha if not all curves in the group are non-bezier.
In addition, it is sometimes helpful to know the general direction
of change of the curve, and which keys are extremes. This commit
also adds an option to highlight extremes, based on comparing the
keyed values with adjacent keys. Half-intensity display is used
for overshot bezier extremes, or non-uniform summaries.
Reviewers: brecht, aligorith, billreynish
Differential Revision: https://developer.blender.org/D3788
2018-10-19 17:55:19 +02:00
fcurve_to_keylist ( adt , fcu , & keys , 0 ) ;
2011-03-24 04:02:34 +01:00
}
2018-06-04 09:31:30 +02:00
/* find the long keyframe (i.e. hold), and hence obtain the endFrame value
2018-11-14 02:53:15 +01:00
* - the best case would be one that starts on the frame itself
2011-03-24 04:02:34 +01:00
*/
2018-10-13 19:22:44 +02:00
ActKeyColumn * ab = ( ActKeyColumn * ) BLI_dlrbTree_search_exact ( & keys , compare_ak_cfraPtr , & startFrame ) ;
/* There are only two cases for no-exact match:
* 1 ) the current frame is just before another key but not on a key itself
* 2 ) the current frame is on a key , but that key doesn ' t link to the next
*
* If we ' ve got the first case , then we can search for another block ,
* otherwise forget it , as we ' d be overwriting some valid data .
*/
if ( ab = = NULL ) {
/* we've got case 1, so try the one after */
ab = ( ActKeyColumn * ) BLI_dlrbTree_search_next ( & keys , compare_ak_cfraPtr , & startFrame ) ;
if ( ( actkeyblock_get_valid_hold ( ab ) & ACTKEYBLOCK_FLAG_STATIC_HOLD ) = = 0 ) {
/* try the block before this frame then as last resort */
ab = ( ActKeyColumn * ) BLI_dlrbTree_search_prev ( & keys , compare_ak_cfraPtr , & startFrame ) ;
2011-03-24 04:02:34 +01:00
}
}
2018-06-04 09:31:30 +02:00
2018-10-13 19:22:44 +02:00
/* whatever happens, stop searching now... */
if ( ( actkeyblock_get_valid_hold ( ab ) & ACTKEYBLOCK_FLAG_STATIC_HOLD ) = = 0 ) {
/* restrict range to just the frame itself
* i . e . everything is in motion , so no holds to safely overwrite
*/
ab = NULL ;
}
2011-03-24 04:02:34 +01:00
/* check if we can go any further than we've already gone */
if ( ab ) {
/* go to next if it is also valid and meets "extension" criteria */
while ( ab - > next ) {
2018-10-13 19:22:44 +02:00
ActKeyColumn * abn = ab - > next ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* must be valid */
2018-10-13 19:22:44 +02:00
if ( ( actkeyblock_get_valid_hold ( abn ) & ACTKEYBLOCK_FLAG_STATIC_HOLD ) = = 0 ) {
2011-03-24 04:02:34 +01:00
break ;
2018-10-13 19:22:44 +02:00
}
2011-03-24 04:02:34 +01:00
/* should have the same number of curves */
2018-10-13 19:22:44 +02:00
if ( ab - > totblock ! = abn - > totblock ) {
2011-03-24 04:02:34 +01:00
break ;
2018-10-13 19:22:44 +02:00
}
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* we can extend the bounds to the end of this "next" block now */
ab = abn ;
}
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* end frame can now take the value of the end of the block */
2018-10-13 19:22:44 +02:00
endFrame = ab - > next - > cfra ;
2011-03-24 04:02:34 +01:00
}
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* free temp memory */
BLI_dlrbTree_free ( & keys ) ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* return the end frame we've found */
return endFrame ;
}
/* get reference value from F-Curve using RNA */
2014-04-01 02:34:00 +02:00
static bool pose_propagate_get_refVal ( Object * ob , FCurve * fcu , float * value )
2011-03-24 04:02:34 +01:00
{
PointerRNA id_ptr , ptr ;
PropertyRNA * prop ;
2014-04-01 02:34:00 +02:00
bool found = false ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* base pointer is always the object -> id_ptr */
RNA_id_pointer_create ( & ob - > id , & id_ptr ) ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* resolve the property... */
Bugfix [#34836] Crash when driver variable has path == 'data'
Most of the places which relied on RNA_path_resolve() did so believing that if
it returned true, that it had found a valid property, and that the returned
pointer+property combination would be what the path referred to. However, it
turns out that if the property at the end of the path turns out to be a
"pointer" property (e.g. "data" for Object.data), this would automatically
become the pointer part, while the prop part would be set to null. Hence, if a
user accidentally (or otherwise) specifies a path for the single-property driver
variable type like this, then Blender would crash.
This commit introduces two convenience functions - RNA_path_resolve_property()
and RNA_path_resolve_property_full() - which mirror/wrap the existing
RNA_path_resolve() functions. The only difference though is that these include a
check to ensure that what was found from resolving the path was in fact a
property (they only return true iff this is the case), and make it explicitly
clear in the name that this is what they will do so that there's no further
confusion. It is possible to do without these wrapper functions by doing these
checks inline, but the few cases that had been patched already were pretty
hideous looking specimens. Using these just make it clearer and simpler for all.
I've also beefed up the docs on these a bit, and changed these to using bools.
2013-04-22 15:22:07 +02:00
if ( RNA_path_resolve_property ( & id_ptr , fcu - > rna_path , & ptr , & prop ) ) {
2011-08-14 12:28:18 +02:00
if ( RNA_property_array_check ( prop ) ) {
2011-03-24 04:02:34 +01:00
/* array */
2011-04-23 09:04:50 +02:00
if ( fcu - > array_index < RNA_property_array_length ( & ptr , prop ) ) {
2014-04-01 02:34:00 +02:00
found = true ;
2011-03-24 04:02:34 +01:00
switch ( RNA_property_type ( prop ) ) {
case PROP_BOOLEAN :
2012-05-08 22:18:33 +02:00
* value = ( float ) RNA_property_boolean_get_index ( & ptr , prop , fcu - > array_index ) ;
2011-03-24 04:02:34 +01:00
break ;
case PROP_INT :
2012-05-08 22:18:33 +02:00
* value = ( float ) RNA_property_int_get_index ( & ptr , prop , fcu - > array_index ) ;
2011-03-24 04:02:34 +01:00
break ;
case PROP_FLOAT :
2012-05-08 22:18:33 +02:00
* value = RNA_property_float_get_index ( & ptr , prop , fcu - > array_index ) ;
2011-03-24 04:02:34 +01:00
break ;
default :
2014-04-01 02:34:00 +02:00
found = false ;
2011-03-24 04:02:34 +01:00
break ;
}
}
}
else {
/* not an array */
2014-04-01 02:34:00 +02:00
found = true ;
2011-03-24 04:02:34 +01:00
switch ( RNA_property_type ( prop ) ) {
case PROP_BOOLEAN :
2012-05-08 22:18:33 +02:00
* value = ( float ) RNA_property_boolean_get ( & ptr , prop ) ;
2011-03-24 04:02:34 +01:00
break ;
case PROP_INT :
2012-05-08 22:18:33 +02:00
* value = ( float ) RNA_property_int_get ( & ptr , prop ) ;
2011-03-24 04:02:34 +01:00
break ;
case PROP_ENUM :
2012-05-08 22:18:33 +02:00
* value = ( float ) RNA_property_enum_get ( & ptr , prop ) ;
2011-03-24 04:02:34 +01:00
break ;
case PROP_FLOAT :
2012-05-08 22:18:33 +02:00
* value = RNA_property_float_get ( & ptr , prop ) ;
2011-03-24 04:02:34 +01:00
break ;
default :
2014-04-01 02:34:00 +02:00
found = false ;
2011-03-24 04:02:34 +01:00
break ;
}
}
}
2018-06-04 09:31:30 +02:00
2011-04-23 09:04:50 +02:00
return found ;
2011-03-24 04:02:34 +01:00
}
/* propagate just works along each F-Curve in turn */
2012-05-08 22:18:33 +02:00
static void pose_propagate_fcurve ( wmOperator * op , Object * ob , FCurve * fcu ,
float startFrame , tPosePropagate_ModeData modeData )
2011-03-24 04:02:34 +01:00
{
const int mode = RNA_enum_get ( op - > ptr , " mode " ) ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
BezTriple * bezt ;
float refVal = 0.0f ;
2013-03-17 20:13:04 +01:00
bool keyExists ;
2011-03-24 04:02:34 +01:00
int i , match ;
2012-05-08 22:18:33 +02:00
short first = 1 ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* skip if no keyframes to edit */
if ( ( fcu - > bezt = = NULL ) | | ( fcu - > totvert < 2 ) )
return ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* find the reference value from bones directly, which means that the user
2018-06-04 09:31:30 +02:00
* doesn ' t need to firstly keyframe the pose ( though this doesn ' t mean that
2011-03-24 04:02:34 +01:00
* they can ' t either )
*/
2012-05-08 22:18:33 +02:00
if ( ! pose_propagate_get_refVal ( ob , fcu , & refVal ) )
2011-04-23 09:04:50 +02:00
return ;
2018-06-04 09:31:30 +02:00
/* find the first keyframe to start propagating from
2018-11-14 02:53:15 +01:00
* - if there ' s a keyframe on the current frame , we probably want to save this value there too
* since it may be as of yet unkeyed
* - if starting before the starting frame , don ' t touch the key , as it may have had some valid
* values
* - if only doing selected keyframes , start from the first one
2011-03-24 04:02:34 +01:00
*/
2015-06-16 15:35:01 +02:00
if ( mode ! = POSE_PROPAGATE_SELECTED_KEYS ) {
match = binarysearch_bezt_index ( fcu - > bezt , startFrame , fcu - > totvert , & keyExists ) ;
2018-06-04 09:31:30 +02:00
2015-06-16 15:35:01 +02:00
if ( fcu - > bezt [ match ] . vec [ 1 ] [ 0 ] < startFrame )
i = match + 1 ;
else
i = match ;
}
else {
/* selected - start from first keyframe */
i = 0 ;
}
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
for ( bezt = & fcu - > bezt [ i ] ; i < fcu - > totvert ; i + + , bezt + + ) {
/* additional termination conditions based on the operator 'mode' property go here... */
if ( ELEM ( mode , POSE_PROPAGATE_BEFORE_FRAME , POSE_PROPAGATE_SMART_HOLDS ) ) {
/* stop if keyframe is outside the accepted range */
2011-03-31 03:37:42 +02:00
if ( bezt - > vec [ 1 ] [ 0 ] > modeData . end_frame )
2018-06-04 09:31:30 +02:00
break ;
2011-03-24 04:02:34 +01:00
}
else if ( mode = = POSE_PROPAGATE_NEXT_KEY ) {
/* stop after the first keyframe has been processed */
if ( first = = 0 )
break ;
}
2011-03-31 02:45:52 +02:00
else if ( mode = = POSE_PROPAGATE_LAST_KEY ) {
/* only affect this frame if it will be the last one */
2012-05-08 22:18:33 +02:00
if ( i ! = ( fcu - > totvert - 1 ) )
2011-03-31 02:45:52 +02:00
continue ;
}
2011-03-31 03:37:42 +02:00
else if ( mode = = POSE_PROPAGATE_SELECTED_MARKERS ) {
/* only allow if there's a marker on this frame */
CfraElem * ce = NULL ;
2018-06-04 09:31:30 +02:00
2011-03-31 03:37:42 +02:00
/* stop on matching marker if there is one */
for ( ce = modeData . sel_markers . first ; ce ; ce = ce - > next ) {
2017-09-27 03:13:03 +02:00
if ( ce - > cfra = = round_fl_to_int ( bezt - > vec [ 1 ] [ 0 ] ) )
2011-03-31 03:37:42 +02:00
break ;
}
2018-06-04 09:31:30 +02:00
2011-03-31 03:37:42 +02:00
/* skip this keyframe if no marker */
if ( ce = = NULL )
continue ;
}
2015-04-02 12:30:30 +02:00
else if ( mode = = POSE_PROPAGATE_SELECTED_KEYS ) {
/* only allow if this keyframe is already selected - skip otherwise */
2015-07-09 06:31:27 +02:00
if ( BEZT_ISSEL_ANY ( bezt ) = = 0 )
2015-04-02 12:30:30 +02:00
continue ;
}
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* just flatten handles, since values will now be the same either side... */
2012-10-20 22:20:02 +02:00
/* TODO: perhaps a fade-out modulation of the value is required here (optional once again)? */
2011-03-24 04:02:34 +01:00
bezt - > vec [ 0 ] [ 1 ] = bezt - > vec [ 1 ] [ 1 ] = bezt - > vec [ 2 ] [ 1 ] = refVal ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* select keyframe to indicate that it's been changed */
bezt - > f2 | = SELECT ;
first = 0 ;
}
}
/* --------------------------------- */
2012-05-08 22:18:33 +02:00
static int pose_propagate_exec ( bContext * C , wmOperator * op )
2011-03-24 04:02:34 +01:00
{
Scene * scene = CTX_data_scene ( C ) ;
2018-10-20 00:08:12 +02:00
ViewLayer * view_layer = CTX_data_view_layer ( C ) ;
2018-11-25 12:50:34 +01:00
View3D * v3d = CTX_wm_view3d ( C ) ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
ListBase pflinks = { NULL , NULL } ;
tPChanFCurveLink * pfl ;
2018-06-04 09:31:30 +02:00
2011-03-31 03:37:42 +02:00
tPosePropagate_ModeData modeData ;
2011-03-24 04:02:34 +01:00
const int mode = RNA_enum_get ( op - > ptr , " mode " ) ;
2018-06-04 09:31:30 +02:00
2018-10-20 00:08:12 +02:00
/* isolate F-Curves related to the selected bones */
poseAnim_mapping_get ( C , & pflinks ) ;
if ( BLI_listbase_is_empty ( & pflinks ) ) {
/* There is a change the reason the list is empty is that there is no valid object to propagate poses for.
* This is very unlikely though , so we focus on the most likely issue . */
2011-03-24 04:02:34 +01:00
BKE_report ( op - > reports , RPT_ERROR , " No keyframed poses to propagate to " ) ;
return OPERATOR_CANCELLED ;
}
2018-06-04 09:31:30 +02:00
2011-03-31 03:37:42 +02:00
/* mode-specific data preprocessing (requiring no access to curves) */
if ( mode = = POSE_PROPAGATE_SELECTED_MARKERS ) {
/* get a list of selected markers */
ED_markers_make_cfra_list ( & scene - > markers , & modeData . sel_markers , SELECT ) ;
}
else {
/* assume everything else wants endFrame */
modeData . end_frame = RNA_float_get ( op - > ptr , " end_frame " ) ;
}
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* for each bone, perform the copying required */
for ( pfl = pflinks . first ; pfl ; pfl = pfl - > next ) {
LinkData * ld ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* mode-specific data preprocessing (requiring access to all curves) */
if ( mode = = POSE_PROPAGATE_SMART_HOLDS ) {
/* we store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting
* from the keyframe that occurs after the current frame
*/
2018-10-20 00:08:12 +02:00
modeData . end_frame = pose_propagate_get_boneHoldEndFrame ( pfl , ( float ) CFRA ) ;
2011-03-24 04:02:34 +01:00
}
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* go through propagating pose to keyframes, curve by curve */
2018-10-20 00:08:12 +02:00
for ( ld = pfl - > fcurves . first ; ld ; ld = ld - > next ) {
pose_propagate_fcurve ( op , pfl - > ob , ( FCurve * ) ld - > data , ( float ) CFRA , modeData ) ;
}
2011-03-24 04:02:34 +01:00
}
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* free temp data */
poseAnim_mapping_free ( & pflinks ) ;
2018-06-04 09:31:30 +02:00
2011-03-31 03:37:42 +02:00
if ( mode = = POSE_PROPAGATE_SELECTED_MARKERS )
BLI_freelistN ( & modeData . sel_markers ) ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* updates + notifiers */
2018-12-01 17:43:10 +01:00
FOREACH_OBJECT_IN_MODE_BEGIN ( view_layer , v3d , OB_ARMATURE , OB_MODE_POSE , ob ) {
2018-10-20 00:08:12 +02:00
poseAnim_mapping_refresh ( C , scene , ob ) ;
} FOREACH_OBJECT_IN_MODE_END ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
return OPERATOR_FINISHED ;
}
/* --------------------------------- */
2012-04-29 19:11:40 +02:00
void POSE_OT_propagate ( wmOperatorType * ot )
2011-03-24 04:02:34 +01:00
{
2017-10-18 06:07:26 +02:00
static const EnumPropertyItem terminate_items [ ] = {
2015-04-06 20:43:34 +02:00
{ POSE_PROPAGATE_SMART_HOLDS , " WHILE_HELD " , 0 , " While Held " ,
2019-03-14 22:53:22 +01:00
" Propagate pose to all keyframes after current frame that don't change (Default behavior) " } ,
2015-04-06 20:43:34 +02:00
{ POSE_PROPAGATE_NEXT_KEY , " NEXT_KEY " , 0 , " To Next Keyframe " ,
2019-03-14 22:53:22 +01:00
" Propagate pose to first keyframe following the current frame only " } ,
2015-04-06 20:43:34 +02:00
{ POSE_PROPAGATE_LAST_KEY , " LAST_KEY " , 0 , " To Last Keyframe " ,
2019-03-14 22:53:22 +01:00
" Propagate pose to the last keyframe only (i.e. making action cyclic) " } ,
2015-04-06 20:43:34 +02:00
{ POSE_PROPAGATE_BEFORE_FRAME , " BEFORE_FRAME " , 0 , " Before Frame " ,
2019-03-14 22:53:22 +01:00
" Propagate pose to all keyframes between current frame and 'Frame' property " } ,
2015-04-06 20:43:34 +02:00
{ POSE_PROPAGATE_BEFORE_END , " BEFORE_END " , 0 , " Before Last Keyframe " ,
2019-03-14 22:53:22 +01:00
" Propagate pose to all keyframes from current frame until no more are found " } ,
2015-04-06 20:43:34 +02:00
{ POSE_PROPAGATE_SELECTED_KEYS , " SELECTED_KEYS " , 0 , " On Selected Keyframes " ,
2019-03-14 22:53:22 +01:00
" Propagate pose to all selected keyframes " } ,
2015-04-06 20:43:34 +02:00
{ POSE_PROPAGATE_SELECTED_MARKERS , " SELECTED_MARKERS " , 0 , " On Selected Markers " ,
2019-03-14 22:53:22 +01:00
" Propagate pose to all keyframes occurring on frames with Scene Markers after the current frame " } ,
2019-02-03 04:01:45 +01:00
{ 0 , NULL , 0 , NULL , NULL } ,
} ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* identifiers */
2012-03-22 08:26:09 +01:00
ot - > name = " Propagate Pose " ;
ot - > idname = " POSE_OT_propagate " ;
ot - > description = " Copy selected aspects of the current pose to subsequent poses already keyframed " ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* callbacks */
2012-03-22 08:26:09 +01:00
ot - > exec = pose_propagate_exec ;
2012-10-20 22:20:02 +02:00
ot - > poll = ED_operator_posemode ; /* XXX: needs selected bones! */
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* flag */
2012-05-08 22:18:33 +02:00
ot - > flag = OPTYPE_REGISTER | OPTYPE_UNDO ;
2018-06-04 09:31:30 +02:00
2011-03-24 04:02:34 +01:00
/* properties */
2012-10-20 22:20:02 +02:00
/* TODO: add "fade out" control for tapering off amount of propagation as time goes by? */
2012-03-22 08:26:09 +01:00
ot - > prop = RNA_def_enum ( ot - > srna , " mode " , terminate_items , POSE_PROPAGATE_SMART_HOLDS , " Terminate Mode " , " Method used to determine when to stop propagating pose to keyframes " ) ;
2011-03-31 02:45:52 +02:00
RNA_def_float ( ot - > srna , " end_frame " , 250.0 , FLT_MIN , FLT_MAX , " End Frame " , " Frame to stop propagating frames to (for 'Before Frame' mode) " , 1.0 , 250.0 ) ;
2011-03-24 04:02:34 +01:00
}
/* **************************************************** */