static int node_animation_properties(bNodeTree *ntree, bNode *node) { bNodeSocket *sock; const ListBase *lb; Link *link; PointerRNA ptr; PropertyRNA *prop; /* check to see if any of the node's properties have fcurves */ RNA_pointer_create((ID *)ntree, &RNA_Node, node, &ptr); lb = RNA_struct_type_properties(ptr.type); for (link = lb->first; link; link = link->next) { int driven, len = 1, index; prop = (PropertyRNA *)link; if (RNA_property_array_check(prop)) len = RNA_property_array_length(&ptr, prop); for (index = 0; index < len; index++) { if (rna_get_fcurve(&ptr, prop, index, NULL, &driven)) { nodeUpdate(ntree, node); return 1; } } } /* now check node sockets */ for (sock = node->inputs.first; sock; sock = sock->next) { int driven, len = 1, index; RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr); prop = RNA_struct_find_property(&ptr, "default_value"); if (prop) { if (RNA_property_array_check(prop)) len = RNA_property_array_length(&ptr, prop); for (index = 0; index < len; index++) { if (rna_get_fcurve(&ptr, prop, index, NULL, &driven)) { nodeUpdate(ntree, node); return 1; } } } } return 0; }
static bool depthdropper_poll(bContext *C) { PointerRNA ptr; PropertyRNA *prop; int index_dummy; uiBut *but; /* check if there's an active button taking depth value */ if ((CTX_wm_window(C) != NULL) && (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) && (but->type == UI_BTYPE_NUM) && (prop != NULL)) { if ((RNA_property_type(prop) == PROP_FLOAT) && (RNA_property_subtype(prop) & PROP_UNIT_LENGTH) && (RNA_property_array_check(prop) == false)) { return 1; } } else { RegionView3D *rv3d = CTX_wm_region_view3d(C); if (rv3d && rv3d->persp == RV3D_CAMOB) { View3D *v3d = CTX_wm_view3d(C); if (v3d->camera && v3d->camera->data && !ID_IS_LINKED(v3d->camera->data)) { return 1; } } } return 0; }
/* 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; }
/* get reference value from F-Curve using RNA */ static bool pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value) { PointerRNA id_ptr, ptr; PropertyRNA *prop; bool found = false; /* base pointer is always the object -> id_ptr */ RNA_id_pointer_create(&ob->id, &id_ptr); /* resolve the property... */ if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) { if (RNA_property_array_check(prop)) { /* array */ if (fcu->array_index < RNA_property_array_length(&ptr, prop)) { found = true; switch (RNA_property_type(prop)) { case PROP_BOOLEAN: *value = (float)RNA_property_boolean_get_index(&ptr, prop, fcu->array_index); break; case PROP_INT: *value = (float)RNA_property_int_get_index(&ptr, prop, fcu->array_index); break; case PROP_FLOAT: *value = RNA_property_float_get_index(&ptr, prop, fcu->array_index); break; default: found = false; break; } } } else { /* not an array */ found = true; switch (RNA_property_type(prop)) { case PROP_BOOLEAN: *value = (float)RNA_property_boolean_get(&ptr, prop); break; case PROP_INT: *value = (float)RNA_property_int_get(&ptr, prop); break; case PROP_ENUM: *value = (float)RNA_property_enum_get(&ptr, prop); break; case PROP_FLOAT: *value = RNA_property_float_get(&ptr, prop); break; default: found = false; break; } } } return found; }
/** * \a check_prop callback filters functions to avoid drawing certain properties, * in cases where PROP_HIDDEN flag can't be used for a property. */ int uiDefAutoButsRNA(uiLayout *layout, PointerRNA *ptr, int (*check_prop)(PointerRNA *, PropertyRNA *), const char label_align) { uiLayout *split, *col; int flag; const char *name; int tot = 0; assert(ELEM3(label_align, '\0', 'H', 'V')); RNA_STRUCT_BEGIN (ptr, prop) { flag = RNA_property_flag(prop); if (flag & PROP_HIDDEN || (check_prop && check_prop(ptr, prop) == FALSE)) continue; if (label_align != '\0') { PropertyType type = RNA_property_type(prop); int is_boolean = (type == PROP_BOOLEAN && !RNA_property_array_check(prop)); name = RNA_property_ui_name(prop); if (label_align == 'V') { col = uiLayoutColumn(layout, TRUE); if (!is_boolean) uiItemL(col, name, ICON_NONE); } else if (label_align == 'H') { split = uiLayoutSplit(layout, 0.5f, FALSE); col = uiLayoutColumn(split, FALSE); uiItemL(col, (is_boolean) ? "" : name, ICON_NONE); col = uiLayoutColumn(split, FALSE); } else { col = NULL; } /* may meed to add more cases here. * don't override enum flag names */ /* name is shown above, empty name for button below */ name = (flag & PROP_ENUM_FLAG || is_boolean) ? NULL : ""; } else { col = layout; name = NULL; /* no smart label alignment, show default name with button */ } uiItemFullR(col, ptr, prop, -1, 0, 0, name, ICON_NONE); tot++; }
static bool assign_default_button_poll(bContext *C) { PointerRNA ptr; PropertyRNA *prop; int index; UI_context_active_but_prop_get(C, &ptr, &prop, &index); if (ptr.data && prop && RNA_property_editable(&ptr, prop)) { PropertyType type = RNA_property_type(prop); return RNA_property_is_idprop(prop) && !RNA_property_array_check(prop) && ELEM(type, PROP_INT, PROP_FLOAT); } return false; }
/* Filtering callback for driver mapping types enum */ static const EnumPropertyItem *driver_mapping_type_itemsf(bContext *C, PointerRNA *UNUSED(owner_ptr), PropertyRNA *UNUSED(owner_prop), bool *r_free) { EnumPropertyItem *input = prop_driver_create_mapping_types; EnumPropertyItem *item = NULL; PointerRNA ptr = {{NULL}}; PropertyRNA *prop = NULL; int index; int totitem = 0; if (!C) { /* needed for docs */ return prop_driver_create_mapping_types; } UI_context_active_but_prop_get(C, &ptr, &prop, &index); if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { const bool is_array = RNA_property_array_check(prop); while (input->identifier) { if (ELEM(input->value, CREATEDRIVER_MAPPING_1_1, CREATEDRIVER_MAPPING_NONE) || (is_array)) { RNA_enum_item_add(&item, &totitem, input); } input++; } } else { /* We need at least this one! */ RNA_enum_items_add_value(&item, &totitem, input, CREATEDRIVER_MAPPING_NONE); } RNA_enum_item_end(&item, &totitem); *r_free = true; return item; }
static int depthdropper_poll(bContext *C) { PointerRNA ptr; PropertyRNA *prop; int index_dummy; uiBut *but; /* check if there's an active button taking depth value */ if ((CTX_wm_window(C) != NULL) && (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) && (but->type == UI_BTYPE_NUM) && (prop != NULL)) { if ((RNA_property_type(prop) == PROP_FLOAT) && (RNA_property_subtype(prop) & PROP_UNIT_LENGTH) && (RNA_property_array_check(prop) == false)) { return 1; } } return 0; }
/* Write into "name" buffer, the name of the property (retrieved using RNA from the curve's settings), * and return the icon used for the struct that this property refers to * WARNING: name buffer we're writing to cannot exceed 256 chars (check anim_channels_defines.c for details) */ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu) { int icon = 0; /* sanity checks */ if (name == NULL) return icon; else if (ELEM(NULL, id, fcu, fcu->rna_path)) { if (fcu == NULL) strcpy(name, IFACE_("<invalid>")); else if (fcu->rna_path == NULL) strcpy(name, IFACE_("<no path>")); else /* id == NULL */ BLI_snprintf(name, 256, "%s[%d]", fcu->rna_path, fcu->array_index); } else { PointerRNA id_ptr, ptr; PropertyRNA *prop; /* get RNA pointer, and resolve the path */ RNA_id_pointer_create(id, &id_ptr); /* try to resolve the path */ if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) { const char *structname = NULL, *propname = NULL; char arrayindbuf[16]; const char *arrayname = NULL; short free_structname = 0; /* For now, name will consist of 3 parts: struct-name, property name, array index * There are several options possible: * 1) <struct-name>.<property-name>.<array-index> * i.e. Bone1.Location.X, or Object.Location.X * 2) <array-index> <property-name> (<struct name>) * i.e. X Location (Bone1), or X Location (Object) * * Currently, option 2 is in use, to try and make it easier to quickly identify F-Curves (it does have * problems with looking rather odd though). Option 1 is better in terms of revealing a consistent sense of * hierarchy though, which isn't so clear with option 2. */ /* for structname * - as base, we use a custom name from the structs if one is available * - however, if we're showing subdata of bones (probably there will be other exceptions later) * need to include that info too since it gets confusing otherwise * - if a pointer just refers to the ID-block, then don't repeat this info * since this just introduces clutter */ if (strstr(fcu->rna_path, "bones") && strstr(fcu->rna_path, "constraints")) { /* perform string 'chopping' to get "Bone Name : Constraint Name" */ char *pchanName = BLI_str_quoted_substrN(fcu->rna_path, "bones["); char *constName = BLI_str_quoted_substrN(fcu->rna_path, "constraints["); /* assemble the string to display in the UI... */ structname = BLI_sprintfN("%s : %s", pchanName, constName); free_structname = 1; /* free the temp names */ if (pchanName) MEM_freeN(pchanName); if (constName) MEM_freeN(constName); } else if (ptr.data != ptr.id.data) { PropertyRNA *nameprop = RNA_struct_name_property(ptr.type); if (nameprop) { /* this gets a string which will need to be freed */ structname = RNA_property_string_get_alloc(&ptr, nameprop, NULL, 0, NULL); free_structname = 1; } else structname = RNA_struct_ui_name(ptr.type); } /* Property Name is straightforward */ propname = RNA_property_ui_name(prop); /* Array Index - only if applicable */ if (RNA_property_array_check(prop)) { char c = RNA_property_array_item_char(prop, fcu->array_index); /* we need to write the index to a temp buffer (in py syntax) */ if (c) BLI_snprintf(arrayindbuf, sizeof(arrayindbuf), "%c ", c); else BLI_snprintf(arrayindbuf, sizeof(arrayindbuf), "[%d]", fcu->array_index); arrayname = &arrayindbuf[0]; } else { /* no array index */ arrayname = ""; } /* putting this all together into the buffer */ /* XXX we need to check for invalid names... * XXX the name length limit needs to be passed in or as some define */ if (structname) BLI_snprintf(name, 256, "%s%s (%s)", arrayname, propname, structname); else BLI_snprintf(name, 256, "%s%s", arrayname, propname); /* free temp name if nameprop is set */ if (free_structname) MEM_freeN((void *)structname); /* Icon for this property's owner: * use the struct's icon if it is set */ icon = RNA_struct_ui_icon(ptr.type); /* valid path - remove the invalid tag since we now know how to use it saving * users manual effort to reenable using "Revive Disabled FCurves" [#29629] */ fcu->flag &= ~FCURVE_DISABLED; } else { /* invalid path */ BLI_snprintf(name, 256, "\"%s[%d]\"", fcu->rna_path, fcu->array_index); /* icon for this should be the icon for the base ID */ /* TODO: or should we just use the error icon? */ icon = RNA_struct_ui_icon(id_ptr.type); /* tag F-Curve as disabled - as not usable path */ fcu->flag |= FCURVE_DISABLED; } } /* return the icon that the active data had */ return icon; }
/* Main Driver Management API calls: * Add a new driver for the specified property on the given ID block, * and make it be driven by the specified target. * * This is intended to be used in conjunction with a modal "eyedropper" * for picking the variable that is going to be used to drive this one. * * - flag: eCreateDriverFlags * - driver_type: eDriver_Types * - mapping_type: eCreateDriver_MappingTypes */ int ANIM_add_driver_with_target(ReportList *reports, ID *dst_id, const char dst_path[], int dst_index, ID *src_id, const char src_path[], int src_index, short flag, int driver_type, short mapping_type) { PointerRNA id_ptr, ptr; PropertyRNA *prop; PointerRNA id_ptr2, ptr2; PropertyRNA *prop2; int done_tot = 0; /* validate pointers first - exit if failure */ RNA_id_pointer_create(dst_id, &id_ptr); if (RNA_path_resolve_property(&id_ptr, dst_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)", dst_id->name, dst_path); return 0; } RNA_id_pointer_create(src_id, &id_ptr2); if ((RNA_path_resolve_property(&id_ptr2, src_path, &ptr2, &prop2) == false) || (mapping_type == CREATEDRIVER_MAPPING_NONE)) { /* No target - So, fall back to default method for adding a "simple" driver normally */ return ANIM_add_driver( reports, dst_id, dst_path, dst_index, flag | CREATEDRIVER_WITH_DEFAULT_DVAR, driver_type); } /* handle curve-property mappings based on mapping_type */ switch (mapping_type) { case CREATEDRIVER_MAPPING_N_N: /* N-N - Try to match as much as possible, * then use the first one */ { /* Use the shorter of the two (to avoid out of bounds access) */ int dst_len = (RNA_property_array_check(prop)) ? RNA_property_array_length(&ptr, prop) : 1; int src_len = (RNA_property_array_check(prop)) ? RNA_property_array_length(&ptr2, prop2) : 1; int len = MIN2(dst_len, src_len); int i; for (i = 0; i < len; i++) { done_tot += add_driver_with_target(reports, dst_id, dst_path, i, src_id, src_path, i, &ptr, prop, &ptr2, prop2, flag, driver_type); } break; } case CREATEDRIVER_MAPPING_1_N: /* 1-N - Specified target index for all */ default: { int len = (RNA_property_array_check(prop)) ? RNA_property_array_length(&ptr, prop) : 1; int i; for (i = 0; i < len; i++) { done_tot += add_driver_with_target(reports, dst_id, dst_path, i, src_id, src_path, src_index, &ptr, prop, &ptr2, prop2, flag, driver_type); } break; } case CREATEDRIVER_MAPPING_1_1: /* 1-1 - Use the specified index (unless -1) */ { done_tot = add_driver_with_target(reports, dst_id, dst_path, dst_index, src_id, src_path, src_index, &ptr, prop, &ptr2, prop2, flag, driver_type); break; } } /* 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); }
/* for keyframes and drivers */ static int pyrna_struct_anim_args_parse( PointerRNA *ptr, const char *error_prefix, const char *path, const char **path_full, int *index) { const bool is_idbase = RNA_struct_is_ID(ptr->type); PropertyRNA *prop; PointerRNA r_ptr; if (ptr->data == NULL) { PyErr_Format(PyExc_TypeError, "%.200s this struct has no data, can't be animated", error_prefix); return -1; } /* full paths can only be given from ID base */ if (is_idbase) { int r_index = -1; if (RNA_path_resolve_property_full(ptr, path, &r_ptr, &prop, &r_index) == false) { prop = NULL; } else if (r_index != -1) { PyErr_Format(PyExc_ValueError, "%.200s path includes index, must be a separate argument", error_prefix, path); return -1; } else if (ptr->id.data != r_ptr.id.data) { PyErr_Format(PyExc_ValueError, "%.200s path spans ID blocks", error_prefix, path); return -1; } } else { prop = RNA_struct_find_property(ptr, path); r_ptr = *ptr; } if (prop == NULL) { PyErr_Format(PyExc_TypeError, "%.200s property \"%s\" not found", error_prefix, path); return -1; } if (!RNA_property_animateable(&r_ptr, prop)) { PyErr_Format(PyExc_TypeError, "%.200s property \"%s\" not animatable", error_prefix, path); return -1; } if (RNA_property_array_check(prop) == 0) { if ((*index) == -1) { *index = 0; } else { PyErr_Format(PyExc_TypeError, "%.200s index %d was given while property \"%s\" is not an array", error_prefix, *index, path); return -1; } } else { int array_len = RNA_property_array_length(&r_ptr, prop); if ((*index) < -1 || (*index) >= array_len) { PyErr_Format(PyExc_TypeError, "%.200s index out of range \"%s\", given %d, array length is %d", error_prefix, path, *index, array_len); return -1; } } if (is_idbase) { *path_full = BLI_strdup(path); } else { *path_full = RNA_path_from_ID_to_property(&r_ptr, prop); if (*path_full == NULL) { PyErr_Format(PyExc_TypeError, "%.200s could not make path to \"%s\"", error_prefix, path); return -1; } } return 0; }