/* create new expression for button (i.e. a "scripted driver"), if it can be created... */ bool ui_but_anim_expression_create(uiBut *but, const char *str) { bContext *C = but->block->evil_C; ID *id; FCurve *fcu; char *path; bool ok = false; /* button must have RNA-pointer to a numeric-capable property */ if (ELEM(NULL, but->rnapoin.data, but->rnaprop)) { if (G.debug & G_DEBUG) printf("ERROR: create expression failed - button has no RNA info attached\n"); return false; } if (RNA_property_array_check(but->rnaprop) != 0) { if (but->rnaindex == -1) { if (G.debug & G_DEBUG) printf("ERROR: create expression failed - can't create expression for entire array\n"); return false; } } /* make sure we have animdata for this */ /* FIXME: until materials can be handled by depsgraph, don't allow drivers to be created for them */ id = (ID *)but->rnapoin.id.data; if ((id == NULL) || (GS(id->name) == ID_MA) || (GS(id->name) == ID_TE)) { if (G.debug & G_DEBUG) printf("ERROR: create expression failed - invalid id-datablock for adding drivers (%p)\n", id); return false; } /* get path */ path = RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop); /* create driver */ fcu = verify_driver_fcurve(id, path, but->rnaindex, 1); if (fcu) { ChannelDriver *driver = fcu->driver; if (driver) { /* set type of driver */ driver->type = DRIVER_TYPE_PYTHON; /* set the expression */ /* TODO: need some way of identifying variables used */ BLI_strncpy_utf8(driver->expression, str, sizeof(driver->expression)); /* updates */ driver->flag |= DRIVER_FLAG_RECOMPILE; WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME, NULL); ok = true; } } MEM_freeN(path); return ok; }
/* Main Driver Management API calls: * 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)", 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... */ 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 */ /* keyframes/samples */ fcu->bezt = MEM_dupallocN(channeldriver_copypaste_buf->bezt); fcu->fpt = MEM_dupallocN(channeldriver_copypaste_buf->fpt); fcu->totvert = channeldriver_copypaste_buf->totvert; /* modifiers */ copy_fmodifiers(&fcu->modifiers, &channeldriver_copypaste_buf->modifiers); /* extrapolation mode */ fcu->extend = channeldriver_copypaste_buf->extend; /* the 'juicy' stuff - the driver */ fcu->driver = fcurve_copy_driver(channeldriver_copypaste_buf->driver); } /* done */ return (fcu != NULL); }
/* Main Driver Management API calls: * 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)", id->name, rna_path); return 0; } /* try to get F-Curve with Driver */ fcu = verify_driver_fcurve(id, rna_path, array_index, 0); /* clear copy/paste buffer first (for consistency with other copy/paste buffers) */ ANIM_drivers_copybuf_free(); /* 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; fcu->rna_path = NULL; /* make a copy of the F-Curve with */ channeldriver_copypaste_buf = copy_fcurve(fcu); /* restore the path */ fcu->rna_path = tmp_path; /* copied... */ return 1; } /* done */ return 0; }
/* Main Driver Management API calls: * 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... */ 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 */ FCurve *fcu_iter = adt->drivers.first; while ((fcu = iter_step_fcurve(fcu_iter, rna_path)) != NULL) { /* store the next fcurve for looping */ 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! * so 'add' var must be 0 */ fcu = verify_driver_fcurve(id, rna_path, array_index, 0); if (fcu) { BLI_remlink(&adt->drivers, fcu); free_fcurve(fcu); success = true; } } } return success; }
/* Main Driver Management API calls: * 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)", id->name, rna_path); return 0; } /* key entire array convenience method */ if (array_index == -1) { array_index_max = RNA_property_array_length(&ptr, prop); array_index = 0; } else 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) { ChannelDriver *driver = fcu->driver; /* set the type of the driver */ 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) { 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) { if (!array) val = RNA_property_boolean_get(&ptr, prop); else val = RNA_property_boolean_get_index(&ptr, prop, array_index); BLI_strncpy(expression, (val) ? "True" : "False", maxlen); } else if (proptype == PROP_INT) { 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) { 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; }
/* Helper for ANIM_add_driver_with_target - Adds the actual driver */ static int add_driver_with_target(ReportList *UNUSED(reports), ID *dst_id, const char dst_path[], int dst_index, ID *src_id, const char src_path[], int src_index, PointerRNA *dst_ptr, PropertyRNA *dst_prop, PointerRNA *src_ptr, PropertyRNA *src_prop, short flag, int driver_type) { FCurve *fcu; short add_mode = (flag & CREATEDRIVER_WITH_FMODIFIER) ? 2 : 1; const char *prop_name = RNA_property_identifier(src_prop); /* Create F-Curve with Driver */ fcu = verify_driver_fcurve(dst_id, dst_path, dst_index, add_mode); if (fcu && fcu->driver) { ChannelDriver *driver = fcu->driver; DriverVar *dvar; /* Set the type of the driver */ driver->type = driver_type; /* Set driver expression, so that the driver works out of the box * * The following checks define a bit of "autodetection magic" we use * to ensure that the drivers will behave as expected out of the box * when faced with properties with different units. */ /* XXX: if we have N-1 mapping, should we include all those in the expression? */ if ((RNA_property_unit(dst_prop) == PROP_UNIT_ROTATION) && (RNA_property_unit(src_prop) != PROP_UNIT_ROTATION)) { /* Rotation Destination: normal -> radians, so convert src to radians * (However, if both input and output is a rotation, don't apply such corrections) */ BLI_strncpy(driver->expression, "radians(var)", sizeof(driver->expression)); } else if ((RNA_property_unit(src_prop) == PROP_UNIT_ROTATION) && (RNA_property_unit(dst_prop) != PROP_UNIT_ROTATION)) { /* Rotation Source: radians -> normal, so convert src to degrees * (However, if both input and output is a rotation, don't apply such corrections) */ BLI_strncpy(driver->expression, "degrees(var)", sizeof(driver->expression)); } else { /* Just a normal property without any unit problems */ BLI_strncpy(driver->expression, "var", sizeof(driver->expression)); } /* Create a driver variable for the target * - For transform properties, we want to automatically use "transform channel" instead * (The only issue is with quat rotations vs euler channels...) * - To avoid problems with transform properties depending on the final transform that they * control (thus creating pseudo-cycles - see T48734), we don't use transform channels * when both the source and destinations are in same places. */ dvar = driver_add_new_variable(driver); if (ELEM(src_ptr->type, &RNA_Object, &RNA_PoseBone) && (STREQ(prop_name, "location") || STREQ(prop_name, "scale") || STRPREFIX(prop_name, "rotation_")) && (src_ptr->data != dst_ptr->data)) { /* Transform Channel */ DriverTarget *dtar; driver_change_variable_type(dvar, DVAR_TYPE_TRANSFORM_CHAN); dtar = &dvar->targets[0]; /* Bone or Object target? */ dtar->id = src_id; dtar->idtype = GS(src_id->name); if (src_ptr->type == &RNA_PoseBone) { RNA_string_get(src_ptr, "name", dtar->pchan_name); } /* Transform channel depends on type */ if (STREQ(prop_name, "location")) { if (src_index == 2) { dtar->transChan = DTAR_TRANSCHAN_LOCZ; } else if (src_index == 1) { dtar->transChan = DTAR_TRANSCHAN_LOCY; } else { dtar->transChan = DTAR_TRANSCHAN_LOCX; } } else if (STREQ(prop_name, "scale")) { if (src_index == 2) { dtar->transChan = DTAR_TRANSCHAN_SCALEZ; } else if (src_index == 1) { dtar->transChan = DTAR_TRANSCHAN_SCALEY; } else { dtar->transChan = DTAR_TRANSCHAN_SCALEX; } } else { /* XXX: With quaternions and axis-angle, this mapping might not be correct... * But since those have 4 elements instead, there's not much we can do */ if (src_index == 2) { dtar->transChan = DTAR_TRANSCHAN_ROTZ; } else if (src_index == 1) { dtar->transChan = DTAR_TRANSCHAN_ROTY; } else { dtar->transChan = DTAR_TRANSCHAN_ROTX; } } } else { /* Single RNA Property */ DriverTarget *dtar = &dvar->targets[0]; /* ID is as-is */ dtar->id = src_id; dtar->idtype = GS(src_id->name); /* Need to make a copy of the path (or build one with array index built in) */ if (RNA_property_array_check(src_prop)) { dtar->rna_path = BLI_sprintfN("%s[%d]", src_path, src_index); } else { dtar->rna_path = BLI_strdup(src_path); } } } /* set the done status */ return (fcu != NULL); }
/* create new expression for button (i.e. a "scripted driver"), if it can be created... */ int ui_but_anim_expression_create(uiBut *but, const char *str) { bContext *C = but->block->evil_C; ID *id; FCurve *fcu; char *path; short ok=0; /* button must have RNA-pointer to a numeric-capable property */ if (ELEM(NULL, but->rnapoin.data, but->rnaprop)) { if (G.f & G_DEBUG) printf("ERROR: create expression failed - button has no RNA info attached\n"); return 0; } /* make sure we have animdata for this */ // FIXME: until materials can be handled by depsgraph, don't allow drivers to be created for them id = (ID *)but->rnapoin.id.data; if ((id == NULL) || (GS(id->name)==ID_MA) || (GS(id->name)==ID_TE)) { if (G.f & G_DEBUG) printf("ERROR: create expression failed - invalid id-datablock for adding drivers (%p)\n", id); return 0; } /* get path */ path = RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop); /* create driver */ fcu = verify_driver_fcurve(id, path, but->rnaindex, 1); if (fcu) { ChannelDriver *driver= fcu->driver; if (driver) { /* set type of driver */ driver->type = DRIVER_TYPE_PYTHON; /* set the expression */ // TODO: need some way of identifying variables used BLI_strncpy(driver->expression, str, sizeof(driver->expression)); /* FIXME: for now, assume that * - for expressions, users are likely to be using "frame" -> current frame" as a variable * - driver_add_new_variable() adds a single-prop variable by default */ { DriverVar *dvar; DriverTarget *dtar; dvar = driver_add_new_variable(driver); BLI_strncpy(dvar->name, "frame", sizeof(dvar->name)); dtar = &dvar->targets[0]; dtar->id = (ID *)CTX_data_scene(C); // XXX: should we check that C is valid first? dtar->rna_path = BLI_sprintfN("frame_current"); } /* updates */ driver->flag |= DRIVER_FLAG_RECOMPILE; WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME, NULL); } } MEM_freeN(path); return ok; }