2011-02-23 11:52:22 +01:00
/*
2009-09-19 02:18:42 +02:00
* * * * * * BEGIN GPL LICENSE BLOCK * * * * *
*
* 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
*
* Contributor ( s ) : Joshua Leung
*
* * * * * * END GPL LICENSE BLOCK * * * * *
*/
2011-02-27 21:29:51 +01:00
2013-02-28 01:01:20 +01:00
/** \file blender/editors/armature/pose_slide.c
2011-02-27 21:29:51 +01:00
* \ ingroup edarmature
*/
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"
# include "BKE_context.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"
# 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"
/* **************************************************** */
/* == POSE 'SLIDING' TOOLS ==
*
* 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
* rigs to be manipulated using a familiar paint - based interface .
*/
/* **************************************************** */
/* A) Push & Relax, Breakdowner */
/* Temporary data shared between these operators */
typedef struct tPoseSlideOp {
2012-05-08 22:18:33 +02:00
Scene * scene ; /* current scene */
ScrArea * sa ; /* area that we're operating in (needed for modal()) */
ARegion * ar ; /* region that we're operating in (needed for modal()) */
Object * ob ; /* active object that Pose Info comes from */
bArmature * arm ; /* armature for pose */
ListBase pfLinks ; /* links between posechannels and f-curves */
DLRBT_Tree keys ; /* binary tree for quicker searching for keyframes (when applicable) */
int cframe ; /* current frame number */
int prevFrame ; /* frame before current frame (blend-from) */
int nextFrame ; /* frame after current frame (blend-to) */
2009-09-19 02:18:42 +02:00
2012-05-08 22:18:33 +02:00
int mode ; /* sliding mode (ePoseSlide_Modes) */
2012-10-20 22:20:02 +02:00
int flag ; /* unused for now, but can later get used for storing runtime settings.... */
2009-09-19 02:18:42 +02:00
2012-05-08 22:18:33 +02:00
float percentage ; /* 0-1 value for determining the influence of whatever is relevant */
2015-05-17 09:45:59 +02:00
NumInput num ; /* numeric input */
2009-09-19 02:18:42 +02:00
} tPoseSlideOp ;
/* 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 ;
/* ------------------------------------ */
/* operator init */
2012-05-08 22:18:33 +02:00
static int pose_slide_init ( bContext * C , wmOperator * op , short mode )
2009-09-19 02:18:42 +02:00
{
tPoseSlideOp * pso ;
2012-05-08 22:18:33 +02:00
bAction * act = NULL ;
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 " ) ;
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 - > ob = BKE_object_pose_armature_get ( CTX_data_active_object ( C ) ) ;
pso - > arm = ( pso - > ob ) ? pso - > ob - > data : NULL ;
pso - > sa = CTX_wm_area ( C ) ; /* only really needed when doing modal() */
pso - > ar = CTX_wm_region ( C ) ; /* only really needed when doing modal() */
2009-09-19 02:18:42 +02:00
2012-05-08 22:18:33 +02:00
pso - > cframe = pso - > scene - > r . cfra ;
pso - > mode = mode ;
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 " ) ;
2009-09-19 02:18:42 +02:00
/* check the settings from the context */
2014-07-19 17:30:29 +02:00
if ( ELEM ( NULL , pso - > ob , pso - > arm , pso - > ob - > adt , pso - > ob - > adt - > action ) )
2009-09-19 02:18:42 +02:00
return 0 ;
else
2012-05-08 22:18:33 +02:00
act = pso - > ob - > adt - > action ;
2009-09-19 02:18:42 +02:00
/* for each Pose-Channel which gets affected, get the F-Curves for that channel
* and set the relevant transform flags . . .
*/
2010-02-19 12:42:21 +01:00
poseAnim_mapping_get ( C , & pso - > pfLinks , pso - > ob , act ) ;
2009-09-19 02:18:42 +02:00
2009-09-19 13:59:23 +02:00
/* set depsgraph flags */
2012-05-08 22:18:33 +02:00
/* make sure the lock is set OK, unlock can be accidentally saved? */
2009-09-19 13:59:23 +02:00
pso - > ob - > pose - > flag | = POSE_LOCKED ;
pso - > ob - > pose - > flag & = ~ POSE_DO_UNLOCK ;
2012-03-02 17:05:54 +01: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 ) ;
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... */
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 ;
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 ) ;
2009-09-19 02:18:42 +02:00
/* free RB-BST for keyframes (if it contained data) */
BLI_dlrbTree_free ( & pso - > keys ) ;
/* free data itself */
MEM_freeN ( pso ) ;
}
/* 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 */
poseAnim_mapping_refresh ( C , pso - > scene , pso - > ob ) ;
2009-09-19 02:18:42 +02:00
}
2011-03-13 13:22:57 +01:00
/* helper for apply() - perform sliding for some value */
2012-05-08 22:18:33 +02:00
static void pose_slide_apply_val ( tPoseSlideOp * pso , FCurve * fcu , float * val )
2011-03-13 13:22:57 +01:00
{
float cframe = ( float ) pso - > cframe ;
float sVal , eVal ;
float w1 , w2 ;
/* get keyframe values for endpoint poses to blend with */
2012-05-08 22:18:33 +02:00
/* previous/start */
sVal = evaluate_fcurve ( fcu , ( float ) pso - > prevFrame ) ;
/* next/end */
eVal = evaluate_fcurve ( fcu , ( float ) pso - > nextFrame ) ;
2011-03-13 13:22:57 +01: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 ;
}
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 {
/* - these weights are derived from the relative distance of these
* poses from the current frame
2012-07-03 21:09:07 +02:00
* - they then get normalized so that they only sum up to 1
2011-03-13 13:22:57 +01:00
*/
float wtot ;
w1 = cframe - ( float ) pso - > prevFrame ;
w2 = ( float ) pso - > nextFrame - cframe ;
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
}
/* depending on the mode, calculate the new value
* - 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
*/
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
2011-03-13 13:22:57 +01:00
* - numerator should be larger than denominator to ' expand ' the result
* - perform this weighting a number of times given by the percentage . . .
*/
2012-07-07 01:56:59 +02:00
int iters = ( int ) ceil ( 10.0f * pso - > percentage ) ; /* TODO: maybe a sensitivity ctrl on top of this is needed */
2015-04-08 04:17:36 +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
2011-03-13 13:22:57 +01:00
* - numerator should be smaller than denominator to ' relax ' the result
* - perform this weighting a number of times given by the percentage . . .
*/
2012-07-07 01:56:59 +02:00
int iters = ( int ) ceil ( 10.0f * pso - > percentage ) ; /* TODO: maybe a sensitivity ctrl on top of this is needed */
2015-04-08 04:17:36 +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 ;
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 ) ;
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 ;
2013-03-26 08:29:01 +01:00
2011-03-13 13:22:57 +01:00
/* just work on these channels one by one... there's no interaction between values */
2013-03-26 08:29:01 +01:00
BLI_assert ( fcu - > array_index < 3 ) ;
2011-03-13 13:26:53 +01:00
pose_slide_apply_val ( pso , fcu , & vec [ fcu - > array_index ] ) ;
2011-03-13 13:22:57 +01:00
}
/* free the temp path we got */
MEM_freeN ( path ) ;
}
/* helper for apply() - perform sliding for custom properties */
2012-05-08 22:18:33 +02:00
static void pose_slide_apply_props ( tPoseSlideOp * pso , tPChanFCurveLink * pfl )
2011-03-13 13:22:57 +01:00
{
PointerRNA ptr = { { NULL } } ;
LinkData * ld ;
int len = strlen ( pfl - > pchan_path ) ;
/* setup pointer RNA for resolving paths */
RNA_pointer_create ( NULL , & RNA_PoseBone , pfl - > pchan , & ptr ) ;
/* custom properties are just denoted using ["..."][etc.] after the end of the base path,
* so just check for opening pair after the end of the path
*/
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 ;
2009-09-19 02:18:42 +02:00
2011-03-13 13:22:57 +01:00
if ( fcu - > rna_path = = NULL )
continue ;
2009-09-19 02:18:42 +02:00
2011-03-13 13:22:57 +01:00
/* do we have a match?
* - bPtr is the RNA Path with the standard part chopped off
* - pPtr is the chunk of the path which is left over
*/
bPtr = strstr ( fcu - > rna_path , pfl - > pchan_path ) + len ;
pPtr = strstr ( bPtr , " [ \" " ) ; /* dummy " for texteditor bugs */
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 ) ;
2009-09-19 13:59:23 +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 ) ;
pose_slide_apply_val ( pso , fcu , & tval ) ;
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 ) ;
pose_slide_apply_val ( pso , fcu , & tval ) ;
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
}
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 ) ;
pose_slide_apply_val ( pso , fcu , & tval ) ;
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 ;
}
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 ;
/* 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 " ) ;
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 ;
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 ;
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 ;
}
}
/* only if all channels exist, proceed */
if ( fcu_w & & fcu_x & & fcu_y & & fcu_z ) {
float quat_prev [ 4 ] , quat_next [ 4 ] ;
/* get 2 quats */
quat_prev [ 0 ] = evaluate_fcurve ( fcu_w , pso - > prevFrame ) ;
quat_prev [ 1 ] = evaluate_fcurve ( fcu_x , pso - > prevFrame ) ;
quat_prev [ 2 ] = evaluate_fcurve ( fcu_y , pso - > prevFrame ) ;
quat_prev [ 3 ] = evaluate_fcurve ( fcu_z , pso - > prevFrame ) ;
quat_next [ 0 ] = evaluate_fcurve ( fcu_w , pso - > nextFrame ) ;
quat_next [ 1 ] = evaluate_fcurve ( fcu_x , pso - > nextFrame ) ;
quat_next [ 2 ] = evaluate_fcurve ( fcu_y , pso - > nextFrame ) ;
quat_next [ 3 ] = evaluate_fcurve ( fcu_z , pso - > nextFrame ) ;
/* perform blending */
if ( pso - > mode = = POSESLIDE_BREAKDOWN ) {
/* just perform the interpol between quat_prev and quat_next using pso->percentage as a guide */
2009-11-10 21:43:45 +01:00
interp_qt_qtqt ( pchan - > quat , 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 ) {
float quat_diff [ 4 ] , quat_orig [ 4 ] ;
2015-04-08 04:17:36 +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? */
2009-12-30 00:25:46 +01:00
sub_qt_qtqt ( quat_diff , pchan - > quat , quat_prev ) ;
2015-04-08 04:17:36 +02:00
2009-12-30 00:25:46 +01:00
/* make a copy of the original rotation */
2011-10-28 14:40:15 +02:00
copy_qt_qt ( quat_orig , pchan - > quat ) ;
2009-12-30 00:25:46 +01:00
/* increase the original by the delta transform, by an amount determined by percentage */
add_qt_qtqt ( pchan - > quat , quat_orig , quat_diff , pso - > percentage ) ;
}
2009-09-20 14:54:30 +02:00
else {
float quat_interp [ 4 ] , quat_orig [ 4 ] ;
2012-07-07 01:56:59 +02:00
int iters = ( int ) ceil ( 10.0f * pso - > percentage ) ; /* TODO: maybe a sensitivity ctrl on top of this is needed */
2015-04-08 04:17:36 +02:00
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 ) ) ;
2009-09-20 14:54:30 +02:00
/* make a copy of the original rotation */
2011-10-28 14:40:15 +02:00
copy_qt_qt ( quat_orig , pchan - > quat ) ;
2009-09-20 14:54:30 +02:00
2009-12-30 00:25:46 +01:00
/* tricky interpolations - blending between original and new */
2012-05-08 22:18:33 +02:00
interp_qt_qtqt ( pchan - > quat , quat_orig , quat_interp , 1.0f / 6.0f ) ;
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 ;
/* sanitise the frame ranges */
if ( pso - > prevFrame = = pso - > nextFrame ) {
/* move out one step either side */
pso - > prevFrame - - ;
pso - > nextFrame + + ;
}
/* 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 ) {
2009-09-19 02:18:42 +02:00
/* valid transforms for each PoseChannel should have been noted already
* - 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 . . .
*/
2012-05-08 22:18:33 +02:00
bPoseChannel * pchan = pfl - > pchan ;
2009-09-19 02:18:42 +02:00
if ( pchan - > flag & POSE_LOC ) {
/* calculate these for the 'location' vector, and use location curves */
pose_slide_apply_vec3 ( pso , pfl , pchan - > loc , " location " ) ;
}
if ( pchan - > flag & POSE_SIZE ) {
/* calculate these for the 'scale' vector, and use scale curves */
pose_slide_apply_vec3 ( pso , pfl , pchan - > size , " scale " ) ;
}
if ( pchan - > flag & POSE_ROT ) {
/* 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 ) ;
}
}
2011-03-13 13:22:57 +01:00
if ( pfl - > oldprops ) {
/* not strictly a transform, but contributes to the pose produced in many rigs */
pose_slide_apply_props ( pso , pfl ) ;
}
2009-09-19 02:18:42 +02:00
}
2009-09-20 07:05:16 +02:00
/* depsgraph updates + redraws */
pose_slide_refresh ( C , pso ) ;
}
/* perform autokeyframing 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 */
poseAnim_mapping_autoKeyframe ( C , pso - > scene , pso - > ob , & 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 */
2012-05-08 22:18:33 +02:00
static void pose_slide_draw_status ( tPoseSlideOp * pso )
2011-08-10 02:46:20 +02:00
{
2015-05-17 09:45:59 +02:00
char status_str [ 256 ] ;
2012-01-11 10:33:44 +01:00
char mode_str [ 32 ] ;
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 ;
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 ;
}
2015-05-17 09:45:59 +02:00
if ( hasNumInput ( & pso - > num ) ) {
Scene * scene = pso - > scene ;
char str_offs [ NUM_STR_REP_LEN ] ;
outputNumInput ( & pso - > num , str_offs , & scene - > unit ) ;
BLI_snprintf ( status_str , sizeof ( status_str ) , " %s: %s " , mode_str , str_offs ) ;
}
else {
BLI_snprintf ( status_str , sizeof ( status_str ) , " %s: %d %% " , mode_str , ( int ) ( pso - > percentage * 100.0f ) ) ;
}
2012-01-11 10:33:44 +01:00
ED_area_headerprint ( 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
AnimData * adt = pso - > ob - > adt ;
wmWindow * win = CTX_wm_window ( C ) ;
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 ;
/* 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 ;
2009-09-19 02:18:42 +02:00
fcurve_to_keylist ( adt , fcu , & pso - > keys , NULL ) ;
}
}
/* consolidate these keyframes, and figure out the nearest ones */
BLI_dlrbTree_linkedlist_sync ( & pso - > keys ) ;
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 ;
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 ) ;
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 ) ;
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 ) ;
}
}
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 ;
}
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 ) ;
2009-09-20 07:05:16 +02:00
/* depsgraph updates + redraws */
pose_slide_refresh ( C , pso ) ;
/* set cursor to indicate modal */
2013-09-07 00:34:29 +02:00
WM_cursor_modal_set ( win , BC_EW_SCROLLCURSOR ) ;
2009-09-20 07:05:16 +02:00
2011-08-10 02:46:20 +02:00
/* header print */
2011-08-30 11:50:31 +02:00
pose_slide_draw_status ( pso ) ;
2011-08-10 02:46:20 +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 ) ;
}
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 ) ;
2015-05-17 09:45:59 +02:00
const bool has_numinput = hasNumInput ( & pso - > num ) ;
2009-09-19 02:18:42 +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 :
2009-09-20 07:05:16 +02:00
{
2011-08-10 02:46:20 +02:00
/* return to normal cursor and header status */
ED_area_headerprint ( pso - > sa , NULL ) ;
2013-09-07 00:34:29 +02:00
WM_cursor_modal_restore ( win ) ;
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 ) ;
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
}
2009-09-19 13:59:23 +02:00
2012-05-08 22:18:33 +02:00
case ESCKEY : /* cancel */
2009-09-19 13:59:23 +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 */
ED_area_headerprint ( pso - > sa , NULL ) ;
2013-09-07 00:34:29 +02:00
WM_cursor_modal_restore ( win ) ;
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 ) ;
2009-09-20 07:05:16 +02:00
/* depsgraph updates + redraws */
pose_slide_refresh ( C , pso ) ;
/* clean up temp data */
2010-10-16 04:40:31 +02:00
pose_slide_exit ( op ) ;
2009-09-20 07:05:16 +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
}
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 ) ;
2015-05-17 09:45:59 +02:00
/* update percentage indicator in header */
pose_slide_draw_status ( pso ) ;
/* reset transforms (to avoid accumulation errors) */
pose_slide_reset ( pso ) ;
/* apply... */
pose_slide_apply ( C , pso ) ;
}
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 :
if ( ( event - > val = = KM_PRESS ) & & handleNumInput ( C , & pso - > num , event ) ) {
float value ;
/* Grab percentage from numeric input, and store this new value for redo
* NOTE : users see ints , while internally we use a 0 - 1 float
*/
value = pso - > percentage * 100.0f ;
applyNumInput ( & pso - > num , & value ) ;
pso - > percentage = value / 100.0f ;
CLAMP ( pso - > percentage , 0.0f , 1.0f ) ;
RNA_float_set ( op - > ptr , " percentage " , pso - > percentage ) ;
/* update percentage indicator in header */
pose_slide_draw_status ( pso ) ;
/* reset transforms (to avoid accumulation errors) */
pose_slide_reset ( pso ) ;
/* apply... */
pose_slide_apply ( C , pso ) ;
break ;
}
else {
/* unhandled event - maybe it was some view manip? */
/* allow to pass through */
return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH ;
}
2009-09-19 13:59:23 +02:00
}
2009-09-19 02:18:42 +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 ) ;
2009-09-19 02:18:42 +02:00
2009-09-20 07:05:16 +02:00
/* insert keyframes if needed */
pose_slide_autoKeyframe ( C , pso ) ;
2009-09-19 02:18:42 +02:00
/* cleanup and done */
2010-10-16 04:40:31 +02:00
pose_slide_exit ( op ) ;
2009-09-19 02:18:42 +02:00
return OPERATOR_FINISHED ;
}
/* common code for defining RNA properties */
2012-05-08 22:18:33 +02:00
static void pose_slide_opdef_properties ( wmOperatorType * ot )
2009-09-19 02:18:42 +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 ) ;
2009-09-19 02:18:42 +02:00
RNA_def_float_percentage ( ot - > srna , " percentage " , 0.5f , 0.0f , 1.0f , " Percentage " , " Weighting factor for the sliding operation " , 0.3 , 0.7 ) ;
}
/* ------------------------------------ */
/* 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 ;
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 ;
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 ) ;
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 ;
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 ;
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 " ;
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 ;
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 ;
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 ;
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 ;
2009-09-19 02:18:42 +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 ) ;
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 ;
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 ;
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 " ;
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 ;
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 ;
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 ;
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 ;
2009-09-19 02:18:42 +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 ) ;
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 ;
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 ;
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 " ;
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 ;
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 ;
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 ,
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 */
2011-03-31 03:37:42 +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 ;
/* selected markers: listbase for CfraElem's marking these frames */
ListBase sel_markers ;
} tPosePropagate_ModeData ;
2011-03-24 04:02:34 +01:00
/* --------------------------------- */
/* get frame on which the "hold" for the bone ends
* 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
2011-03-24 04:02:34 +01:00
* independently per F - Curve
*/
2012-05-08 22:18:33 +02:00
static float pose_propagate_get_boneHoldEndFrame ( Object * ob , tPChanFCurveLink * pfl , float startFrame )
2011-03-24 04:02:34 +01:00
{
DLRBT_Tree keys , blocks ;
ActKeyBlock * ab ;
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 ;
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 ) ;
BLI_dlrbTree_init ( & blocks ) ;
for ( ld = pfl - > fcurves . first ; ld ; ld = ld - > next ) {
FCurve * fcu = ( FCurve * ) ld - > data ;
fcurve_to_keylist ( adt , fcu , & keys , & blocks ) ;
}
BLI_dlrbTree_linkedlist_sync ( & keys ) ;
BLI_dlrbTree_linkedlist_sync ( & blocks ) ;
/* find the long keyframe (i.e. hold), and hence obtain the endFrame value
* - the best case would be one that starts on the frame itself
*/
ab = ( ActKeyBlock * ) BLI_dlrbTree_search_exact ( & blocks , compare_ab_cfraPtr , & startFrame ) ;
if ( actkeyblock_is_valid ( ab , & keys ) = = 0 ) {
/* There are only two cases for no-exact match:
2012-05-08 22:18:33 +02:00
* 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
2011-03-24 04:02:34 +01:00
*
* 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 ( BLI_dlrbTree_search_exact ( & keys , compare_ak_cfraPtr , & startFrame ) = = NULL ) {
/* we've got case 1, so try the one after */
ab = ( ActKeyBlock * ) BLI_dlrbTree_search_next ( & blocks , compare_ab_cfraPtr , & startFrame ) ;
if ( actkeyblock_is_valid ( ab , & keys ) = = 0 ) {
/* try the block before this frame then as last resort */
ab = ( ActKeyBlock * ) BLI_dlrbTree_search_prev ( & blocks , compare_ab_cfraPtr , & startFrame ) ;
/* whatever happens, stop searching now... */
if ( actkeyblock_is_valid ( ab , & keys ) = = 0 ) {
/* restrict range to just the frame itself
* i . e . everything is in motion , so no holds to safely overwrite
*/
ab = NULL ;
}
}
}
else {
/* we've got case 2 - set ab to NULL just in case, since we shouldn't do anything in this case */
ab = NULL ;
}
}
/* 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 ) {
ActKeyBlock * abn = ( ActKeyBlock * ) ab - > next ;
/* must be valid */
if ( actkeyblock_is_valid ( abn , & keys ) = = 0 )
break ;
/* should start on the same frame that the last ended on */
if ( ab - > end ! = abn - > start )
break ;
/* should have the same number of curves */
if ( ab - > totcurve ! = abn - > totcurve )
break ;
/* should have the same value
* XXX : this may be a bit fuzzy on larger data sets , so be careful
*/
if ( ab - > val ! = abn - > val )
break ;
/* we can extend the bounds to the end of this "next" block now */
ab = abn ;
}
/* end frame can now take the value of the end of the block */
endFrame = ab - > end ;
}
/* free temp memory */
BLI_dlrbTree_free ( & keys ) ;
BLI_dlrbTree_free ( & blocks ) ;
/* 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 ;
2011-03-24 04:02:34 +01:00
/* base pointer is always the object -> id_ptr */
RNA_id_pointer_create ( & ob - > id , & id_ptr ) ;
/* 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 ;
}
}
}
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 " ) ;
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 ;
2011-03-24 04:02:34 +01:00
/* skip if no keyframes to edit */
if ( ( fcu - > bezt = = NULL ) | | ( fcu - > totvert < 2 ) )
return ;
/* find the reference value from bones directly, which means that the user
* doesn ' t need to firstly keyframe the pose ( though this doesn ' t mean that
* 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 ;
2011-04-23 13:09:24 +02:00
2011-03-24 04:02:34 +01:00
/* find the first keyframe to start propagating from
* - 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
2012-05-08 22:18:33 +02:00
* - if starting before the starting frame , don ' t touch the key , as it may have had some valid
2011-03-24 04:02:34 +01:00
* values
2015-06-16 15:35:01 +02:00
* - 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 ) ;
if ( fcu - > bezt [ match ] . vec [ 1 ] [ 0 ] < startFrame )
i = match + 1 ;
else
i = match ;
}
else {
/* selected - start from first keyframe */
i = 0 ;
}
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 )
2011-03-31 02:45:52 +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 ;
/* stop on matching marker if there is one */
for ( ce = modeData . sel_markers . first ; ce ; ce = ce - > next ) {
2014-01-15 03:00:03 +01:00
if ( ce - > cfra = = iroundf ( bezt - > vec [ 1 ] [ 0 ] ) )
2011-03-31 03:37:42 +02:00
break ;
}
/* 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 ;
}
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 ;
/* 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 ) ;
2012-05-08 22:18:33 +02:00
Object * ob = BKE_object_pose_armature_get ( CTX_data_active_object ( C ) ) ;
bAction * act = ( ob & & ob - > adt ) ? ob - > adt - > action : NULL ;
2011-03-24 04:02:34 +01:00
ListBase pflinks = { NULL , NULL } ;
tPChanFCurveLink * pfl ;
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 " ) ;
/* sanity checks */
if ( ob = = NULL ) {
BKE_report ( op - > reports , RPT_ERROR , " No object to propagate poses for " ) ;
return OPERATOR_CANCELLED ;
}
if ( act = = NULL ) {
BKE_report ( op - > reports , RPT_ERROR , " No keyframed poses to propagate to " ) ;
return OPERATOR_CANCELLED ;
}
/* isolate F-Curves related to the selected bones */
poseAnim_mapping_get ( C , & pflinks , ob , act ) ;
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 " ) ;
}
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 ;
/* 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
*/
2011-03-31 03:37:42 +02:00
modeData . end_frame = pose_propagate_get_boneHoldEndFrame ( ob , pfl , ( float ) CFRA ) ;
2011-03-24 04:02:34 +01:00
}
/* go through propagating pose to keyframes, curve by curve */
2012-05-08 22:18:33 +02:00
for ( ld = pfl - > fcurves . first ; ld ; ld = ld - > next )
2011-03-31 03:37:42 +02:00
pose_propagate_fcurve ( op , ob , ( FCurve * ) ld - > data , ( float ) CFRA , modeData ) ;
2011-03-24 04:02:34 +01:00
}
/* free temp data */
poseAnim_mapping_free ( & pflinks ) ;
2011-03-31 03:37:42 +02:00
if ( mode = = POSE_PROPAGATE_SELECTED_MARKERS )
BLI_freelistN ( & modeData . sel_markers ) ;
2011-03-24 04:02:34 +01:00
/* updates + notifiers */
poseAnim_mapping_refresh ( C , scene , ob ) ;
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
{
2012-05-08 22:18:33 +02:00
static EnumPropertyItem terminate_items [ ] = {
2015-04-06 20:43:34 +02:00
{ POSE_PROPAGATE_SMART_HOLDS , " WHILE_HELD " , 0 , " While Held " ,
" Propagate pose to all keyframes after current frame that don't change (Default behavior) " } ,
{ POSE_PROPAGATE_NEXT_KEY , " NEXT_KEY " , 0 , " To Next Keyframe " ,
" Propagate pose to first keyframe following the current frame only " } ,
{ POSE_PROPAGATE_LAST_KEY , " LAST_KEY " , 0 , " To Last Keyframe " ,
" Propagate pose to the last keyframe only (i.e. making action cyclic) " } ,
{ POSE_PROPAGATE_BEFORE_FRAME , " BEFORE_FRAME " , 0 , " Before Frame " ,
" Propagate pose to all keyframes between current frame and 'Frame' property " } ,
{ POSE_PROPAGATE_BEFORE_END , " BEFORE_END " , 0 , " Before Last Keyframe " ,
" Propagate pose to all keyframes from current frame until no more are found " } ,
{ POSE_PROPAGATE_SELECTED_KEYS , " SELECTED_KEYS " , 0 , " On Selected Keyframes " ,
" Propagate pose to all selected keyframes " } ,
{ POSE_PROPAGATE_SELECTED_MARKERS , " SELECTED_MARKERS " , 0 , " On Selected Markers " ,
" Propagate pose to all keyframes occurring on frames with Scene Markers after the current frame " } ,
2011-03-24 04:02:34 +01:00
{ 0 , NULL , 0 , NULL , NULL } } ;
/* 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 " ;
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! */
2011-03-24 04:02:34 +01:00
/* flag */
2012-05-08 22:18:33 +02:00
ot - > flag = OPTYPE_REGISTER | OPTYPE_UNDO ;
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
}
/* **************************************************** */