4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
21 * All rights reserved.
23 * The Original Code is: all of this file.
25 * Contributor(s): Joshua Leung (full recode)
27 * ***** END GPL LICENSE BLOCK *****
30 /** \file blender/editors/animation/drivers.c
31 * \ingroup edanimation
38 #include "MEM_guardedalloc.h"
40 #include "BLI_blenlib.h"
41 #include "BLI_utildefines.h"
43 #include "DNA_anim_types.h"
44 #include "DNA_object_types.h"
45 #include "DNA_material_types.h"
46 #include "DNA_texture_types.h"
47 #include "DNA_screen_types.h"
48 #include "DNA_space_types.h"
50 #include "BKE_animsys.h"
51 #include "BKE_depsgraph.h"
52 #include "BKE_fcurve.h"
53 #include "BKE_context.h"
54 #include "BKE_report.h"
55 #include "BKE_material.h"
56 #include "BKE_texture.h"
58 #include "ED_keyframing.h"
60 #include "UI_interface.h"
65 #include "RNA_access.h"
66 #include "RNA_define.h"
68 #include "anim_intern.h"
71 void free_anim_drivers_copybuf (void);
73 /* ************************************************** */
74 /* Animation Data Validation */
76 /* Get (or add relevant data to be able to do so) F-Curve from the driver stack,
77 * for the given Animation Data block. This assumes that all the destinations are valid.
79 * - add: 0 - don't add anything if not found,
80 * 1 - add new Driver FCurve,
81 * -1 - add new Driver FCurve without driver stuff (for pasting)
83 FCurve *verify_driver_fcurve (ID *id, const char rna_path[], const int array_index, short add)
89 if ELEM(NULL, id, rna_path)
92 /* init animdata if none available yet */
93 adt= BKE_animdata_from_id(id);
94 if ((adt == NULL) && (add))
95 adt= BKE_id_add_animdata(id);
97 /* if still none (as not allowed to add, or ID doesn't have animdata for some reason) */
101 /* try to find f-curve matching for this setting
102 * - add if not found and allowed to add one
103 * TODO: add auto-grouping support? how this works will need to be resolved
105 fcu= list_find_fcurve(&adt->drivers, rna_path, array_index);
107 if ((fcu == NULL) && (add)) {
108 /* use default settings to make a F-Curve */
109 fcu= MEM_callocN(sizeof(FCurve), "FCurve");
111 fcu->flag = (FCURVE_VISIBLE|FCURVE_SELECTED);
113 /* store path - make copy, and store that */
114 fcu->rna_path= BLI_strdupn(rna_path, strlen(rna_path));
115 fcu->array_index= array_index;
117 /* if add is negative, don't init this data yet, since it will be filled in by the pasted driver */
119 /* add some new driver data */
120 fcu->driver= MEM_callocN(sizeof(ChannelDriver), "ChannelDriver");
122 /* add simple generator modifier for driver so that there is some visible representation */
123 add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR);
126 /* just add F-Curve to end of driver list */
127 BLI_addtail(&adt->drivers, fcu);
130 /* return the F-Curve */
134 /* ************************************************** */
135 /* Driver Management API */
137 /* Main Driver Management API calls:
138 * Add a new driver for the specified property on the given ID block
140 short ANIM_add_driver (ReportList *reports, ID *id, const char rna_path[], int array_index, short flag, int type)
142 PointerRNA id_ptr, ptr;
148 /* validate pointer first - exit if failure */
149 RNA_id_pointer_create(id, &id_ptr);
150 if ((RNA_path_resolve(&id_ptr, rna_path, &ptr, &prop) == 0) || (prop == NULL)) {
151 BKE_reportf(reports, RPT_ERROR,
152 "Could not add Driver, as RNA Path is invalid for the given ID (ID = %s, Path = %s)",
157 /* key entire array convenience method */
158 if (array_index == -1) {
159 array_index_max= RNA_property_array_length(&ptr, prop);
163 array_index_max= array_index;
165 /* maximum index should be greater than the start index */
166 if (array_index == array_index_max)
167 array_index_max += 1;
169 /* will only loop once unless the array index was -1 */
170 for (; array_index < array_index_max; array_index++) {
171 /* create F-Curve with Driver */
172 fcu= verify_driver_fcurve(id, rna_path, array_index, 1);
174 if (fcu && fcu->driver) {
175 ChannelDriver *driver= fcu->driver;
177 /* set the type of the driver */
180 /* creating drivers for buttons will create the driver(s) with type
181 * "scripted expression" so that their values won't be lost immediately,
182 * so here we copy those values over to the driver's expression
184 if (type == DRIVER_TYPE_PYTHON) {
185 PropertyType proptype= RNA_property_type(prop);
186 int array= RNA_property_array_length(&ptr, prop);
187 char *expression= driver->expression;
188 int val, maxlen= sizeof(driver->expression);
191 if (proptype == PROP_BOOLEAN) {
192 if (!array) val= RNA_property_boolean_get(&ptr, prop);
193 else val= RNA_property_boolean_get_index(&ptr, prop, array_index);
195 BLI_strncpy(expression, (val)? "True": "False", maxlen);
197 else if (proptype == PROP_INT) {
198 if (!array) val= RNA_property_int_get(&ptr, prop);
199 else val= RNA_property_int_get_index(&ptr, prop, array_index);
201 BLI_snprintf(expression, maxlen, "%d", val);
203 else if (proptype == PROP_FLOAT) {
204 if (!array) fval= RNA_property_float_get(&ptr, prop);
205 else fval= RNA_property_float_get_index(&ptr, prop, array_index);
207 BLI_snprintf(expression, maxlen, "%.3f", fval);
211 /* for easier setup of drivers from UI, a driver variable should be
212 * added if flag is set (UI calls only)
214 if (flag & CREATEDRIVER_WITH_DEFAULT_DVAR) {
215 /* assume that users will mostly want this to be of type "Transform Channel" too,
216 * since this allows the easiest setting up of common rig components
218 DriverVar *dvar = driver_add_new_variable(driver);
219 driver_change_variable_type(dvar, DVAR_TYPE_TRANSFORM_CHAN);
223 /* set the done status */
224 done += (fcu != NULL);
231 /* Main Driver Management API calls:
232 * Remove the driver for the specified property on the given ID block (if available)
234 short ANIM_remove_driver (ReportList *UNUSED(reports), ID *id, const char rna_path[], int array_index, short UNUSED(flag))
240 /* we don't check the validity of the path here yet, but it should be ok... */
241 adt= BKE_animdata_from_id(id);
244 if (array_index == -1) {
245 /* step through all drivers, removing all of those with the same base path */
246 FCurve *fcu_iter= adt->drivers.first;
248 while ((fcu = iter_step_fcurve(fcu_iter, rna_path)) != NULL) {
249 /* store the next fcurve for looping */
252 /* remove F-Curve from driver stack, then free it */
253 BLI_remlink(&adt->drivers, fcu);
256 /* done successfully */
261 /* find the matching driver and remove it only
262 * Note: here is one of the places where we don't want new F-Curve + Driver added!
263 * so 'add' var must be 0
265 fcu= verify_driver_fcurve(id, rna_path, array_index, 0);
267 BLI_remlink(&adt->drivers, fcu);
278 /* ************************************************** */
279 /* Driver Management API - Copy/Paste Drivers */
281 /* Copy/Paste Buffer for Driver Data... */
282 static FCurve *channeldriver_copypaste_buf = NULL;
284 /* This function frees any MEM_calloc'ed copy/paste buffer data */
285 // XXX find some header to put this in!
286 void free_anim_drivers_copybuf (void)
288 /* free the buffer F-Curve if it exists, as if it were just another F-Curve */
289 if (channeldriver_copypaste_buf)
290 free_fcurve(channeldriver_copypaste_buf);
291 channeldriver_copypaste_buf= NULL;
294 /* Checks if there is a driver in the copy/paste buffer */
295 short ANIM_driver_can_paste (void)
297 return (channeldriver_copypaste_buf != NULL);
300 /* ------------------- */
302 /* Main Driver Management API calls:
303 * Make a copy of the driver for the specified property on the given ID block
305 short ANIM_copy_driver (ReportList *reports, ID *id, const char rna_path[], int array_index, short UNUSED(flag))
307 PointerRNA id_ptr, ptr;
311 /* validate pointer first - exit if failure */
312 RNA_id_pointer_create(id, &id_ptr);
313 if ((RNA_path_resolve(&id_ptr, rna_path, &ptr, &prop) == 0) || (prop == NULL)) {
314 BKE_reportf(reports, RPT_ERROR,
315 "Could not find Driver to copy, as RNA Path is invalid for the given ID (ID = %s, Path = %s)",
320 /* try to get F-Curve with Driver */
321 fcu= verify_driver_fcurve(id, rna_path, array_index, 0);
323 /* clear copy/paste buffer first (for consistency with other copy/paste buffers) */
324 free_anim_drivers_copybuf();
326 /* copy this to the copy/paste buf if it exists */
327 if (fcu && fcu->driver) {
328 /* make copies of some info such as the rna_path, then clear this info from the F-Curve temporarily
329 * so that we don't end up wasting memory storing the path which won't get used ever...
331 char *tmp_path = fcu->rna_path;
334 /* make a copy of the F-Curve with */
335 channeldriver_copypaste_buf= copy_fcurve(fcu);
337 /* restore the path */
338 fcu->rna_path= tmp_path;
348 /* Main Driver Management API calls:
349 * Add a new driver for the specified property on the given ID block or replace an existing one
350 * with the driver + driver-curve data from the buffer
352 short ANIM_paste_driver (ReportList *reports, ID *id, const char rna_path[], int array_index, short UNUSED(flag))
354 PointerRNA id_ptr, ptr;
358 /* validate pointer first - exit if failure */
359 RNA_id_pointer_create(id, &id_ptr);
360 if ((RNA_path_resolve(&id_ptr, rna_path, &ptr, &prop) == 0) || (prop == NULL)) {
361 BKE_reportf(reports, RPT_ERROR,
362 "Could not paste Driver, as RNA Path is invalid for the given ID (ID = %s, Path = %s)",
367 /* if the buffer is empty, cannot paste... */
368 if (channeldriver_copypaste_buf == NULL) {
369 BKE_report(reports, RPT_ERROR, "Paste Driver: No Driver to paste.");
373 /* create Driver F-Curve, but without data which will be copied across... */
374 fcu= verify_driver_fcurve(id, rna_path, array_index, -1);
377 /* copy across the curve data from the buffer curve
378 * NOTE: this step needs care to not miss new settings
380 /* keyframes/samples */
381 fcu->bezt= MEM_dupallocN(channeldriver_copypaste_buf->bezt);
382 fcu->fpt= MEM_dupallocN(channeldriver_copypaste_buf->fpt);
383 fcu->totvert= channeldriver_copypaste_buf->totvert;
386 copy_fmodifiers(&fcu->modifiers, &channeldriver_copypaste_buf->modifiers);
388 /* flags - on a per-relevant-flag basis */
389 /* extrapolation mode */
390 fcu->extend= channeldriver_copypaste_buf->extend;
392 /* the 'juicy' stuff - the driver */
393 fcu->driver= fcurve_copy_driver(channeldriver_copypaste_buf->driver);
397 return (fcu != NULL);
400 /* ************************************************** */
401 /* UI-Button Interface */
403 /* Temporary wrapper for driver operators for buttons to make it easier to create
404 * such drivers by rerouting all paths through the active object instead so that
405 * they will get picked up by the dependency system.
407 * < C: context pointer - for getting active data
408 * <> ptr: RNA pointer for property's datablock. May be modified as result of path remapping.
409 * < prop: RNA definition of property to add for
411 * > returns: MEM_alloc'd string representing the path to the property from the given PointerRNA
413 static char *get_driver_path_hack (bContext *C, PointerRNA *ptr, PropertyRNA *prop)
415 ID *id = (ID *)ptr->id.data;
416 ScrArea *sa = CTX_wm_area(C);
418 /* get standard path which may be extended */
419 char *basepath = RNA_path_from_ID_to_property(ptr, prop);
420 char *path = basepath; /* in case no remapping is needed */
422 /* Remapping will only be performed in the Properties Editor, as only this
423 * restricts the subspace of options to the 'active' data (a manageable state)
425 // TODO: watch out for pinned context?
426 if ((sa) && (sa->spacetype == SPACE_BUTS)) {
427 Object *ob = CTX_data_active_object(C);
430 /* only id-types which can be remapped to go through objects should be considered */
431 switch (GS(id->name)) {
432 case ID_MA: /* materials */
434 Material *ma = give_current_material(ob, ob->actcol);
436 /* assumes: material will only be shown if it is active objects's active material it's ok */
438 /* create new path */
439 // TODO: use RNA path functions to construct instead?
440 path = BLI_sprintfN("material_slots[\"%s\"].material.%s",
441 ma->id.name+2, basepath);
449 case ID_TE: /* textures */
451 Material *ma = give_current_material(ob, ob->actcol);
452 Tex *tex = give_current_material_texture(ma);
454 /* assumes: texture will only be shown if it is active material's active texture it's ok */
455 if ((ID*)tex == id) {
456 /* create new path */
457 // TODO: use RNA path functions to construct step by step instead?
458 path = BLI_sprintfN("material_slots[\"%s\"].material.texture_slots[\"%s\"].texture.%s",
459 ma->id.name+2, tex->id.name+2, basepath);
468 /* fix RNA pointer, as we've now changed the ID root by changing the paths */
469 if (basepath != path) {
470 /* rebase provided pointer so that it starts from object... */
471 RNA_pointer_create(&ob->id, ptr->type, ptr->data, ptr);
476 /* the path should now have been corrected for use */
480 /* Add Driver Button Operator ------------------------ */
482 static int add_driver_button_exec (bContext *C, wmOperator *op)
484 PointerRNA ptr= {{NULL}};
485 PropertyRNA *prop= NULL;
487 int index, all= RNA_boolean_get(op->ptr, "all");
489 /* try to create driver using property retrieved from UI */
490 uiContextActiveProperty(C, &ptr, &prop, &index);
495 if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
496 char *path= get_driver_path_hack(C, &ptr, prop);
497 short flags = CREATEDRIVER_WITH_DEFAULT_DVAR;
500 success+= ANIM_add_driver(op->reports, ptr.id.data, path, index, flags, DRIVER_TYPE_PYTHON);
508 uiContextAnimUpdate(C);
510 DAG_ids_flush_update(CTX_data_main(C), 0);
512 WM_event_add_notifier(C, NC_ANIMATION|ND_FCURVES_ORDER, NULL); // XXX
515 return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
518 void ANIM_OT_driver_button_add (wmOperatorType *ot)
521 ot->name= "Add Driver";
522 ot->idname= "ANIM_OT_driver_button_add";
523 ot->description= "Add driver(s) for the property(s) connected represented by the highlighted button";
526 ot->exec= add_driver_button_exec;
527 //op->poll= ??? // TODO: need to have some animateable property to do this
530 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
533 RNA_def_boolean(ot->srna, "all", 1, "All", "Create drivers for all elements of the array.");
536 /* Remove Driver Button Operator ------------------------ */
538 static int remove_driver_button_exec (bContext *C, wmOperator *op)
540 PointerRNA ptr= {{NULL}};
541 PropertyRNA *prop= NULL;
543 int index, all= RNA_boolean_get(op->ptr, "all");
545 /* try to find driver using property retrieved from UI */
546 uiContextActiveProperty(C, &ptr, &prop, &index);
551 if (ptr.id.data && ptr.data && prop) {
552 char *path= get_driver_path_hack(C, &ptr, prop);
554 success= ANIM_remove_driver(op->reports, ptr.id.data, path, index, 0);
560 uiContextAnimUpdate(C);
562 DAG_ids_flush_update(CTX_data_main(C), 0);
564 WM_event_add_notifier(C, NC_ANIMATION|ND_FCURVES_ORDER, NULL); // XXX
567 return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
570 void ANIM_OT_driver_button_remove (wmOperatorType *ot)
573 ot->name= "Remove Driver";
574 ot->idname= "ANIM_OT_driver_button_remove";
575 ot->description= "Remove the driver(s) for the property(s) connected represented by the highlighted button";
578 ot->exec= remove_driver_button_exec;
579 //op->poll= ??? // TODO: need to have some driver to be able to do this...
582 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
585 RNA_def_boolean(ot->srna, "all", 1, "All", "Delete drivers for all elements of the array.");
588 /* Copy Driver Button Operator ------------------------ */
590 static int copy_driver_button_exec (bContext *C, wmOperator *op)
592 PointerRNA ptr= {{NULL}};
593 PropertyRNA *prop= NULL;
597 /* try to create driver using property retrieved from UI */
598 uiContextActiveProperty(C, &ptr, &prop, &index);
600 if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
601 char *path= get_driver_path_hack(C, &ptr, prop);
604 /* only copy the driver for the button that this was involved for */
605 success= ANIM_copy_driver(op->reports, ptr.id.data, path, index, 0);
607 uiContextAnimUpdate(C);
613 /* since we're just copying, we don't really need to do anything else...*/
614 return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
617 void ANIM_OT_copy_driver_button (wmOperatorType *ot)
620 ot->name= "Copy Driver";
621 ot->idname= "ANIM_OT_copy_driver_button";
622 ot->description= "Copy the driver for the highlighted button";
625 ot->exec= copy_driver_button_exec;
626 //op->poll= ??? // TODO: need to have some driver to be able to do this...
629 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
632 /* Paste Driver Button Operator ------------------------ */
634 static int paste_driver_button_exec (bContext *C, wmOperator *op)
636 PointerRNA ptr= {{NULL}};
637 PropertyRNA *prop= NULL;
641 /* try to create driver using property retrieved from UI */
642 uiContextActiveProperty(C, &ptr, &prop, &index);
644 if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
645 char *path= get_driver_path_hack(C, &ptr, prop);
648 /* only copy the driver for the button that this was involved for */
649 success= ANIM_paste_driver(op->reports, ptr.id.data, path, index, 0);
651 uiContextAnimUpdate(C);
657 /* since we're just copying, we don't really need to do anything else...*/
658 return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
661 void ANIM_OT_paste_driver_button (wmOperatorType *ot)
664 ot->name= "Paste Driver";
665 ot->idname= "ANIM_OT_paste_driver_button";
666 ot->description= "Paste the driver in the copy/paste buffer for the highlighted button";
669 ot->exec= paste_driver_button_exec;
670 //op->poll= ??? // TODO: need to have some driver to be able to do this...
673 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
676 /* ************************************************** */