This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/animation/drivers.c

622 lines
18 KiB
C
Raw Normal View History

/*
* ***** 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 13:34:04 +00:00
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Joshua Leung (full recode)
*
* ***** END GPL LICENSE BLOCK *****
*/
2011-02-27 20:29:51 +00:00
/** \file blender/editors/animation/drivers.c
* \ingroup edanimation
*/
#include <stdio.h>
#include <string.h>
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "DNA_anim_types.h"
#include "DNA_texture_types.h"
#include "BKE_animsys.h"
#include "BKE_depsgraph.h"
#include "BKE_fcurve.h"
#include "BKE_context.h"
#include "BKE_report.h"
#include "ED_keyframing.h"
#include "UI_interface.h"
#include "WM_api.h"
#include "WM_types.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "anim_intern.h"
/* called by WM */
void free_anim_drivers_copybuf(void);
/* ************************************************** */
/* Animation Data Validation */
/* Get (or add relevant data to be able to do so) F-Curve from the driver stack,
* for the given Animation Data block. This assumes that all the destinations are valid.
*
* - add: 0 - don't add anything if not found,
* 1 - add new Driver FCurve (with keyframes for visual tweaking),
2013-10-31 14:10:01 +00:00
* 2 - add new Driver FCurve (with generator, for script backwards compatibility)
* -1 - add new Driver FCurve without driver stuff (for pasting)
*/
2012-05-08 15:30:00 +00:00
FCurve *verify_driver_fcurve(ID *id, const char rna_path[], const int array_index, short add)
{
AnimData *adt;
FCurve *fcu;
/* sanity checks */
if (ELEM(NULL, id, rna_path))
return NULL;
/* init animdata if none available yet */
2012-05-08 15:30:00 +00:00
adt = BKE_animdata_from_id(id);
if ((adt == NULL) && (add))
2015-04-04 15:13:56 +11:00
adt = BKE_animdata_add_id(id);
if (adt == NULL) {
/* if still none (as not allowed to add, or ID doesn't have animdata for some reason) */
return NULL;
}
/* try to find f-curve matching for this setting
* - add if not found and allowed to add one
* TODO: add auto-grouping support? how this works will need to be resolved
*/
2012-05-08 15:30:00 +00:00
fcu = list_find_fcurve(&adt->drivers, rna_path, array_index);
if ((fcu == NULL) && (add)) {
/* use default settings to make a F-Curve */
2012-05-08 15:30:00 +00:00
fcu = MEM_callocN(sizeof(FCurve), "FCurve");
2012-05-08 15:30:00 +00:00
fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED);
/* store path - make copy, and store that */
fcu->rna_path = BLI_strdup(rna_path);
2012-05-08 15:30:00 +00:00
fcu->array_index = array_index;
/* if add is negative, don't init this data yet, since it will be filled in by the pasted driver */
if (add > 0) {
BezTriple *bezt;
size_t i;
/* add some new driver data */
2012-05-08 15:30:00 +00:00
fcu->driver = MEM_callocN(sizeof(ChannelDriver), "ChannelDriver");
fcu->driver->flag |= DRIVER_FLAG_SHOWDEBUG;
/* F-Modifier or Keyframes? */
// FIXME: replace these magic numbers with defines
if (add == 2) {
2013-10-31 14:10:01 +00:00
/* Python API Backwards compatibility hack:
* Create FModifier so that old scripts won't break
* for now before 2.7 series -- (September 4, 2013)
*/
add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR);
}
else {
/* add 2 keyframes so that user has something to work with
* - These are configured to 0,0 and 1,1 to give a 1-1 mapping
* which can be easily tweaked from there.
*/
insert_vert_fcurve(fcu, 0.0f, 0.0f, INSERTKEY_FAST);
insert_vert_fcurve(fcu, 1.0f, 1.0f, INSERTKEY_FAST);
/* configure this curve to extrapolate */
for (i = 0, bezt = fcu->bezt; (i < fcu->totvert) && bezt; i++, bezt++) {
bezt->h1 = bezt->h2 = HD_VECT;
}
fcu->extend = FCURVE_EXTRAPOLATE_LINEAR;
calchandles_fcurve(fcu);
}
}
/* just add F-Curve to end of driver list */
BLI_addtail(&adt->drivers, fcu);
}
/* return the F-Curve */
return fcu;
}
/* ************************************************** */
/* Driver Management API */
/* Main Driver Management API calls:
2012-05-08 15:30:00 +00:00
* Add a new driver for the specified property on the given ID block
*/
int ANIM_add_driver(ReportList *reports, ID *id, const char rna_path[], int array_index, short flag, int type)
{
PointerRNA id_ptr, ptr;
PropertyRNA *prop;
FCurve *fcu;
int array_index_max;
int done_tot = 0;
/* validate pointer first - exit if failure */
RNA_id_pointer_create(id, &id_ptr);
if (RNA_path_resolve_property(&id_ptr, rna_path, &ptr, &prop) == false) {
BKE_reportf(reports, RPT_ERROR,
"Could not add driver, as RNA path is invalid for the given ID (ID = %s, path = %s)",
2012-05-08 15:30:00 +00:00
id->name, rna_path);
return 0;
}
/* key entire array convenience method */
if (array_index == -1) {
2012-05-08 15:30:00 +00:00
array_index_max = RNA_property_array_length(&ptr, prop);
array_index = 0;
}
else
2012-05-08 15:30:00 +00:00
array_index_max = array_index;
/* maximum index should be greater than the start index */
if (array_index == array_index_max)
array_index_max += 1;
/* will only loop once unless the array index was -1 */
for (; array_index < array_index_max; array_index++) {
short add_mode = (flag & CREATEDRIVER_WITH_FMODIFIER) ? 2 : 1;
/* create F-Curve with Driver */
fcu = verify_driver_fcurve(id, rna_path, array_index, add_mode);
if (fcu && fcu->driver) {
2012-05-08 15:30:00 +00:00
ChannelDriver *driver = fcu->driver;
/* set the type of the driver */
2012-05-08 15:30:00 +00:00
driver->type = type;
/* creating drivers for buttons will create the driver(s) with type
* "scripted expression" so that their values won't be lost immediately,
* so here we copy those values over to the driver's expression
*/
if (type == DRIVER_TYPE_PYTHON) {
2012-05-08 15:30:00 +00:00
PropertyType proptype = RNA_property_type(prop);
int array = RNA_property_array_length(&ptr, prop);
char *expression = driver->expression;
int val, maxlen = sizeof(driver->expression);
float fval;
if (proptype == PROP_BOOLEAN) {
2012-05-08 15:30:00 +00:00
if (!array) val = RNA_property_boolean_get(&ptr, prop);
else val = RNA_property_boolean_get_index(&ptr, prop, array_index);
2012-05-08 15:30:00 +00:00
BLI_strncpy(expression, (val) ? "True" : "False", maxlen);
}
else if (proptype == PROP_INT) {
2012-05-08 15:30:00 +00:00
if (!array) val = RNA_property_int_get(&ptr, prop);
else val = RNA_property_int_get_index(&ptr, prop, array_index);
BLI_snprintf(expression, maxlen, "%d", val);
}
else if (proptype == PROP_FLOAT) {
2012-05-08 15:30:00 +00:00
if (!array) fval = RNA_property_float_get(&ptr, prop);
else fval = RNA_property_float_get_index(&ptr, prop, array_index);
BLI_snprintf(expression, maxlen, "%.3f", fval);
}
}
/* for easier setup of drivers from UI, a driver variable should be
* added if flag is set (UI calls only)
*/
if (flag & CREATEDRIVER_WITH_DEFAULT_DVAR) {
/* assume that users will mostly want this to be of type "Transform Channel" too,
* since this allows the easiest setting up of common rig components
*/
DriverVar *dvar = driver_add_new_variable(driver);
driver_change_variable_type(dvar, DVAR_TYPE_TRANSFORM_CHAN);
}
}
/* set the done status */
done_tot += (fcu != NULL);
}
/* done */
return done_tot;
}
/* Main Driver Management API calls:
2012-05-08 15:30:00 +00:00
* Remove the driver for the specified property on the given ID block (if available)
*/
bool ANIM_remove_driver(ReportList *UNUSED(reports), ID *id, const char rna_path[], int array_index, short UNUSED(flag))
{
AnimData *adt;
FCurve *fcu;
bool success = false;
/* we don't check the validity of the path here yet, but it should be ok... */
2012-05-08 15:30:00 +00:00
adt = BKE_animdata_from_id(id);
if (adt) {
if (array_index == -1) {
/* step through all drivers, removing all of those with the same base path */
2012-05-08 15:30:00 +00:00
FCurve *fcu_iter = adt->drivers.first;
while ((fcu = iter_step_fcurve(fcu_iter, rna_path)) != NULL) {
/* store the next fcurve for looping */
2012-05-08 15:30:00 +00:00
fcu_iter = fcu->next;
/* remove F-Curve from driver stack, then free it */
BLI_remlink(&adt->drivers, fcu);
free_fcurve(fcu);
/* done successfully */
success = true;
}
}
else {
/* find the matching driver and remove it only
* Note: here is one of the places where we don't want new F-Curve + Driver added!
2012-05-08 15:30:00 +00:00
* so 'add' var must be 0
*/
2012-05-08 15:30:00 +00:00
fcu = verify_driver_fcurve(id, rna_path, array_index, 0);
if (fcu) {
BLI_remlink(&adt->drivers, fcu);
free_fcurve(fcu);
success = true;
}
}
}
return success;
}
/* ************************************************** */
/* Driver Management API - Copy/Paste Drivers */
/* Copy/Paste Buffer for Driver Data... */
static FCurve *channeldriver_copypaste_buf = NULL;
/* This function frees any MEM_calloc'ed copy/paste buffer data */
// XXX find some header to put this in!
void free_anim_drivers_copybuf(void)
{
/* free the buffer F-Curve if it exists, as if it were just another F-Curve */
if (channeldriver_copypaste_buf)
free_fcurve(channeldriver_copypaste_buf);
2012-05-08 15:30:00 +00:00
channeldriver_copypaste_buf = NULL;
}
/* Checks if there is a driver in the copy/paste buffer */
bool ANIM_driver_can_paste(void)
{
return (channeldriver_copypaste_buf != NULL);
}
/* ------------------- */
/* Main Driver Management API calls:
2012-05-08 15:30:00 +00:00
* Make a copy of the driver for the specified property on the given ID block
*/
bool ANIM_copy_driver(ReportList *reports, ID *id, const char rna_path[], int array_index, short UNUSED(flag))
{
PointerRNA id_ptr, ptr;
PropertyRNA *prop;
FCurve *fcu;
/* validate pointer first - exit if failure */
RNA_id_pointer_create(id, &id_ptr);
if (RNA_path_resolve_property(&id_ptr, rna_path, &ptr, &prop) == false) {
BKE_reportf(reports, RPT_ERROR,
"Could not find driver to copy, as RNA path is invalid for the given ID (ID = %s, path = %s)",
2012-05-08 15:30:00 +00:00
id->name, rna_path);
return 0;
}
/* try to get F-Curve with Driver */
2012-05-08 15:30:00 +00:00
fcu = verify_driver_fcurve(id, rna_path, array_index, 0);
/* clear copy/paste buffer first (for consistency with other copy/paste buffers) */
free_anim_drivers_copybuf();
/* copy this to the copy/paste buf if it exists */
if (fcu && fcu->driver) {
/* make copies of some info such as the rna_path, then clear this info from the F-Curve temporarily
* so that we don't end up wasting memory storing the path which won't get used ever...
*/
char *tmp_path = fcu->rna_path;
2012-05-08 15:30:00 +00:00
fcu->rna_path = NULL;
/* make a copy of the F-Curve with */
2012-05-08 15:30:00 +00:00
channeldriver_copypaste_buf = copy_fcurve(fcu);
/* restore the path */
2012-05-08 15:30:00 +00:00
fcu->rna_path = tmp_path;
/* copied... */
return 1;
}
/* done */
return 0;
}
/* Main Driver Management API calls:
2012-05-08 15:30:00 +00:00
* Add a new driver for the specified property on the given ID block or replace an existing one
* with the driver + driver-curve data from the buffer
*/
bool ANIM_paste_driver(ReportList *reports, ID *id, const char rna_path[], int array_index, short UNUSED(flag))
{
PointerRNA id_ptr, ptr;
PropertyRNA *prop;
FCurve *fcu;
/* validate pointer first - exit if failure */
RNA_id_pointer_create(id, &id_ptr);
if (RNA_path_resolve_property(&id_ptr, rna_path, &ptr, &prop) == false) {
BKE_reportf(reports, RPT_ERROR,
"Could not paste driver, as RNA path is invalid for the given ID (ID = %s, path = %s)",
2012-05-08 15:30:00 +00:00
id->name, rna_path);
return 0;
}
/* if the buffer is empty, cannot paste... */
if (channeldriver_copypaste_buf == NULL) {
BKE_report(reports, RPT_ERROR, "Paste driver: no driver to paste");
return 0;
}
/* create Driver F-Curve, but without data which will be copied across... */
2012-05-08 15:30:00 +00:00
fcu = verify_driver_fcurve(id, rna_path, array_index, -1);
if (fcu) {
/* copy across the curve data from the buffer curve
* NOTE: this step needs care to not miss new settings
*/
2012-05-08 15:30:00 +00:00
/* keyframes/samples */
fcu->bezt = MEM_dupallocN(channeldriver_copypaste_buf->bezt);
fcu->fpt = MEM_dupallocN(channeldriver_copypaste_buf->fpt);
fcu->totvert = channeldriver_copypaste_buf->totvert;
2012-05-08 15:30:00 +00:00
/* modifiers */
copy_fmodifiers(&fcu->modifiers, &channeldriver_copypaste_buf->modifiers);
2012-05-08 15:30:00 +00:00
/* extrapolation mode */
fcu->extend = channeldriver_copypaste_buf->extend;
2012-05-08 15:30:00 +00:00
/* the 'juicy' stuff - the driver */
fcu->driver = fcurve_copy_driver(channeldriver_copypaste_buf->driver);
}
/* done */
return (fcu != NULL);
}
/* ************************************************** */
/* UI-Button Interface */
/* Add Driver Button Operator ------------------------ */
2012-05-08 15:30:00 +00:00
static int add_driver_button_exec(bContext *C, wmOperator *op)
{
2012-05-08 15:30:00 +00:00
PointerRNA ptr = {{NULL}};
PropertyRNA *prop = NULL;
int success = 0;
2014-02-03 18:55:59 +11:00
int index;
const bool all = RNA_boolean_get(op->ptr, "all");
/* try to create driver using property retrieved from UI */
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
if (all)
2012-05-08 15:30:00 +00:00
index = -1;
if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL);
short flags = CREATEDRIVER_WITH_DEFAULT_DVAR;
if (path) {
2012-05-08 15:30:00 +00:00
success += ANIM_add_driver(op->reports, ptr.id.data, path, index, flags, DRIVER_TYPE_PYTHON);
MEM_freeN(path);
}
}
if (success) {
/* send updates */
UI_context_update_anim_flag(C);
DAG_relations_tag_update(CTX_data_main(C));
2012-05-08 15:30:00 +00:00
WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); // XXX
}
2012-05-08 15:30:00 +00:00
return (success) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
void ANIM_OT_driver_button_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Driver";
ot->idname = "ANIM_OT_driver_button_add";
ot->description = "Add driver(s) for the property(s) connected represented by the highlighted button";
/* callbacks */
ot->exec = add_driver_button_exec;
2012-05-27 19:40:36 +00:00
//op->poll = ??? // TODO: need to have some animatable property to do this
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
/* properties */
RNA_def_boolean(ot->srna, "all", 1, "All", "Create drivers for all elements of the array");
}
/* Remove Driver Button Operator ------------------------ */
2012-05-08 15:30:00 +00:00
static int remove_driver_button_exec(bContext *C, wmOperator *op)
{
2012-05-08 15:30:00 +00:00
PointerRNA ptr = {{NULL}};
PropertyRNA *prop = NULL;
short success = 0;
2014-02-03 18:55:59 +11:00
int index;
const bool all = RNA_boolean_get(op->ptr, "all");
/* try to find driver using property retrieved from UI */
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
if (all)
2012-05-08 15:30:00 +00:00
index = -1;
if (ptr.id.data && ptr.data && prop) {
char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL);
if (path) {
success = ANIM_remove_driver(op->reports, ptr.id.data, path, index, 0);
MEM_freeN(path);
}
}
if (success) {
/* send updates */
UI_context_update_anim_flag(C);
DAG_relations_tag_update(CTX_data_main(C));
2012-05-08 15:30:00 +00:00
WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); // XXX
}
2012-05-08 15:30:00 +00:00
return (success) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
void ANIM_OT_driver_button_remove(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove Driver";
ot->idname = "ANIM_OT_driver_button_remove";
ot->description = "Remove the driver(s) for the property(s) connected represented by the highlighted button";
/* callbacks */
ot->exec = remove_driver_button_exec;
2012-05-27 19:40:36 +00:00
//op->poll = ??? // TODO: need to have some driver to be able to do this...
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
/* properties */
RNA_def_boolean(ot->srna, "all", 1, "All", "Delete drivers for all elements of the array");
}
/* Copy Driver Button Operator ------------------------ */
2012-05-08 15:30:00 +00:00
static int copy_driver_button_exec(bContext *C, wmOperator *op)
{
2012-05-08 15:30:00 +00:00
PointerRNA ptr = {{NULL}};
PropertyRNA *prop = NULL;
short success = 0;
int index;
/* try to create driver using property retrieved from UI */
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL);
if (path) {
/* only copy the driver for the button that this was involved for */
2012-05-08 15:30:00 +00:00
success = ANIM_copy_driver(op->reports, ptr.id.data, path, index, 0);
UI_context_update_anim_flag(C);
MEM_freeN(path);
}
}
/* since we're just copying, we don't really need to do anything else...*/
2012-05-08 15:30:00 +00:00
return (success) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
void ANIM_OT_copy_driver_button(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Copy Driver";
ot->idname = "ANIM_OT_copy_driver_button";
ot->description = "Copy the driver for the highlighted button";
/* callbacks */
ot->exec = copy_driver_button_exec;
2012-05-27 19:40:36 +00:00
//op->poll = ??? // TODO: need to have some driver to be able to do this...
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
}
/* Paste Driver Button Operator ------------------------ */
2012-05-08 15:30:00 +00:00
static int paste_driver_button_exec(bContext *C, wmOperator *op)
{
2012-05-08 15:30:00 +00:00
PointerRNA ptr = {{NULL}};
PropertyRNA *prop = NULL;
short success = 0;
int index;
/* try to create driver using property retrieved from UI */
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL);
if (path) {
/* only copy the driver for the button that this was involved for */
2012-05-08 15:30:00 +00:00
success = ANIM_paste_driver(op->reports, ptr.id.data, path, index, 0);
UI_context_update_anim_flag(C);
MEM_freeN(path);
}
}
/* since we're just copying, we don't really need to do anything else...*/
2012-05-08 15:30:00 +00:00
return (success) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
void ANIM_OT_paste_driver_button(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Paste Driver";
ot->idname = "ANIM_OT_paste_driver_button";
ot->description = "Paste the driver in the copy/paste buffer for the highlighted button";
/* callbacks */
ot->exec = paste_driver_button_exec;
2012-05-27 19:40:36 +00:00
//op->poll = ??? // TODO: need to have some driver to be able to do this...
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
}
/* ************************************************** */